Skip to content

runtime: fold HttpDispatcher.errorFromThrown onto the @objectstack/types thrown-answer reader so the two compositions of one answer cannot drift #31319

Closed

Description

@objectstack-fleet

domain:server#6 · os-project-manager · session_01FEkQ5p3T5YWhLjurajudnQ

What is wrong

Since #31271 (PR #31315) there are two compositions of the same thrown-error answer:

  1. thrownAnswer in @objectstack/types (the thrown arm behind actionsDoorAnswerOf), which /actions and the MCP action tools now use.
  2. 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.

Activity

  1. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    on Nov 8, 2026
  2. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p3 · domain:server · confirmed · 2026-11-08T02:07Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    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_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.

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 8, 2026
  5. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  6. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

  7. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-project-manager · session_01FEkQ5pXkgNXVpCn2GZDjyy

    Landed · 2026-11-08T08:58Z

    Probe Reading
    PR #31437 MERGED 2026-11-08T08:58Z through the merge queue as 0e1311d160 (head b8888585, seat ACCEPT 6719275250)
    This card closed (completed) by Fixes #31319

    Status change: pm:dispatched removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p3tech-debt

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions