fix: reconcile uncommitted production hotfix to main#174
Conversation
…ror logging Reconciles a production hotfix that was deployed (worker version 3d46be98, 2026-05-01T10:45:21Z) without a corresponding commit on main. This change is what restored connect.chitty.cc after the post-merge 1101 outage. The OAuthProvider library was being invoked for every non-agent request, including plain Hono routes (/api/health, /integrations/github/webhook, /intelligence/*). On non-OAuth paths it was resolving to a non-Response value, causing the runtime to throw "Incorrect type for Promise: the Promise did not resolve to 'Response'" and Cloudflare to surface 1101 across the entire worker. Adds isOAuthProviderRoute(url) — a narrow allowlist of OAuth-specific paths (/authorize, /token, /register, /.well-known/oauth-*). Only those routes go through oauthProvider.fetch; everything else is dispatched straight to app.fetch (the Hono router). Also adds formatCaughtError() to consistently extract message+stack from Error vs. non-Error throws across all top-level catch blocks, replacing the ad-hoc \`err.message\` / \`err.stack\` pattern (which throws on non-Error rejects). Also wraps the host==="mcp.chitty.cc" /mcp branch in try/catch with the same error-normalization pattern, matching the existing /chatgpt/mcp handler. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds POST /api/v1/intelligence/context/experience/migrate as the records-only half of the identity-supersession saga. ChittyConnect moves D1 records between two existing contexts when given a ChittyID- issued supersession manifest; it does not mint identities, recalculate trust, or write to event-storage. Those steps are owned upstream by ChittyID/ChittyChronicle/ChittyTrust and composed by Ch1tty's experience_reassign saga. - src/api/routes/context-intelligence.js: route handler, manifest validation (from_chitty_id, to_chitty_id, mint_audit_id, signature, issued_at, reason all required), ledger entries on both source and target, structured 200 response with nextSteps for the caller saga. - src/mcp/tool-dispatcher.js: dispatcher case forwarding to the new endpoint with Bearer auth header. - src/mcp/tool-registry.js: experience_migrate MCP tool definition with full input schema for the manifest. Aligns with the offline twin at ~/.claude/chittycontext/scripts/experience-reassign.py which is explicit about not pretending to be authoritative. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the inline validTypes array + ad-hoc toUpperCase() approach
with VALID_DISPUTE_TYPES + LEGACY_DISPUTE_TYPE_MAP module constants and
a normalizeDisputeType() helper. The helper handles trim, case-fold,
and legacy lowercase aliases ("billing" -> "FINANCIAL", "service" ->
"VENDOR", etc) in one place instead of scattering the rules across
each route.
Also fixes a latent ReferenceError in /create: a prior conflict-resolved
merge dropped the rawType binding while leaving \`if (!rawType || !title)\`
in place. Reintroduces \`const rawType = dispute_type || type;\` ahead of
the existence check; the canonical-type check still runs against the
normalized value.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PATH_TO_ENV is consulted in declaration order. With "services/chittydispute/token" declared before "services/chittydispute/service_token", a token resolved by the canonical service_token path was getting eclipsed by a stale plain-token entry. Swapping the order so service_token resolves first matches how the 1Password/secrets store is actually populated for ChittyDispute. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ture Removes the hardcoded 2048-bit PKCS#8 PEM (~30 lines of base64) in favor of node:crypto generateKeyPairSync at module load. Keeps tests deterministic per-run (the keypair is generated once and reused across the describe block) while keeping no static private keys in version control — even ones explicitly labeled "test fixture only" tend to trigger secret scanners and create false positives in audits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ChittyConnect owns the dumb-aggregate MCP semantics for mcp.chitty.cc plus the service catalog feeding the public Chitty discovery document, but the canonical external host matrix, alias rules, and Cloudflare- managed OAuth pattern live in the ch1tty repo. Adds a pointer so consumers know where the source-of-truth document is. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (8)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 33 minutes and 59 seconds.Comment |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
chittyconnect | 617a7ee | May 01 2026, 12:50 PM |
There was a problem hiding this comment.
Pull request overview
Reconciles an uncommitted production hotfix back into main for the Cloudflare Worker (ChittyConnect), restoring traceability for routing/OAuth behavior and bundling a few additional feature/fix changes that were present in the deployed worker.
Changes:
- Adjust worker request routing to bypass
oauthProvider.fetch()for non-OAuth endpoints and add safer top-level error formatting for non-Errorthrows. - Add “experience migration” API endpoint + MCP tool wiring, and enhance disputes type normalization/validation.
- Clean up secrets path mapping priority and replace a committed RSA PEM test fixture with runtime key generation; update README MCP host standard link.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/index.js |
Changes fetch routing between OAuth provider, MCP agent handler, and Hono app; improves catch/log formatting. |
src/api/routes/context-intelligence.js |
Adds POST /context/experience/migrate records-only migration endpoint and ledger logging. |
src/mcp/tool-registry.js |
Registers new experience_migrate MCP tool schema. |
src/mcp/tool-dispatcher.js |
Dispatches experience_migrate tool calls to the new API endpoint. |
src/api/routes/chittydisputes.js |
Normalizes dispute types via canonical allowlist + legacy mapping; fixes create/list behavior. |
src/services/cloudflare-secrets-client.js |
Updates secret path → env mapping for disputes service tokens. |
tests/services/jwt-helper.test.js |
Generates RSA keypair at runtime to avoid committed PEM fixture/secret scanning noise. |
README.md |
Adds MCP Host Standard section and link to canonical doc. |
Comments suppressed due to low confidence (1)
src/index.js:2076
- In this catch block you compute
errorInfobut the response body still useserr.message. This can returnundefinedfor non-Errorthrows and makes the newformatCaughtError()ineffective for the client response. UseerrorInfo.messagein the JSON response for consistency with the updated logging.
`[Agents] routeAgentRequest threw for ${request.method} ${url.pathname}: ${errorInfo.message}
${errorInfo.stack}`,
);
return new Response(
JSON.stringify({ error: "agent_routing_failed", error_description: err.message }),
{ status: 500, headers: { "Content-Type": "application/json" } },
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Order: target receives first (acquires the experience), then source closes. | ||
| const tgtEntry = await resolver.logToLedger( | ||
| tgt.id, | ||
| to_chitty_id, | ||
| "experience_migrate", | ||
| "experience_received", | ||
| { | ||
| type: "experience_received", | ||
| fromChittyId: from_chitty_id, | ||
| mintAuditId: mint_audit_id, | ||
| reason, | ||
| metricsTransferred: transferred, | ||
| receivedAt: timestamp, | ||
| }, | ||
| ); | ||
|
|
||
| const srcEntry = await resolver.logToLedger( | ||
| src.id, | ||
| from_chitty_id, | ||
| "experience_migrate", | ||
| "experience_migrated", | ||
| { | ||
| type: "experience_migrated", | ||
| toChittyId: to_chitty_id, | ||
| mintAuditId: mint_audit_id, | ||
| reason, | ||
| metricsTransferred: transferred, | ||
| linkedTargetEntryId: tgtEntry.entryId, | ||
| linkedTargetHash: tgtEntry.hash, | ||
| migratedAt: timestamp, | ||
| }, | ||
| ); |
|
|
||
| ChittyConnect currently owns the dumb aggregate MCP semantics for `mcp.chitty.cc` and the service catalog that feeds the public Chitty discovery document. | ||
|
|
||
| For the canonical external host matrix, alias rules, and Cloudflare-managed OAuth pattern, see [../ch1tty/docs/MCP_HOST_STANDARD.md](../ch1tty/docs/MCP_HOST_STANDARD.md). |
| try { | ||
| response = await oauthProvider.fetch(request, env, ctx); | ||
| if (isOAuthProviderRoute(url)) { | ||
| response = await oauthProvider.fetch(request, env, ctx); | ||
| } else { | ||
| response = await app.fetch(request, env, ctx); | ||
| } |
| "services/chittychronicle/token": "CHITTY_CHRONICLE_TOKEN", | ||
| "services/chittydispute/service_token": "DISPUTES_API_TOKEN", | ||
| "services/chittydispute/token": "DISPUTES_API_TOKEN", | ||
| "services/chittydispute/service_token": "DISPUTES_API_TOKEN", |
| if (!manifest || typeof manifest !== "object") { | ||
| return apiResponse( | ||
| c, | ||
| { | ||
| success: false, | ||
| error: { | ||
| code: "MISSING_MANIFEST", | ||
| message: | ||
| "ChittyID-issued supersession manifest is required. " + | ||
| "Records-only migration cannot run without identity authority.", | ||
| }, | ||
| }, | ||
| 400, | ||
| ); | ||
| } | ||
|
|
||
| const { | ||
| from_chitty_id, | ||
| to_chitty_id, | ||
| mint_audit_id, | ||
| signature, | ||
| issued_at, | ||
| reason, | ||
| } = manifest; | ||
|
|
||
| const missing = [ | ||
| ["from_chitty_id", from_chitty_id], | ||
| ["to_chitty_id", to_chitty_id], | ||
| ["mint_audit_id", mint_audit_id], | ||
| ["signature", signature], | ||
| ["issued_at", issued_at], | ||
| ["reason", reason], | ||
| ] | ||
| .filter(([, v]) => !v || (typeof v === "string" && !v.trim())) | ||
| .map(([k]) => k); | ||
|
|
||
| if (missing.length > 0) { | ||
| return apiResponse( | ||
| c, | ||
| { | ||
| success: false, | ||
| error: { | ||
| code: "MANIFEST_INCOMPLETE", | ||
| message: `manifest missing required fields: ${missing.join(", ")}`, | ||
| }, | ||
| }, | ||
| 400, | ||
| ); | ||
| } | ||
|
|
||
| if (from_chitty_id === to_chitty_id) { | ||
| return apiResponse( | ||
| c, | ||
| { | ||
| success: false, | ||
| error: { | ||
| code: "SAME_ENTITY", | ||
| message: "manifest from/to are identical", | ||
| }, | ||
| }, | ||
| 400, | ||
| ); | ||
| } | ||
|
|
||
| // TODO: verify Ed25519 signature against ChittyID public key + replay-check | ||
| // mint_audit_id against ChittyID audit endpoint. For now, this is a TRUST | ||
| // boundary that requires the manifest's existence; full crypto verification | ||
| // is the follow-up before production deploy. | ||
|
|
Summary
Reconciles the worker version
3d46be98(deployed 2026-05-01T10:45:21Z) withmain. That deploy carried 8 modified files that were never committed — discovered while investigating why connect.chitty.cc was 1101 across all paths immediately after PR #168 squash-merged.The OAuth-route bypass in
src/index.jsis the load-bearing change that brought the worker back. Other commits cluster around two independent features (experience migration, dispute type validation) plus one secrets-path priority fix and small test/docs hygiene.Commits (in dependency order, last-to-first)
fix(routing)—src/index.js: bypassoauthProvider.fetchfor non-OAuth paths viaisOAuthProviderRoute()allowlist; everything else goes straight toapp.fetch. AddsformatCaughtError()to handle non-Errorthrows across all top-level catch blocks. This is what restored prod after the post-fix: MCP portal zero-trust fixes #168 outage.feat(experience)—context-intelligence.js+tool-dispatcher.js+tool-registry.js: records-onlyPOST /api/v1/intelligence/context/experience/migrate+ matching MCP tool. Strict manifest validation (refuses without ChittyID-issued supersession), ledger entries on both source and target. Composing saga lives upstream at Ch1tty.feat(disputes)—chittydisputes.js: factor type validation intoVALID_DISPUTE_TYPES+normalizeDisputeType()helper with explicitLEGACY_DISPUTE_TYPE_MAP(billing→FINANCIAL, service→VENDOR, etc). Also fixes a latentReferenceErrorin/create(prior conflict resolution droppedconst rawType = ...while leaving the existence check that referenced it).fix(secrets)—cloudflare-secrets-client.js: reorderPATH_TO_ENVsoservices/chittydispute/service_tokenresolves before the legacyservices/chittydispute/tokenentry.test(jwt)—jwt-helper.test.js: generate test RSA keypair at module load vianode:cryptoinstead of committing a static PKCS#8 PEM. Removes a recurring secret-scanner false-positive.docs(readme)—README.md: link to the canonical MCP Host Standard in the ch1tty repo.Why this PR exists
fix/mcp-portal-zerotrustwas the branch behind PR #168, which squash-merged into main on 2026-04-30T22:16:36Z. Work continued on the same local branch after merge, was deployed directly viawrangler deploy --env productionto fix the post-merge 1101 outage, but never made it back to git. This PR restores commit-traceability per ChittyOS canon.Test plan
node --checkpasses on every modified .js filegit logreviewed — every change is purposeful, no whitespace churn/createReferenceErrorreproduced by reading and confirmed fixed by the patchnextStepsstrings as guidance — verify the caller (Ch1ttyexperience_reassign) actually reads them.Production state at time of opening
3d46be98-344c-4711-9e44-f5418ca84881(the hotfix, healthy, drained queue)wrangler deploy, modulo therawTypefix in commit 3.🤖 Generated with Claude Code