Skip to content

runtime: /actions answers a driver string code (SQLITE_BUSY) as 400 VALIDATION_ERROR with declaredCode, where /data answers the same throw 500 DATABASE_ERROR #31317

Closed

Description

@objectstack-fleet

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

What is wrong

The same thrown driver error gets two different answers depending on the door.

  • /data answers a driver error carrying a string code such as SQLITE_BUSY as 500 DATABASE_ERROR: a server fault, message withheld.
  • /actions (packages/runtime/src/domains/actions.ts) answers the same throw from an action body as 400 VALIDATION_ERROR, with the driver spelling carried in declaredCode. That is a client refusal, and it puts the driver's own code on the wire.

Since #31271 (PR #31315), the MCP run_action and resume_run tools mirror /actions, so the divergence now governs two MCP tools as well as the REST door.

Expected

A driver/database fault thrown inside an action body reads the same at /actions as at /data: a 5xx fault answer with no driver code on the wire. Unless the lane decides the 400 reading is right for action bodies, in which case /data and the tests should say why the two differ.

Where to start

Provenance

Out-of-scope finding 1 in the contract review on PR #31315 (#31271), which escalated it to the seat for a carrier.

Activity

  1. added
    bugSomething isn't working
    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 · p2 · domain:server · confirmed · 2026-11-08T02:02Z

    ⛔ 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 · #31317 · 2026-11-08T04:58Z · build (chain head; #31319 follows on this branch)
    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-31317
    Domain: domain:server
    Seat: domain:server#3
    File surface: packages/runtime/src/domains/actions.ts and/or the @objectstack/types thrown-answer reader, the pin file(s), a changeset (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: default (coordinator default)
    Clause-②: no (a driver fault inside an action body moves from 400 VALIDATION_ERROR to the 5xx DATABASE_ERROR /data already answers: a status/code correction on an error answer, not a widening; the dev confirms from the real diff)
    Metadata-protocol: none
    Responsibility: the runtime /actions door (objectstack, packages/runtime) reads a driver string code as a declared refusal | /data already answers the same fault 5xx DATABASE_ERROR | any action body that touches a busy database; reached today through /actions and the MCP action tools
    Thread-read: 6715943805
    Premise: holds
    Serial constraints cleared: precondition PR #31315 merged (f4b4c8ae); seat 6 #31330 in flight (thread oiz4q5) on the same run_action thrown answer in action-execution.ts — whoever lands second merges main and re-reads the other's pins. #31319 follows on this branch. Order: pm-orders/server/order-31317-31319-s3.md (project files); handed over by seat 5.

  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": 31317,
    "status": "done",
    "branch": "claude/project-thread-2n2fmy",
    "pr": "#31397",
    "session": "session_01FEkQ5nBSzc6ktJyjdFcDb3",
    "premise_still_valid": true,
    "summary": "actionsDoorAnswerOf (@objectstack/types) gains an arm before the 400 rejection: a status-less throw whose .code is not a closed ADR-0112 code (demotedDeclaredCode(resolveThrownHttpError(err, 400)) !== undefined, the read that put the driver spelling into /actions' declaredCode) asks the /data door's own classifier mapDataError(err, objectName), and when isServerFault(door.status) /actions answers that door's status, code and sentence with nothing the throw carried, logging a 'driver fault (CODE): ...' operator line. SQLITE_BUSY now answers 500 DATABASE_ERROR 'Internal data error' at /actions and on MCP run_action / resume_run, exactly as /data; ECONNREFUSED answers /data's 500 INTERNAL_ERROR. Closed codes with no status, bare Errors, declared statuses and sandboxed coded refusals (which /data answers 400) keep the 400 rejection, pinned as controls. No caller found for whom the 400 reading is load-bearing (runtime/mcp/types suites green, hotcrm coded refusals all declare a status), so no open question.",
    "tests": "Head 0b36be0. Red first on base f4b4c8a: runtime actions-driver-code-fault-parity.test.ts Tests 3 failed | 4 passed (7), 'AssertionError: expected 400 to be 500'; with the fix 7 passed. @objectstack/types vitest: Test Files 38 passed (38), Tests 881 passed. @objectstack/runtime vitest (types from rebuilt dist): Test Files 559 passed (559), Tests 10316 passed | 19 skipped. @objectstack/mcp vitest: Test Files 60 passed (60), Tests 872 passed. Ablation: ablation-replace on actions-door-answer.ts made the new arm's isServerFault guard never-true (String('ABL31317').length < 0); anchor 1 -> 0, blob e90d4eecf361 -> 79160314fb55; types rebuilt; ablation-dist-preflight: marker in dist/index.js and dist/index.mjs; the pin: Tests 3 failed | 4 passed (7), 'expected 400 to be 500'. Restore: blob == HEAD e90d4eecf361, git diff HEAD empty, git status --porcelain empty; types rebuilt; preflight --absent: marker absent from all 12 built files; pin 7 passed.",
    "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at f8f5786 (after merging origin/main eff90af): 66 commands, timeout 900 each, exit codes captured before any pipe. First pass 64 exit 0, check:dual-build-cjs-loads exit 3 (prerequisite, dists not all built), check:test-source-alias exit 1 (new pin reached @objectstack/metadata's lazy import; fixed in 0b36be0 with a module-top import). Re-run on 0b36be0: check:test-source-alias exit 0, check:dual-build-cjs-loads exit 0, runtime typecheck exit 0, eslint on the edited test exit 0, the pin exit 0. dispatch-gates --ran: 66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN. Outside the derivation: typecheck for types/runtime/mcp exit 0; scoped lint 'eslint packages/types packages/runtime packages/mcp --no-inline-config' exit 0. Record: pm-orders/server/31317-gates-ran.txt. CI check-runs: in_progress at hand-back (PR just opened); not waited on per os-dev.md. Not run locally (CI): test:repo, dogfood shards, type-check lanes.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "7 — issue_read x4 (#31317 get, #31319 get, #31317 get_comments x2), pull_request_read x2 (#31315 get, #31397 get read-back), get_session x1 (not GitHub); no write tool",
    "api_writes": "2 — relay pr_create (fleet-write run 42452869690, read-back identical, 10373 bytes, assignees none, labels none: the order names none and the diff carries a changeset); this os-dev-report comment on #31317 via post-stamped",
    "files_changed": [
    ".changeset/31317-actions-driver-code-fault.md (+23)",
    "packages/types/src/actions-door-answer.ts (+53/-10)",
    "packages/types/src/actions-door-answer.test.ts (+3/-3)",
    "packages/runtime/src/domains/actions-driver-code-fault-parity.test.ts (+155, new)",
    "packages/mcp/src/mcp-coded-refusal.test.ts (+5/-6)",
    "packages/mcp/src/mcp-http-tools.ts (+2/-3, comment only)"
    ],
    "line_budget": "6 files, +241/-22 vs merge base eff90af (dispatch-gates: under the 3000-line human-merge threshold)",
    "deviations": [
    "packages/mcp/src/mcp-coded-refusal.test.ts and packages/mcp/src/mcp-http-tools.ts are outside the claim surface by the letter: the #31271 SQLITE_BUSY row on run_action/resume_run pinned the old 400 answer (it moves with the fix, still asserted equal to actionsDoorAnswerOf), and the errorResultFromThrown docblock stated the difference this PR removes (comment only, not shipped; no mcp changeset).",
    "Changeset lists @objectstack/types only: runtime and mcp change tests/comments only; their wire answers move through types.",
    "No labels on pr_create: the order names none and skip-changeset does not apply. No assignees, per the order (os-dev.md rule 2 reads the other way).",
    "The full derived loop ran at f8f5786; the final commit 0b36be0 only added one import line to the new test file, and the gates that read it (test-source-alias, eslint, runtime typecheck) plus dual-build-cjs-loads were re-run on 0b36be0."
    ],
    "open_questions": [],
    "security_findings": "none",
    "out_of_scope_findings": [
    "carrier: none · noted, not filed — closed ADR-0112 codes thrown with no status (VALIDATION_ERROR, RECORD_LOCKED, CONFLICT) still answer /actions 400 with the code while /data answers them 500 INTERNAL_ERROR; kept by the order (only the driver-fault class moves). PR Acceptance notes.",
    "carrier: #31330 (seat 6) · sibling: the uncoded No handler registered throw in action-execution.ts carries no code, so the new arm never reads it; whoever lands second re-reads the other pins."
    ]
    }


    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 #31397 comment 6718219048 · 2026-11-08T06:27Z · head 0b36be09 · ready + auto-merge armed by the seat; #31319 dispatches on this branch after the merge.

  7. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

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

    Landed · 2026-11-08T06:50Z

    Probe Reading
    PR #31397 MERGED 2026-11-08T06:50Z through the merge queue as db50231413 (head 0b36be09, ACCEPT 6718219048)
    This card closed by Fixes #31317

    Status change: pm:dispatched removed. #31319 (claim 6717496306) now starts on the same thread and branch, rebuilt from fresh origin/main.

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, jobsbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions