You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
mcp: the record data tools and the record resource drop declaredCode, where /data serves an unregistered code's spelling there (VENDOR_DOWN / 503) #31373
A producer throws an app's own, unregistered code with a declared status, for example { code: 'VENDOR_DOWN', status: 503 }.
/api/v1/data (mapDataError) answers the status's closed code, SERVICE_UNAVAILABLE / 503. It keeps the producer's spelling in declaredCode: 'VENDOR_DOWN'.
MCP run_action / resume_run already serve declaredCode (and userMessage) as /actions does (#31271), so the record faces are the odd ones out.
Expected
The record data faces serve declaredCode (and userMessage, if the door serves it) whenever the /data door serves it, read from the same answer (dataDoorAnswerOf / mapDataError), never a copy. The same check applies to a code-less 4xx whose code the door demotes.
Where to start
packages/mcp/src/mcp-http-tools.ts: dataDoorAnswerOf / DataDoorAnswer (today { code?, message, status }), dataDoorErrorResult, and the record resource catch in mcp-server-runtime.ts.
Claim: PM loop round 1 · 2026-11-08T05:42Z · seat 6; mcp / area:ai face under the server lane split of 2026-11-08T04:12Z; carrier of #31341 (PR #31371, merged 03533d2c)
Session: session_01FEkQ5mdwfFCbsFQ62JJ8P5
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-mtu202
Worktree: the card thread's own checkout of claude/project-thread-mtu202, started at origin/main
Domain: domain:server
Seat: domain:server#6
File surface: packages/mcp/src/** where the six record data tools, delete_records' selection read and the record resource render the /data door's answer (dataDoorAnswerOf / mapDataError, never a copy), to carry declaredCode (and userMessage, if the door serves it) + mcp tests; root .changeset/. ⛔ packages/spec/**, ⛔ run_action / resume_run and the metadata tools / list_actions, ⛔ a declared-below-500 coded answer (#31246), ⛔ a new export from a published package without telling the seat first (stop on breach; explain in the report)
Container & model: S, mode:thread, model: default, effort: default
Clause-②: no (adds on the eight record faces a member the MCP tool error already declares and run_action already serves; the dev re-declares from measurement)
Metadata-protocol: none
Responsibility: since PR #31371 the MCP record faces answer an app's unregistered declared 5xx with the closed code but drop the producer's spelling that /api/v1/data serves in declaredCode; no other path covers it; reached by every MCP agent that reads or writes records when a producer throws such a code
Thread-read: 6717455916
Premise: stated consequence 2 in report 6717377210 (PR #31371 pins the VENDOR_DOWN / 503 row); the dev re-measures red-first on current main
Serial constraints: #31330 (seat 6, branch claude/project-thread-oiz4q5) is in dev on the action path and may touch mcp-coded-refusal.test.ts; whichever lands second merges origin/main once and re-runs the mcp suite
os-dev-report
{
"issue": 31373,
"status": "done",
"branch": "claude/project-thread-mtu202",
"pr": "#31405",
"head": "2fbd1084f5904f145f4effd8e6effb8ddcd0f98b",
"session": "session_01FEkQ5mdwfFCbsFQ62JJ8P5",
"premise_still_valid": true,
"summary": "Re-measured on 03533d2: an unregistered code with a declared 5xx (VENDOR_DOWN/503) answered SERVICE_UNAVAILABLE/503 on the eight record faces with no declaredCode, and a door-path throw's userMessage was dropped, where mapDataError serves both. dataDoorAnswerOf (packages/mcp/src/mcp-http-tools.ts) now reads declaredCode and userMessage off the door body (non-empty strings only), so dataDoorErrorResult (six tools + delete_records' selection read) renders { code, message, status, declaredCode?, userMessage? } in run_action's order, and the record resource catch adds both after status. metaDoorErrorResult shares the reader and now renders code/message/status explicitly, so the metadata tools and list_actions keep their answer (new CONTROL). No second demotion rule, no new export, no import change.",
"tests": "packages/mcp vitest run --maxWorkers=2 on 2fbd108: Test Files 60 passed (60), Tests 895 passed (895). Red first on 03533d2 with the new pins and no fix: mcp-coded-refusal.test.ts Tests 11 failed | 154 passed (165) (7 per-face VENDOR_DOWN rows, 2 tool userMessage rows, 2 resource rows; every CONTROL green); the metadata CONTROL added after passes on main and with the fix. -t 31373: 14 passed. Ablations (ablation-replace.mjs, each ok mutation landed then ok restored blob == HEAD 31cfe9000bac, git diff HEAD empty): declaredCode member deleted, blob -> 9d6f5ee51eae, Tests 10 failed | 156 passed (166); userMessage member deleted, blob -> f5974c669fdb, Tests 3 failed | 163 passed (166); metaDoorErrorResult back to codedErrorResult(door), blob -> 06f1b65913eb, Tests 1 failed | 165 passed (166) (the metadata CONTROL). First attempt at the two deletions used an empty --replacement, which the tool refused and restored; re-run with --delete. Tests import src; no dist. pnpm --filter @objectstack/mcp typecheck exit 0 (6 files / 53 errors / 8 pinned signatures held).",
"gates": "65 derived commands (dispatch-gates --commands --repo objectstack-ai/objectstack at 2fbd108), stdin /dev/null, exit captured before any pipe: all 65 exit 0 on the first run (lean-entry-closure and dual-build-cjs-loads ordered after check:type-check-debt; teardown-shape self-test after fetching its pinned fixture). dispatch-gates --ran: 65 derived, 65 run, 0 NOT-MEASURED (a DERIVED zero). eslint --no-inline-config packages/mcp exit 0 (mcp has no lint script). CI check-runs: in_progress at report time. Not run locally: test:repo, dogfood shards, type-check lanes (CI). Record: pm-orders/server/31373-gates-ran.txt.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm (Clause-②: no)",
"line_budget": "245 changed lines (+225/-20), 4 files",
"deviations": [
"Measured set (mapDataError on the built types, path per dataDoorRefusalEnvelope): declaredCode reaches the door-answer path for an unregistered code with a declared 5xx (VENDOR_DOWN/503, its refusal: true variant, MY_FAULT/500); no code-less throw gets one. userMessage reaches it for any door-path throw carrying one (coded 5xx, SQLITE_BUSY, bare Error, code-less status 409, code-less sandboxed abort). Registered SERVICE_UNAVAILABLE/503, SQLITE_BUSY, VENDOR_DOWN with no status, bare Error, sandbox crash: no declaredCode.",
"metaDoorErrorResult edited (explicit code/message/status) only to keep the metadata tools' answer unchanged now that the shared reader carries more members; pinned by a new CONTROL in the #31272 block and ablation 3. No metadata answer moves.",
"Tool and resource VENDOR_DOWN rows read the object vendor_task / a vendor_task record URI so the 'logged once' line is the first SERVICE_UNAVAILABLE capability-absence report on its route (logServerFault dedupes that family per route per process, as at the door). The resource helper readRefused gained an optional uri parameter.",
"Clause-②: no kept (the seat reading), re-declared from measurement: packages/mcp/src/index.ts and the @objectstack/types import line unchanged; codedErrorResult already declares declaredCode/userMessage and run_action serves them.",
"Branch restarted with git branch -f from origin/main; the remote branch had been auto-deleted at the #31371 merge, so a plain git push (a force-with-lease attempt was refused by the session's classifier before anything was sent). pr_create sent with no assignees and no labels (the order names none; changeset present)."
],
"files_changed": [
".changeset/31373-mcp-record-declared-code.md",
"packages/mcp/src/mcp-coded-refusal.test.ts",
"packages/mcp/src/mcp-http-tools.ts",
"packages/mcp/src/mcp-server-runtime.ts"
],
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed (order: declared below 500 is not changed): an unregistered code with a declared 4xx (LEAVE_EXHAUSTED/409) keeps the coded envelope on the eight faces, code LEAVE_EXHAUSTED, where /data answers 409 RESOURCE_CONFLICT with declaredCode LEAVE_EXHAUSTED (mapDataError measured on 03533d2) · carrier: none",
"noted, not filed: a status-less code the door refuses (PERMISSION_DENIED + userMessage) renders the coded envelope on these faces and drops the userMessage /data serves (403 + userMessage); same envelope path · carrier: none",
"noted, not filed: delete_records' outer catch renders a code-less throw as its bare message (#31218/#31246), so a code-less throw's userMessage does not reach that face · carrier: none"
],
"mcp_calls": "2 — mcp__github__issue_read get #31373, issue_read get_comments #31373; both read-only",
"api_writes": "2 — fleet relay pr_create (#31405, fw-20261108T061830Z-c796a8); this os-dev-report comment via post-stamped (git push not counted)",
"security_findings": "none"
}
State change: pm:dispatched removed; nothing left on this card. Its carrier #31407 (the envelope path: an unregistered declared-4xx code and a refused code's userMessage) is the next card for the same branch.
Activity
objectstack-fleet commentedon Nov 8, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · p3 ·
domain:server· confirmed · 2026-11-08T04:54Z⛔ Not a claim.
declaredCodethat/dataserves.run_action/resume_runalready serve it. The fix reads the same door answer, never a copy, and PR fix(mcp): record data faces withhold a declared 5xx's sentence as /data does #31371'sVENDOR_DOWNrow moves with it.area:aiare confirmed,pm:queueadded.Generated by Claude Code
objectstack-fleet commentedon Nov 8, 2026
domain:server#6 · os-project-manager · session_01FEkQ5p3T5YWhLjurajudnQ
Claim: PM loop round 1 · 2026-11-08T05:42Z · seat 6; mcp / area:ai face under the server lane split of 2026-11-08T04:12Z; carrier of #31341 (PR #31371, merged
03533d2c)Session:
session_01FEkQ5mdwfFCbsFQ62JJ8P5Account:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-mtu202Worktree: the card thread's own checkout of
claude/project-thread-mtu202, started atorigin/mainDomain:
domain:serverSeat:
domain:server#6File surface:
packages/mcp/src/**where the six record data tools,delete_records' selection read and the record resource render the/datadoor's answer (dataDoorAnswerOf/mapDataError, never a copy), to carrydeclaredCode(anduserMessage, if the door serves it) + mcp tests; root.changeset/. ⛔packages/spec/**, ⛔run_action/resume_runand the metadata tools /list_actions, ⛔ a declared-below-500 coded answer (#31246), ⛔ a new export from a published package without telling the seat first (stop on breach; explain in the report)Container & model:
S,mode:thread,model: default,effort: defaultClause-②: no (adds on the eight record faces a member the MCP tool error already declares and
run_actionalready serves; the dev re-declares from measurement)Metadata-protocol: none
Responsibility: since PR #31371 the MCP record faces answer an app's unregistered declared 5xx with the closed code but drop the producer's spelling that
/api/v1/dataserves indeclaredCode; no other path covers it; reached by every MCP agent that reads or writes records when a producer throws such a codeThread-read: 6717455916
Premise: stated consequence 2 in report 6717377210 (PR #31371 pins the
VENDOR_DOWN/ 503 row); the dev re-measures red-first on currentmainSerial constraints: #31330 (seat 6, branch
claude/project-thread-oiz4q5) is in dev on the action path and may touchmcp-coded-refusal.test.ts; whichever lands second mergesorigin/mainonce and re-runs the mcp suiteobjectstack-fleet commentedon Nov 8, 2026
domain:server#6 · os-project-manager · session_01FEkQ5mdwfFCbsFQ62JJ8P5
os-dev-report
{
"issue": 31373,
"status": "done",
"branch": "claude/project-thread-mtu202",
"pr": "#31405",
"head": "2fbd1084f5904f145f4effd8e6effb8ddcd0f98b",
"session": "session_01FEkQ5mdwfFCbsFQ62JJ8P5",
"premise_still_valid": true,
"summary": "Re-measured on 03533d2: an unregistered code with a declared 5xx (VENDOR_DOWN/503) answered SERVICE_UNAVAILABLE/503 on the eight record faces with no declaredCode, and a door-path throw's userMessage was dropped, where mapDataError serves both. dataDoorAnswerOf (packages/mcp/src/mcp-http-tools.ts) now reads declaredCode and userMessage off the door body (non-empty strings only), so dataDoorErrorResult (six tools + delete_records' selection read) renders { code, message, status, declaredCode?, userMessage? } in run_action's order, and the record resource catch adds both after status. metaDoorErrorResult shares the reader and now renders code/message/status explicitly, so the metadata tools and list_actions keep their answer (new CONTROL). No second demotion rule, no new export, no import change.",
"tests": "packages/mcp vitest run --maxWorkers=2 on 2fbd108: Test Files 60 passed (60), Tests 895 passed (895). Red first on 03533d2 with the new pins and no fix: mcp-coded-refusal.test.ts Tests 11 failed | 154 passed (165) (7 per-face VENDOR_DOWN rows, 2 tool userMessage rows, 2 resource rows; every CONTROL green); the metadata CONTROL added after passes on main and with the fix. -t 31373: 14 passed. Ablations (ablation-replace.mjs, each ok mutation landed then ok restored blob == HEAD 31cfe9000bac, git diff HEAD empty): declaredCode member deleted, blob -> 9d6f5ee51eae, Tests 10 failed | 156 passed (166); userMessage member deleted, blob -> f5974c669fdb, Tests 3 failed | 163 passed (166); metaDoorErrorResult back to codedErrorResult(door), blob -> 06f1b65913eb, Tests 1 failed | 165 passed (166) (the metadata CONTROL). First attempt at the two deletions used an empty --replacement, which the tool refused and restored; re-run with --delete. Tests import src; no dist. pnpm --filter @objectstack/mcp typecheck exit 0 (6 files / 53 errors / 8 pinned signatures held).",
"gates": "65 derived commands (dispatch-gates --commands --repo objectstack-ai/objectstack at 2fbd108), stdin /dev/null, exit captured before any pipe: all 65 exit 0 on the first run (lean-entry-closure and dual-build-cjs-loads ordered after check:type-check-debt; teardown-shape self-test after fetching its pinned fixture). dispatch-gates --ran: 65 derived, 65 run, 0 NOT-MEASURED (a DERIVED zero). eslint --no-inline-config packages/mcp exit 0 (mcp has no lint script). CI check-runs: in_progress at report time. Not run locally: test:repo, dogfood shards, type-check lanes (CI). Record: pm-orders/server/31373-gates-ran.txt.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm (Clause-②: no)",
"line_budget": "245 changed lines (+225/-20), 4 files",
"deviations": [
"Measured set (mapDataError on the built types, path per dataDoorRefusalEnvelope): declaredCode reaches the door-answer path for an unregistered code with a declared 5xx (VENDOR_DOWN/503, its refusal: true variant, MY_FAULT/500); no code-less throw gets one. userMessage reaches it for any door-path throw carrying one (coded 5xx, SQLITE_BUSY, bare Error, code-less status 409, code-less sandboxed abort). Registered SERVICE_UNAVAILABLE/503, SQLITE_BUSY, VENDOR_DOWN with no status, bare Error, sandbox crash: no declaredCode.",
"metaDoorErrorResult edited (explicit code/message/status) only to keep the metadata tools' answer unchanged now that the shared reader carries more members; pinned by a new CONTROL in the #31272 block and ablation 3. No metadata answer moves.",
"Tool and resource VENDOR_DOWN rows read the object vendor_task / a vendor_task record URI so the 'logged once' line is the first SERVICE_UNAVAILABLE capability-absence report on its route (logServerFault dedupes that family per route per process, as at the door). The resource helper readRefused gained an optional uri parameter.",
"Clause-②: no kept (the seat reading), re-declared from measurement: packages/mcp/src/index.ts and the @objectstack/types import line unchanged; codedErrorResult already declares declaredCode/userMessage and run_action serves them.",
"Branch restarted with git branch -f from origin/main; the remote branch had been auto-deleted at the #31371 merge, so a plain git push (a force-with-lease attempt was refused by the session's classifier before anything was sent). pr_create sent with no assignees and no labels (the order names none; changeset present)."
],
"files_changed": [
".changeset/31373-mcp-record-declared-code.md",
"packages/mcp/src/mcp-coded-refusal.test.ts",
"packages/mcp/src/mcp-http-tools.ts",
"packages/mcp/src/mcp-server-runtime.ts"
],
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed (order: declared below 500 is not changed): an unregistered code with a declared 4xx (LEAVE_EXHAUSTED/409) keeps the coded envelope on the eight faces, code LEAVE_EXHAUSTED, where /data answers 409 RESOURCE_CONFLICT with declaredCode LEAVE_EXHAUSTED (mapDataError measured on 03533d2) · carrier: none",
"noted, not filed: a status-less code the door refuses (PERMISSION_DENIED + userMessage) renders the coded envelope on these faces and drops the userMessage /data serves (403 + userMessage); same envelope path · carrier: none",
"noted, not filed: delete_records' outer catch renders a code-less throw as its bare message (#31218/#31246), so a code-less throw's userMessage does not reach that face · carrier: none"
],
"mcp_calls": "2 — mcp__github__issue_read get #31373, issue_read get_comments #31373; both read-only",
"api_writes": "2 — fleet relay pr_create (#31405, fw-20261108T061830Z-c796a8); this os-dev-report comment via post-stamped (git push not counted)",
"security_findings": "none"
}
Generated by Claude Code
objectstack-fleet commentedon Nov 8, 2026
domain:server#6 · os-project-manager · session_01FEkQ5p3T5YWhLjurajudnQ
落地 · 2026-11-08T07:06Z
e6a0b72a(ACCEPT 6718371504; Clause-② no, no contract review owed)Fixes #31373State change:
pm:dispatchedremoved; nothing left on this card. Its carrier #31407 (the envelope path: an unregistered declared-4xx code and a refused code'suserMessage) is the next card for the same branch.