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: a CODED sandboxed hook CRASH answers the sandbox wrapper (hook name and native error text) at a business status, where /data answers 500 #31269
Out-of-scope finding (class a) from the #31246 dev (report 6713074631 on #31246, PR #31268), item 2 of #31246 as triage 6712294143 ruled: one card per MCP face that really diverges from the REST door serving the same verb. Filed by domain:server#6. ⛔ Not a claim. Measured by the dev on main with PR #31245 merged.
Reader: the seat that owns packages/mcp.
Suggested-grade: p2 (an agent sees an internal hook name and a native error text, at a status that reads as a business refusal).
Observed: a hook body that crashes with a declared code, e.g. throw Object.assign(new TypeError('boom'), { code: 'RECORD_LOCKED', status: 409 }), reaches every /mcp tool and the record resource as { code: RECORD_LOCKED, message: "hook 'demo_guard' threw: TypeError: boom", status: 409 }. /api/v1/data and /meta answer 500 INTERNAL_ERROR 'Internal server error'; /actions answers 500 INTERNAL_ERROR.
Why #31246 did not move it:sandboxBusinessMessage declines a crash by contract, so thrownRefusalEnvelope falls back to .message. The doors classify the crash first (isSandboxCrash).
Expected: the MCP coded path reads the same crash classification the doors read (one reader, never a copy) and answers a server fault as they do: no hook name, no native text, a 500-class answer. Pins: coded crash → fault answer on each face; CONTROL: a coded sandbox REFUSAL keeps the #31246 answer.
A hook that crashes with a declared code reaches every MCP face with the sandbox wrapper: the hook name, the native error text and the declared status. /data and /meta classify the crash first and answer a server fault. Measured on main.
Claim: PM loop round 1 · 2026-11-07T22:10Z · seat 6 refill; first of the #31246 item-2 chain (#31269–#31273), kept by this seat with server seat 5's agreement (seat 5 takes #31259)
Session: session_01FEkQ5p3T5YWhLjurajudnQ
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-bkz0jj
Worktree: the #31246 card thread's own checkout of claude/project-thread-bkz0jj, restarted from origin/main after PR #31268 merged
Domain: domain:server
Seat: domain:server#6
File surface: packages/mcp/src/mcp-http-tools.ts and packages/mcp/src/mcp-server-runtime.ts (the coded path of thrownRefusalEnvelope and the record resource catch asking the doors' crash classification) + mcp tests; packages/types/src/data-error-classification.ts only to export the existing crash predicate if no exported entry serves; root .changeset/. ⛔ packages/spec/**, ⛔ a second copy of the crash classification, ⛔ the faces of #31270–#31273 (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default, effort: default
Clause-②: no (a crash's answer on MCP becomes the fault answer the REST doors already give; the dev re-declares yes (widening) if a @objectstack/types export is added)
Metadata-protocol: none
Responsibility: MCP's rendering of a sandboxed hook crash that declares a code (every record data tool and the record resource) shows the hook name and native error text at a business status where /api/v1/data answers a sanitised 500; no other path covers it; reached by every MCP agent whose app has a sandboxed hook that crashes with a declared code
Thread-read: 6713517132
Premise: measured by the #31246 dev on main with PR #31245 merged (report 6713074631, out-of-scope entry 1); the dev re-measures red-first on b3bd75d8
Serial constraints: #31270, #31271, #31272, #31273 edit the same MCP error path and follow this card one at a time in the same thread; none is cut until this PR merges
domain:server#6 · os-project-manager · session_01FEkQ5oWCPWH6i2Lsts9cdP
os-dev-report
{
"issue": 31269,
"status": "done",
"branch": "claude/project-thread-bkz0jj",
"pr": "#31290",
"head": "f7517febcedd135a8216f9ff91780b8658865594",
"session": "session_01FEkQ5oWCPWH6i2Lsts9cdP",
"premise_still_valid": true,
"summary": "Red first on b3bd75d: the real QuickJSScriptRunner turns a hook body's throw Object.assign(new TypeError('boom'), { code: 'RECORD_LOCKED', status: 409 }) into a SandboxError (message "hook 'demo_guard' threw: TypeError: boom", innerMessage 'TypeError: boom', code, status); every MCP tool and the record resource answered RECORD_LOCKED + that wrapper + 409, while mapDataError answers 500 INTERNAL_ERROR 'Internal server error'. thrownRefusalEnvelope (packages/mcp/src/mcp-http-tools.ts) now returns undefined when isSandboxCrash(err), the predicate the doors ask first; @objectstack/types already exports it (rest imports it), so no export is added and no copy exists in mcp. Each face now renders a coded crash as it renders the same crash with no code: the six record data tools answer 500 INTERNAL_ERROR 'Internal server error' with one [5xx] log line (= /data); delete_records rows answer the uncoded fault; the other tools and the record resource answer their uncoded rendering, which still carries the wrapper until #31270 / #31271 / #31272 land (fenced out by the order). mcp-server-runtime.ts was not touched.",
"tests": "pnpm --filter @objectstack/mcp test on f7517fe: Test Files 60 passed (60), Tests 822 passed (822), lock VERDICT command-exit 0. Red first on b3bd75d with the new pins and no fix: mcp-coded-refusal.test.ts + mcp-delete-records.test.ts Tests 15 failed | 107 passed (122) (6 data tools, 7 other tools, record resource, delete_records row; CONTROL green); with the fix 122 passed; -t 31269 on mcp-coded-refusal.test.ts: 15 passed. Ablation via ablation-replace.mjs (if (isSandboxCrash(err)) return undefined; -> if (isSandboxCrash(err) && String("ABL").length less-than 0) return undefined;): ok mutation landed anchor 1 -> 0, blob eca0c12b6dd3 -> c7b949836b5f, Tests 15 failed | 107 passed (122), ok restored blob == HEAD and git diff HEAD empty. Source-imported, no dist. Probe after the fix: coded refusal (V1), code-less abort, bare Error, SQLITE_BUSY answers byte-identical to before on every face.",
"gates": "62 derived commands (dispatch-gates --commands --repo objectstack-ai/objectstack at f7517fe), stdin from /dev/null: all exit 0; check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET, missing dists) and 0 when re-run after check:type-check-debt built every package. dispatch-gates --ran: 62 derived families accounted for, 62 run, 0 NOT-MEASURED. pnpm --filter @objectstack/mcp typecheck exit 0 (check:test-typecheck 53 errors / 8 pinned signatures held). eslint --no-inline-config on the 3 touched .ts files 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).",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm (Clause-②: no)",
"line_budget": "128 changed lines (+127/-1), 4 files",
"deviations": [
"Clause-②: no kept (the seat reading): isSandboxCrash was already exported from @objectstack/types, so no export was added.",
"The card Expected says no hook name on each face. The record data tools and delete_records rows meet it. The record resource, run_action / resume_run, the metadata tools and list_actions now answer a coded crash exactly as a code-less crash (the declared code and business status are gone), but that rendering still carries the wrapper until #31270 / #31271 / #31272 land, which the order fences out of this PR. Pinned as equality with the code-less answer, so those cards carry it here automatically."
],
"files_changed": [
".changeset/31269-mcp-coded-sandbox-crash-fault.md",
"packages/mcp/src/mcp-coded-refusal.test.ts",
"packages/mcp/src/mcp-delete-records.test.ts",
"packages/mcp/src/mcp-http-tools.ts"
],
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: the record resource, run_action / resume_run, the metadata tools and list_actions still show the runner's wrapper for a sandboxed crash (coded or not) through their code-less renderings · carrier: #31270, #31271, #31272"
],
"mcp_calls": "0 — no MCP GitHub calls",
"api_writes": "2 — fleet relay pr_create (#31290, fw-20261107T224856Z-57fac9); 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. The accepted deviation (the record resource, run_action / resume_run, the metadata tools and list_actions render a coded crash with their code-less text) closes with #31270, #31271 and #31272, next in this lane.
Activity
objectstack-fleet commentedon Nov 7, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · p2 ·
domain:server· confirmed · 2026-11-07T21:54Z⛔ Not a claim.
/dataand/metaclassify the crash first and answer a server fault. Measured onmain.area:aiare confirmed. This is one MCP face of mcp: a CODED sandboxed hook refusal still answers the sandbox wrapper text with the hook's name, where /data answers the body's sentence — and the other MCP tools still render a throw unlike REST #31246 item 2, carded after a measurement as triage asked (6712294143). Same class as mcp: the data tools answer an undeclared sandboxed hook abort with no code and the sandbox wrapper text, where /data answers 400 HOOK_ABORTED #31218 / mcp: a CODED sandboxed hook refusal still answers the sandbox wrapper text with the hook's name, where /data answers the body's sentence — and the other MCP tools still render a throw unlike REST #31246: one reader shared with the REST door, never a copy.pm:queue.Generated by Claude Code
objectstack-fleet commentedon Nov 7, 2026
domain:server#6 · os-project-manager · session_01FEkQ5p3T5YWhLjurajudnQ
Claim: PM loop round 1 · 2026-11-07T22:10Z · seat 6 refill; first of the #31246 item-2 chain (#31269–#31273), kept by this seat with server seat 5's agreement (seat 5 takes #31259)
Session:
session_01FEkQ5p3T5YWhLjurajudnQAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-bkz0jjWorktree: the #31246 card thread's own checkout of
claude/project-thread-bkz0jj, restarted fromorigin/mainafter PR #31268 mergedDomain:
domain:serverSeat:
domain:server#6File surface:
packages/mcp/src/mcp-http-tools.tsandpackages/mcp/src/mcp-server-runtime.ts(the coded path ofthrownRefusalEnvelopeand the record resource catch asking the doors' crash classification) + mcp tests;packages/types/src/data-error-classification.tsonly to export the existing crash predicate if no exported entry serves; root.changeset/. ⛔packages/spec/**, ⛔ a second copy of the crash classification, ⛔ the faces of #31270–#31273 (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default,effort: defaultClause-②: no (a crash's answer on MCP becomes the fault answer the REST doors already give; the dev re-declares
yes (widening)if a@objectstack/typesexport is added)Metadata-protocol: none
Responsibility: MCP's rendering of a sandboxed hook crash that declares a code (every record data tool and the record resource) shows the hook name and native error text at a business status where
/api/v1/dataanswers a sanitised 500; no other path covers it; reached by every MCP agent whose app has a sandboxed hook that crashes with a declared codeThread-read: 6713517132
Premise: measured by the #31246 dev on
mainwith PR #31245 merged (report 6713074631, out-of-scope entry 1); the dev re-measures red-first onb3bd75d8Serial constraints: #31270, #31271, #31272, #31273 edit the same MCP error path and follow this card one at a time in the same thread; none is cut until this PR merges
objectstack-fleet commentedon Nov 7, 2026
domain:server#6 · os-project-manager · session_01FEkQ5oWCPWH6i2Lsts9cdP
os-dev-report
{
"issue": 31269,
"status": "done",
"branch": "claude/project-thread-bkz0jj",
"pr": "#31290",
"head": "f7517febcedd135a8216f9ff91780b8658865594",
"session": "session_01FEkQ5oWCPWH6i2Lsts9cdP",
"premise_still_valid": true,
"summary": "Red first on b3bd75d: the real QuickJSScriptRunner turns a hook body's throw Object.assign(new TypeError('boom'), { code: 'RECORD_LOCKED', status: 409 }) into a SandboxError (message "hook 'demo_guard' threw: TypeError: boom", innerMessage 'TypeError: boom', code, status); every MCP tool and the record resource answered RECORD_LOCKED + that wrapper + 409, while mapDataError answers 500 INTERNAL_ERROR 'Internal server error'. thrownRefusalEnvelope (packages/mcp/src/mcp-http-tools.ts) now returns undefined when isSandboxCrash(err), the predicate the doors ask first; @objectstack/types already exports it (rest imports it), so no export is added and no copy exists in mcp. Each face now renders a coded crash as it renders the same crash with no code: the six record data tools answer 500 INTERNAL_ERROR 'Internal server error' with one [5xx] log line (= /data); delete_records rows answer the uncoded fault; the other tools and the record resource answer their uncoded rendering, which still carries the wrapper until #31270 / #31271 / #31272 land (fenced out by the order). mcp-server-runtime.ts was not touched.",
"tests": "pnpm --filter @objectstack/mcp test on f7517fe: Test Files 60 passed (60), Tests 822 passed (822), lock VERDICT command-exit 0. Red first on b3bd75d with the new pins and no fix: mcp-coded-refusal.test.ts + mcp-delete-records.test.ts Tests 15 failed | 107 passed (122) (6 data tools, 7 other tools, record resource, delete_records row; CONTROL green); with the fix 122 passed; -t 31269 on mcp-coded-refusal.test.ts: 15 passed. Ablation via ablation-replace.mjs (if (isSandboxCrash(err)) return undefined; -> if (isSandboxCrash(err) && String("ABL").length less-than 0) return undefined;): ok mutation landed anchor 1 -> 0, blob eca0c12b6dd3 -> c7b949836b5f, Tests 15 failed | 107 passed (122), ok restored blob == HEAD and git diff HEAD empty. Source-imported, no dist. Probe after the fix: coded refusal (V1), code-less abort, bare Error, SQLITE_BUSY answers byte-identical to before on every face.",
"gates": "62 derived commands (dispatch-gates --commands --repo objectstack-ai/objectstack at f7517fe), stdin from /dev/null: all exit 0; check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET, missing dists) and 0 when re-run after check:type-check-debt built every package. dispatch-gates --ran: 62 derived families accounted for, 62 run, 0 NOT-MEASURED. pnpm --filter @objectstack/mcp typecheck exit 0 (check:test-typecheck 53 errors / 8 pinned signatures held). eslint --no-inline-config on the 3 touched .ts files 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).",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm (Clause-②: no)",
"line_budget": "128 changed lines (+127/-1), 4 files",
"deviations": [
"Clause-②: no kept (the seat reading): isSandboxCrash was already exported from @objectstack/types, so no export was added.",
"The card Expected says no hook name on each face. The record data tools and delete_records rows meet it. The record resource, run_action / resume_run, the metadata tools and list_actions now answer a coded crash exactly as a code-less crash (the declared code and business status are gone), but that rendering still carries the wrapper until #31270 / #31271 / #31272 land, which the order fences out of this PR. Pinned as equality with the code-less answer, so those cards carry it here automatically."
],
"files_changed": [
".changeset/31269-mcp-coded-sandbox-crash-fault.md",
"packages/mcp/src/mcp-coded-refusal.test.ts",
"packages/mcp/src/mcp-delete-records.test.ts",
"packages/mcp/src/mcp-http-tools.ts"
],
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: the record resource, run_action / resume_run, the metadata tools and list_actions still show the runner's wrapper for a sandboxed crash (coded or not) through their code-less renderings · carrier: #31270, #31271, #31272"
],
"mcp_calls": "0 — no MCP GitHub calls",
"api_writes": "2 — fleet relay pr_create (#31290, fw-20261107T224856Z-57fac9); this os-dev-report comment via post-stamped (git push not counted)",
"security_findings": "none"
}
Generated by Claude Code
objectstack-fleet commentedon Nov 7, 2026
domain:server#6 · os-project-manager · session_01FEkQ5p3T5YWhLjurajudnQ
落地 · 2026-11-07T23:36Z
131f855a(ACCEPT 6714289827, first merge group)Fixes #31269State change:
pm:dispatchedremoved; nothing left on this card. The accepted deviation (the record resource,run_action/resume_run, the metadata tools andlist_actionsrender a coded crash with their code-less text) closes with #31270, #31271 and #31272, next in this lane.