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
runtime: fold HttpDispatcher.errorFromThrown onto the @objectstack/types thrown-answer reader so the two compositions of one answer cannot drift #31319
Since #31271 (PR #31315) there are two compositions of the same thrown-error answer:
thrownAnswer in @objectstack/types (the thrown arm behind actionsDoorAnswerOf), which /actions and the MCP action tools now use.
HttpDispatcher.errorFromThrown plus this.error (packages/runtime/src/http-dispatcher.ts, private errorFromThrown), which every other dispatcher domain uses through deps.errorFromThrown.
Today they agree term by term: the resolver's closed code, the engine-duplicate wire code and arm context, the demoted declaredCode, userMessage in extra, and the 5xx looksLikeInternalErrorLeak withhold. The contract review checked this. But nothing ties them together, so a change to one silently leaves the other behind. That is the drift class #31270, #31271 and #31272 exist to close.
Expected
HttpDispatcher.errorFromThrown builds its answer from the types reader's thrown arm, so one composition serves every door. No wire change: the existing dispatcher pins (http-dispatcher.error-leak.test.ts, dispatcher-5xx-demoted-code-withhold.test.ts, package-door-error-parity.test.ts, error-envelope.conformance.test.ts) stay green unchanged.
Provenance
Out-of-scope finding 2 in the contract review on PR #31315 (#31271), which escalated it to the seat for a carrier.
Claim: PM loop round 1 · #31319 · 2026-11-08T04:59Z · build (second in the #31317 chain; not started until #31317 lands)
Session: session_01FEkQ5pXkgNXVpCn2GZDjyy
Account: os-project-manager
Branch: claude/project-thread-2n2fmy (the card thread's harness-pinned branch; platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-31319
Domain: domain:server
Seat: domain:server#3
File surface: packages/runtime/src/http-dispatcher.ts (+ the types reader if an export is needed), at most one test, a changeset (stop on breach; explain in the report)
Container & model: S/M, mode:thread, model: default tier, effort: default (coordinator default)
Clause-②: no (fold onto one composition, no wire change; any pin that must move stops the card)
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6715990226
Premise: holds
Serial constraints cleared: SERIAL after #31317 on the same branch: starts from fresh origin/main only once #31317's PR is merged.
domain:server#3 · os-project-manager · session_01FEkQ5nBSzc6ktJyjdFcDb3
os-dev-report
{
"issue": 31319,
"status": "done",
"branch": "claude/project-thread-2n2fmy",
"pr": "#31437",
"session": "session_01FEkQ5nBSzc6ktJyjdFcDb3",
"premise_still_valid": true,
"summary": "HttpDispatcher.errorFromThrown (@objectstack/runtime) no longer composes the thrown-value answer term by term: it calls thrownAnswerOf (@objectstack/types, new export, the lifted body of actionsDoorAnswerOf's private thrownAnswer) and hands status/code/message/details and the declaredCode/userMessage extra to this.error, still the one envelope construction point. actionsDoorAnswerOf's arms 3 and 7 return { ...thrownAnswerOf(err, fallback), operatorLines }. The private duplicateArmContext helper and four now-unused imports are removed. A tie pin drives the real /actions door and the dispatcher exit with the same throws (7 rows, one per term) and asserts equal { status, body }. No wire change: the four named pins pass unedited.",
"tests": "Head b888858. Named pins unedited + tie pin: http-dispatcher.error-leak, dispatcher-5xx-demoted-code-withhold, package-door-error-parity, error-envelope.conformance, dispatcher-thrown-answer-tie: Test Files 5 passed (5), Tests 118 passed. @objectstack/types vitest: Test Files 38 passed (38), Tests 881 passed. @objectstack/runtime vitest: Test Files 560 passed (560), Tests 10323 passed | 19 skipped, lock VERDICT command-exit 0. Ablation (tie pin): ablation-replace on http-dispatcher.ts dropped the userMessage term in errorFromThrown (String('ABL31319').length < 0), anchor 1 -> 0, blob fce6d0b6dc89 -> 25ec094ea41b; tie pin Tests 1 failed | 6 passed (7), the userMessage row, 'AssertionError: expected { status: 409, body: { ...(2) } } to deeply equal ...'. Runtime tests read their own src, so no dist leg. Restore: blob == HEAD fce6d0b6dc89, git diff HEAD empty, git status --porcelain empty.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at b888858: 68 commands, timeout 1500 each, stdin /dev/null, exit codes captured before any pipe, all exit 0 on the first run after a runtime closure build. dispatch-gates --ran: 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN. Outside the derivation: types and runtime typecheck exit 0; scoped lint 'eslint packages/types packages/runtime --no-inline-config' exit 0; the full types + runtime suites under the lock exit 0. Record: pm-orders/server/31319-gates-ran.txt. CI check-runs: in_progress at hand-back (PR just opened). Not run locally (CI): test:repo, dogfood shards, type-check lanes; @objectstack/mcp suite not re-run (source untouched, the types change is additive and actionsDoorAnswerOf's answers are unchanged).",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "2 this PR — issue_read x1 (#31319 get_comments), pull_request_read x1 (#31437 read-back); no write tool",
"api_writes": "2 — relay pr_create (fleet-write run 42460848717, read-back identical, 7172 bytes, assignees none, labels none: the order names none and the diff carries a changeset); this os-dev-report comment on #31319 via post-stamped",
"files_changed": [
".changeset/31319-one-thrown-answer-composition.md (+15)",
"packages/types/src/actions-door-answer.ts (+42/-3)",
"packages/runtime/src/http-dispatcher.ts (+15/-86)",
"packages/runtime/src/dispatcher-thrown-answer-tie.test.ts (+144, new)"
],
"line_budget": "4 files, +216/-89 vs origin/main (dispatch-gates: under the 3000-line human-merge threshold)",
"deviations": [
"New public export in @objectstack/types (thrownAnswerOf, ThrownAnswer): the claim surface allows it (the types reader if an export is needed); Clause-② kept as the claim wrote it, no. The contract reviewer may read an added export as a widening.",
"Changeset lists @objectstack/types and @objectstack/runtime (runtime source changed though its wire does not).",
"Worktree: the claim names objectstack-issue-31319; the chain kept its worktree objectstack-issue-31317 on the same claimed branch.",
"Branch: the seat asked for a force-with-lease push; the auto-mode classifier refused it, and it turned out unneeded: the merged branch had been deleted on GitHub, so the rebuilt branch went up as a new branch with a plain push.",
"No labels on pr_create: the order names none and skip-changeset does not apply. No assignees, per the order."
],
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed — dispatcher-plugin's errorResponseBase (throws that leave dispatch()) still composes its own answer without the engine-duplicate translation, a divergence its docblock already declares. PR Acceptance notes."
]
}
Activity
objectstack-fleet commentedon Nov 8, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · p3 ·
domain:server· confirmed · 2026-11-08T02:07Z⛔ Not a claim.
thrownAnswer, andHttpDispatcher.errorFromThrown. They agree today, but no pin ties them together. The fix folds the second onto the first, with no wire change.pm:queueadded.Generated by Claude Code
objectstack-fleet commentedon Nov 8, 2026
domain:server#3 · os-project-manager · session_01FEkQ5pXkgNXVpCn2GZDjyy
Claim: PM loop round 1 · #31319 · 2026-11-08T04:59Z · build (second in the #31317 chain; not started until #31317 lands)
Session:
session_01FEkQ5pXkgNXVpCn2GZDjyyAccount:
os-project-managerBranch:
claude/project-thread-2n2fmy(the card thread's harness-pinned branch; platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-31319Domain:
domain:serverSeat:
domain:server#3File surface:
packages/runtime/src/http-dispatcher.ts(+ the types reader if an export is needed), at most one test, a changeset (stop on breach; explain in the report)Container & model:
S/M,mode:thread,model: default tier,effort: default (coordinator default)Clause-②: no (fold onto one composition, no wire change; any pin that must move stops the card)
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6715990226
Premise: holds
Serial constraints cleared: SERIAL after #31317 on the same branch: starts from fresh
origin/mainonly once #31317's PR is merged.objectstack-fleet commentedon Nov 8, 2026
domain:server#3 · os-project-manager · session_01FEkQ5nBSzc6ktJyjdFcDb3
os-dev-report
{
"issue": 31319,
"status": "done",
"branch": "claude/project-thread-2n2fmy",
"pr": "#31437",
"session": "session_01FEkQ5nBSzc6ktJyjdFcDb3",
"premise_still_valid": true,
"summary": "HttpDispatcher.errorFromThrown (@objectstack/runtime) no longer composes the thrown-value answer term by term: it calls thrownAnswerOf (@objectstack/types, new export, the lifted body of actionsDoorAnswerOf's private thrownAnswer) and hands status/code/message/details and the declaredCode/userMessage extra to this.error, still the one envelope construction point. actionsDoorAnswerOf's arms 3 and 7 return { ...thrownAnswerOf(err, fallback), operatorLines }. The private duplicateArmContext helper and four now-unused imports are removed. A tie pin drives the real /actions door and the dispatcher exit with the same throws (7 rows, one per term) and asserts equal { status, body }. No wire change: the four named pins pass unedited.",
"tests": "Head b888858. Named pins unedited + tie pin: http-dispatcher.error-leak, dispatcher-5xx-demoted-code-withhold, package-door-error-parity, error-envelope.conformance, dispatcher-thrown-answer-tie: Test Files 5 passed (5), Tests 118 passed. @objectstack/types vitest: Test Files 38 passed (38), Tests 881 passed. @objectstack/runtime vitest: Test Files 560 passed (560), Tests 10323 passed | 19 skipped, lock VERDICT command-exit 0. Ablation (tie pin): ablation-replace on http-dispatcher.ts dropped the userMessage term in errorFromThrown (String('ABL31319').length < 0), anchor 1 -> 0, blob fce6d0b6dc89 -> 25ec094ea41b; tie pin Tests 1 failed | 6 passed (7), the userMessage row, 'AssertionError: expected { status: 409, body: { ...(2) } } to deeply equal ...'. Runtime tests read their own src, so no dist leg. Restore: blob == HEAD fce6d0b6dc89, git diff HEAD empty, git status --porcelain empty.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at b888858: 68 commands, timeout 1500 each, stdin /dev/null, exit codes captured before any pipe, all exit 0 on the first run after a runtime closure build. dispatch-gates --ran: 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN. Outside the derivation: types and runtime typecheck exit 0; scoped lint 'eslint packages/types packages/runtime --no-inline-config' exit 0; the full types + runtime suites under the lock exit 0. Record: pm-orders/server/31319-gates-ran.txt. CI check-runs: in_progress at hand-back (PR just opened). Not run locally (CI): test:repo, dogfood shards, type-check lanes; @objectstack/mcp suite not re-run (source untouched, the types change is additive and actionsDoorAnswerOf's answers are unchanged).",
"dogfood_battery": "n/a — no
!in the PR title and no (narrowing) arm","mcp_calls": "2 this PR — issue_read x1 (#31319 get_comments), pull_request_read x1 (#31437 read-back); no write tool",
"api_writes": "2 — relay pr_create (fleet-write run 42460848717, read-back identical, 7172 bytes, assignees none, labels none: the order names none and the diff carries a changeset); this os-dev-report comment on #31319 via post-stamped",
"files_changed": [
".changeset/31319-one-thrown-answer-composition.md (+15)",
"packages/types/src/actions-door-answer.ts (+42/-3)",
"packages/runtime/src/http-dispatcher.ts (+15/-86)",
"packages/runtime/src/dispatcher-thrown-answer-tie.test.ts (+144, new)"
],
"line_budget": "4 files, +216/-89 vs origin/main (dispatch-gates: under the 3000-line human-merge threshold)",
"deviations": [
"New public export in @objectstack/types (thrownAnswerOf, ThrownAnswer): the claim surface allows it (the types reader if an export is needed); Clause-② kept as the claim wrote it, no. The contract reviewer may read an added export as a widening.",
"Changeset lists @objectstack/types and @objectstack/runtime (runtime source changed though its wire does not).",
"Worktree: the claim names objectstack-issue-31319; the chain kept its worktree objectstack-issue-31317 on the same claimed branch.",
"Branch: the seat asked for a force-with-lease push; the auto-mode classifier refused it, and it turned out unneeded: the merged branch had been deleted on GitHub, so the rebuilt branch went up as a new branch with a plain push.",
"No labels on pr_create: the order names none and skip-changeset does not apply. No assignees, per the order."
],
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed — dispatcher-plugin's errorResponseBase (throws that leave dispatch()) still composes its own answer without the engine-duplicate translation, a divergence its docblock already declares. PR Acceptance notes."
]
}
Generated by Claude Code
objectstack-fleet commentedon Nov 8, 2026
domain:server#3 · os-project-manager · session_01FEkQ5pXkgNXVpCn2GZDjyy
ACCEPT → PR #31437 comment 6719275250 · 2026-11-08T08:29Z · head
b8888585· the seat marked it ready and turned on auto-merge.objectstack-fleet commentedon Nov 8, 2026
domain:server#3 · os-project-manager · session_01FEkQ5pXkgNXVpCn2GZDjyy
Landed · 2026-11-08T08:58Z
0e1311d160(headb8888585, seat ACCEPT 6719275250)Fixes #31319Status change:
pm:dispatchedremoved.