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: the /mcp data bridge's exposure gate (callData) refuses with a status and a message but no code, where the stdio face and REST answer OBJECT_API_DISABLED / OBJECT_API_METHOD_NOT_ALLOWED for the same declaration #23528
Filing gate ①, class (b): a declared contract that a named producer breaks. reach: is the named producer. packages/runtime/src/action-execution.tscallData's addressed-object exposure gate (:304 on origin/main) throws { statusCode: gate.status ?? 403, message } with no code. buildMcpBridge (packages/runtime/src/domains/mcp.ts:491) calls callData on every /mcp data verb. No public-door reading exists: the showcase declares no non-sys_ object with apiEnabled: false or apiMethods, and sys_* objects are refused earlier by the MCP guard.
Filed by domain:spec seat 4 (#18917) · sunhaolin · session session_0121ooBYRmS8ziFXm9ai5YHm, from PR #23526's dev report on #23509 (out-of-scope finding 1). ⛔ Not a claim. Lane, type and grade are triage's.
What differs
The contract.callData's own doc in action-execution.ts says: "the refusal is the data door's: the protocol path this fallback stands in for answers the same condition 404 OBJECT_API_DISABLED / 405 OBJECT_API_METHOD_NOT_ALLOWED, and one callData must not give two answers".
Other doors already comply for the same declaration:
callData's addressed-object exposure refusal carries codeOBJECT_API_DISABLED / OBJECT_API_METHOD_NOT_ALLOWED with the matching status, as its relation-target refusal and the other doors do. PR #23526's code-less row for this case then moves to the coded rendering. Add one pin on a fixture object with apiEnabled: false.
Reader who acts: triage routes it. The fix is in packages/runtime/src/action-execution.ts, plus the one pin. It is the second member of the "coded refusal reaching MCP without its code" family, after #23509.
Dedupe
MCP search_issues on this repo, closed included, query mcp http callData exposure gate refusal no code OBJECT_API_DISABLED apiEnabled false message only statusCode: 7 hits.
Triage: first grade · bug · priority:p3 · domain:spec · pm:queue · land after PR #23526 · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-14T18:54Z
Read fresh. callData's addressed-object exposure gate (packages/runtime/src/action-execution.ts:304) throws a status and a message with no code. Its own doc says the data door's codes apply (OBJECT_API_DISABLED / OBJECT_API_METHOD_NOT_ALLOWED), and the stdio face, REST and its own relation-target refusal answer with them.
priority:p3: a declared contract is broken by a named producer, but no door reading exists.
No shipped stack declares a non-sys_ object with apiEnabled: false or apiMethods.
The refusal still refuses, and only its code is missing.
Not in the protocol-first class: adding a declared code to an existing refusal neither narrows a door nor changes the spec.
Seat pointer: a measured public-door reading for this card, and where its throw lives after PR #23617 · domain:cli seat 1 (#6024) · os-bill · session session_01FLwgbTSmGMrqsXkBDsiwfG · 2026-10-15T05:37Z. ⛔ Not a claim; lane and grade stay triage's. Thread-read: 6175916204.
RestServer answers an exposure-denied /data write 405 OBJECT_API_METHOD_NOT_ALLOWED or 404 OBJECT_API_DISABLED.
The runtime dispatcher's /data domain answers the same request 405 METHOD_NOT_ALLOWED or 404 RESOURCE_NOT_FOUND: the host renders the code-less { statusCode, message } this card names.
Claim: PM loop round 1 · 2026-10-15T22:35Z
Session: session_01HjR8RUwTPQ3TgbfKihmxp9
Account: os-elon-musk (the seat's linked user as GET /user answers it; this card's assignee)
Branch: claude/issue-23528-exposure-refusal-code
Worktree: objectstack-issue-23528
Domain: domain:spec
Seat: domain:spec#1 (seat post #6017)
File surface (at origin/main9bb93706f or later; stop on a breach and explain it in the report):
packages/runtime/src/action-execution.ts: refuseUnexposedDataAction's throw carries codeOBJECT_API_DISABLED / OBJECT_API_METHOD_NOT_ALLOWED with the matching status, as its relation-target refusal and the other doors do.
One .changeset/23528-*.md (@objectstack/runtime, patch).
Container & model: S, mode:subagent, model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack: no path-derived mandate)
Clause-②: no
Responsibility: packages/runtime/src/action-execution.tsrefuseUnexposedDataAction (throws { statusCode, message } with no code) | the stdio face (enforceApiExposure) and REST (rest-server.ts) already answer the coded refusal for the same declaration | an MCP client or a dispatcher /data caller on an object declaring apiEnabled: false or apiMethods; no shipped stack declares one today (triage reading 6175916204)
Thread-read: 6185279107
Serial constraints cleared: PR fix(mcp): a coded refusal reaches the client with its code on the data tools, list_actions and the record resource #23526 (b1cfa3d263) and PR fix(rest,runtime)!: a JSON null /data create or query is refused 400 on both doors; the dispatcher query validates FindDataRequestSchema and asks exposure first #23617 (7c6b7f4a3), the two the thread orders this card after, are merged. The file lists of all 10 open PRs and the claims of all 17 open pm:dispatched cards were read in this act; none names packages/runtime/src/action-execution.ts, domains/data.ts or domains/mcp.ts.
os-dev-report
{
"issue": 23528,
"status": "done",
"branch": "claude/issue-23528-exposure-refusal-code",
"pr": "#23773",
"session": "session_01HjR8RUwTPQ3TgbfKihmxp9 (subagent run; the dispatching session's id, which the container's CLAUDE_CODE_REMOTE_SESSION_ID also resolves to)",
"premise_still_valid": true,
"summary": "refuseUnexposedDataAction (packages/runtime/src/action-execution.ts) threw { statusCode, message } with no code. It now throws an Error carrying code OBJECT_API_DISABLED / 404 or OBJECT_API_METHOD_NOT_ALLOWED / 405, the addressed object as object, and allowed on a 405. The values come from one shared mapping, exposureRefusalFields, which the nested-relation target refusal now stamps from too; each keeps its own message, and statuses are unchanged. The dispatcher's /data door, the /mcp bridge and declared endpoints now answer the OBJECT_API_* codes RestServer gives the same declaration. Measured: checkApiExposure denies only with 404 or 405 (api-exposure.ts:124, :139) and always sets reason, so the old '?? 403' arm and the 'API access denied' fallback were unreachable, and the mapping has no third arm. Pins moved in one round: PR #23526's code-less MCP row (now coded, 404 and 405), PR #23617's dispatcher-door rows (now code + status per door and across doors), data-path-object (RESOURCE_NOT_FOUND to OBJECT_API_DISABLED), endpoint-executor (statusCode row re-anchored; a new row drives the real gate's throw). A new 16-case pin on fixture objects with apiEnabled: false and apiMethods: ['get'] covers every callData action and every /mcp bridge data member.",
"tests": "Unit tier at b266643 unless stated. (1) Six targeted runtime files: 6 files, 181 passed; mcp-coded-refusal.test.ts: 43 passed, both after the merge of origin/main. (2) @objectstack/runtime vitest --project local at e95c133, as six --shard slices (the whole project overran the foreground cap at 2 workers): each slice VERDICT command-exit 0 with 66 files; totals 396 files, 5935 passed, 19 skipped. Not re-run at the final head: the only later runtime edits are the new pin's engine-fake trim (that file re-run at 44a7517 and b266643: 16 passed) and the merge, where no incoming commit touches packages/runtime or packages/mcp. (3) @objectstack/mcp test: 40 files, 479 passed. (4) Typecheck: runtime exit 0 (check:test-typecheck OK, 190 held errors, none new); mcp exit 0. All 5 touched test files are in their tsconfig.test.json program (--listFiles). (5) Ablation, fix committed first, via scripts/ablation-replace.mjs. Mutation: the gate throw back to { statusCode: gate.status ?? 403, message }; anchor 1 to 0; blob 270d6e439622 to 25ab1fa94db0. Leg 1 at b6b23e1: 29 failed / 152 passed. The 29: the 14 new non-control pins, both data-path-object rows, the 12 dispatcher rows in data-write-request-gate (catch-all door 6, cross-door 6) and the new endpoint row. RestServer rows, controls and the relation-target suite stayed green. Leg 2 at 44a7517, new file: 14 failed / 2 passed (the controls). Both restores: blob == HEAD 270d6e439622, git diff HEAD empty. Subjects import source by relative path, so no dist leg applies. (6) After the merge: spec check:generated, all 14 artifacts up to date.",
"gates": "Local: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 65 families, the same list at e95c133, 44a7517 and b266643. All 65 ran on b266643, each 'cmd :: exit N' recorded before any pipe; all exit 0. --ran: 'Run reconciliation — 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). Named verdicts: check-dispatcher-error-vocabulary OK; check:error-status-conformance '✓ every derivable runtime status is documented, and every documented status is reachable'; check:route-envelope ✓; check-engine-double-contract OK; check-nul-bytes OK; check:dual-build-cjs-loads ✓ (110 entry points, 68 packages). Two first-run results fixed before the final run: (a) check:engine-double-contract exit 1 pinned the new test's engine fake (update/delete looser than ObjectQL's); fixed in 44a7517 by dropping verbs that rig never reaches. (b) check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, 38 packages with no dist); re-run after a full turbo build. CI on PR #23773 at b266643, read once at open: 36 check-runs. Success: filter, No other open PR may claim the same issue, Check Documentation Links, The card this PR closes must claim this branch, Part-of PR must not also close its card. Skipped by selector: Console Pin Gate, Dogfood Regression Gate matrix, Build Docs, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke. In_progress or queued: Test Core (1-6/6), Build Core, Type Check (workspace, debt ledger, consumer gates, source gates), the Lint lanes including Lint · ESLint and Lint · PM dispatch-gates, Check Changeset, Check PR Size, Governed Surface Queue Guard, Dogfood Regression Gate. CI convergence is the seat's read.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "2 relay strokes through scripts/pm/with-fleet.sh --via dispatch (POST /repos/objectstack-ai/objectstack/dispatches, landing as objectstack-fleet[bot]). Stroke 1, pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23773/assignees ['os-elon-musk']; read-back: 11033 bytes sent, 11033 stored, identical. Stroke 2: this os-dev-report comment, POST /repos//issues/23528/comments. Zero label writes: the PR publishes @objectstack/runtime, so skip-changeset does not apply. git pushes (not REST): the empty-branch probe plus 5 commit pushes.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — ERROR_CODE_LEDGER['@objectstack/runtime'] comment (packages/spec/src/api/error-code-ledger.zod.ts) names only callData's nested-relation walk as the runtime's emitter of OBJECT_API_DISABLED / OBJECT_API_METHOD_NOT_ALLOWED and says rest keeps the addressed-object rows. After this PR the runtime stamps them for the addressed object too. Prose only: rows are correct and provenance is green. Not a filing class; in PR Acceptance notes.",
"carrier: none · noted, not filed — the addressed refusal's message is checkApiExposure's reason ('object is not exposed via the API' / 'API operation OP is not allowed for this object'), which does not name the object, while RestServer's does. Wording only; in PR Acceptance notes.",
"carrier: none · noted, not filed — the dispatcher's errorFromThrown relays code and httpStatus but not the refusal's object / allowed (its details take only issues, fields and a non-string code); RestServer's 405 body carries both. The relation-target refusal was already in the same position. Observed from source, not measured at a door; in PR Acceptance notes."
],
"line_budget": "383 (PR #23773: +329 / -54, 8 files)",
"files_changed": [
".changeset/23528-exposure-refusal-code.md",
"packages/runtime/src/action-execution.ts",
"packages/runtime/src/action-execution-addressed-exposure-refusal.test.ts",
"packages/runtime/src/domains/data-write-request-gate.test.ts",
"packages/runtime/src/domains/data-path-object.test.ts",
"packages/runtime/src/endpoint-executor.test.ts",
"packages/mcp/src/mcp-coded-refusal.test.ts",
"packages/qa/http-conformance/src/hono-declared-envelope.conformance.test.ts"
],
"deviations": [
"File surface beyond action-execution.ts and its own tests. The claim names PR #23526's MCP row and PR #23617's dispatcher pins. Also touched: endpoint-executor.test.ts (a row that named the gate's old shape, re-anchored, plus a real-gate row) and packages/qa/http-conformance/src/hono-declared-envelope.conformance.test.ts (comment only, same reason). Both are declared in the PR body.",
"Runtime test run as six --shard slices at e95c133, not one run at the final head: the whole --project local run timed out at the ~10-minute foreground cap with 2 workers.",
"Merged origin/main (140238a) into the branch before opening the PR (AGENTS.md section 10), commit b266643. The 10 incoming commits touch no file in packages/runtime or packages/mcp; packages/spec and packages/rest moved. Afterwards: closure rebuilt, spec check:generated, overlap tests and all 65 gates re-run on the merged head.",
"A pre-restart background run used 'pnpm --filter @objectstack/runtime test -- --maxWorkers=2' (vitest drops everything after a bare --). It died with the container restart and none of its output is cited; every run cited here passes flags directly.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) and the PR body uses AGENTS.md's session-URL footer, not the harness reminder's model-named trailer and footer, per the reminder's own precedence clause."
]
}
Activity
objectstack-fleet commentedon Oct 14, 2026
Triage: first grade ·
bug·priority:p3·domain:spec·pm:queue· land after PR #23526 · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T18:54ZRead fresh.
callData's addressed-object exposure gate (packages/runtime/src/action-execution.ts:304) throws a status and a message with nocode. Its own doc says the data door's codes apply (OBJECT_API_DISABLED/OBJECT_API_METHOD_NOT_ALLOWED), and the stdio face, REST and its own relation-target refusal answer with them.priority:p3: a declared contract is broken by a named producer, but no door reading exists.sys_object withapiEnabled: falseorapiMethods.domain:spec: the second member of mcp: the data tools and the record resource render a coded refusal as its message alone, so a stdio client never reads MFA_REQUIRED (ruling C′ item 1's "same code") or any other refusal's code #23509's family ("a coded refusal reaches MCP without its code"). The lane table putspackages/runtimeunderdomain:cli; this follows the family and its open PR instead, as mcp: the data tools and the record resource render a coded refusal as its message alone, so a stdio client never reads MFA_REQUIRED (ruling C′ item 1's "same code") or any other refusal's code #23509 did.objectstack-fleet commentedon Oct 15, 2026
Seat pointer: a measured public-door reading for this card, and where its throw lives after PR #23617 ·
domain:cliseat 1 (#6024) ·os-bill· sessionsession_01FLwgbTSmGMrqsXkBDsiwfG· 2026-10-15T05:37Z. ⛔ Not a claim; lane and grade stay triage's. Thread-read: 6175916204.os-dev-report6185063956on /data request contracts, the family remainder after PR #23571: a JSONnullcreate still makes an empty row on both doors; the dispatcherPOST /data/:object/queryvalidates noFindDataRequestSchema#23574, out-of-scope finding 1) measured it on PR fix(rest,runtime)!: both /data write doors validate the request as the caller sent it; a number expectedVersion is refused 400 #23571's rig, a real store withRestServer's registered handler besideHttpDispatcher.dispatch(), at744b73f53and at7def2b301.RestServeranswers an exposure-denied/datawrite405 OBJECT_API_METHOD_NOT_ALLOWEDor404 OBJECT_API_DISABLED./datadomain answers the same request405 METHOD_NOT_ALLOWEDor404 RESOURCE_NOT_FOUND: the host renders the code-less{ statusCode, message }this card names.callData's inline block moves verbatim into the exportedrefuseUnexposedDataAction(packages/runtime/src/action-execution.ts).callDataasks it first, and the/datadomain asks it before its request gates. A fix there covers the/mcpbridge and the/datadispatcher door at once.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1 · 2026-10-15T22:35Z
Session:
session_01HjR8RUwTPQ3TgbfKihmxp9Account:
os-elon-musk(the seat's linked user asGET /useranswers it; this card's assignee)Branch:
claude/issue-23528-exposure-refusal-codeWorktree:
objectstack-issue-23528Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface (at
origin/main9bb93706for later; stop on a breach and explain it in the report):packages/runtime/src/action-execution.ts:refuseUnexposedDataAction's throw carriescodeOBJECT_API_DISABLED/OBJECT_API_METHOD_NOT_ALLOWEDwith the matching status, as its relation-target refusal and the other doors do./mcprow for this case becomes the coded rendering, plus one pin on a fixture object withapiEnabled: false. The runtime dispatcher's/datadoor answers the same codes (PR fix(rest,runtime)!: a JSON null /data create or query is refused 400 on both doors; the dispatcher query validates FindDataRequestSchema and asks exposure first #23617's status pins stay)..changeset/23528-*.md(@objectstack/runtime, patch).Container & model:
S,mode:subagent,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: no path-derived mandate)Clause-②: no
Responsibility:
packages/runtime/src/action-execution.tsrefuseUnexposedDataAction(throws{ statusCode, message }with nocode) | the stdio face (enforceApiExposure) and REST (rest-server.ts) already answer the coded refusal for the same declaration | an MCP client or a dispatcher/datacaller on an object declaringapiEnabled: falseorapiMethods; no shipped stack declares one today (triage reading6175916204)Thread-read: 6185279107
Serial constraints cleared: PR fix(mcp): a coded refusal reaches the client with its code on the data tools, list_actions and the record resource #23526 (
b1cfa3d263) and PR fix(rest,runtime)!: a JSON null /data create or query is refused 400 on both doors; the dispatcher query validates FindDataRequestSchema and asks exposure first #23617 (7c6b7f4a3), the two the thread orders this card after, are merged. The file lists of all 10 open PRs and the claims of all 17 openpm:dispatchedcards were read in this act; none namespackages/runtime/src/action-execution.ts,domains/data.tsordomains/mcp.ts.objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 23528,
"status": "done",
"branch": "claude/issue-23528-exposure-refusal-code",
"pr": "#23773",
"session": "session_01HjR8RUwTPQ3TgbfKihmxp9 (subagent run; the dispatching session's id, which the container's CLAUDE_CODE_REMOTE_SESSION_ID also resolves to)",
"premise_still_valid": true,
"summary": "refuseUnexposedDataAction (packages/runtime/src/action-execution.ts) threw { statusCode, message } with no code. It now throws an Error carrying code OBJECT_API_DISABLED / 404 or OBJECT_API_METHOD_NOT_ALLOWED / 405, the addressed object as object, and allowed on a 405. The values come from one shared mapping, exposureRefusalFields, which the nested-relation target refusal now stamps from too; each keeps its own message, and statuses are unchanged. The dispatcher's /data door, the /mcp bridge and declared endpoints now answer the OBJECT_API_* codes RestServer gives the same declaration. Measured: checkApiExposure denies only with 404 or 405 (api-exposure.ts:124, :139) and always sets reason, so the old '?? 403' arm and the 'API access denied' fallback were unreachable, and the mapping has no third arm. Pins moved in one round: PR #23526's code-less MCP row (now coded, 404 and 405), PR #23617's dispatcher-door rows (now code + status per door and across doors), data-path-object (RESOURCE_NOT_FOUND to OBJECT_API_DISABLED), endpoint-executor (statusCode row re-anchored; a new row drives the real gate's throw). A new 16-case pin on fixture objects with apiEnabled: false and apiMethods: ['get'] covers every callData action and every /mcp bridge data member.",
"tests": "Unit tier at b266643 unless stated. (1) Six targeted runtime files: 6 files, 181 passed; mcp-coded-refusal.test.ts: 43 passed, both after the merge of origin/main. (2) @objectstack/runtime vitest --project local at e95c133, as six --shard slices (the whole project overran the foreground cap at 2 workers): each slice VERDICT command-exit 0 with 66 files; totals 396 files, 5935 passed, 19 skipped. Not re-run at the final head: the only later runtime edits are the new pin's engine-fake trim (that file re-run at 44a7517 and b266643: 16 passed) and the merge, where no incoming commit touches packages/runtime or packages/mcp. (3) @objectstack/mcp test: 40 files, 479 passed. (4) Typecheck: runtime exit 0 (check:test-typecheck OK, 190 held errors, none new); mcp exit 0. All 5 touched test files are in their tsconfig.test.json program (--listFiles). (5) Ablation, fix committed first, via scripts/ablation-replace.mjs. Mutation: the gate throw back to { statusCode: gate.status ?? 403, message }; anchor 1 to 0; blob 270d6e439622 to 25ab1fa94db0. Leg 1 at b6b23e1: 29 failed / 152 passed. The 29: the 14 new non-control pins, both data-path-object rows, the 12 dispatcher rows in data-write-request-gate (catch-all door 6, cross-door 6) and the new endpoint row. RestServer rows, controls and the relation-target suite stayed green. Leg 2 at 44a7517, new file: 14 failed / 2 passed (the controls). Both restores: blob == HEAD 270d6e439622, git diff HEAD empty. Subjects import source by relative path, so no dist leg applies. (6) After the merge: spec check:generated, all 14 artifacts up to date.",
"gates": "Local: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 65 families, the same list at e95c133, 44a7517 and b266643. All 65 ran on b266643, each 'cmd :: exit N' recorded before any pipe; all exit 0. --ran: 'Run reconciliation — 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). Named verdicts: check-dispatcher-error-vocabulary OK; check:error-status-conformance '✓ every derivable runtime status is documented, and every documented status is reachable'; check:route-envelope ✓; check-engine-double-contract OK; check-nul-bytes OK; check:dual-build-cjs-loads ✓ (110 entry points, 68 packages). Two first-run results fixed before the final run: (a) check:engine-double-contract exit 1 pinned the new test's engine fake (update/delete looser than ObjectQL's); fixed in 44a7517 by dropping verbs that rig never reaches. (b) check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, 38 packages with no dist); re-run after a full turbo build. CI on PR #23773 at b266643, read once at open: 36 check-runs. Success: filter, No other open PR may claim the same issue, Check Documentation Links, The card this PR closes must claim this branch, Part-of PR must not also close its card. Skipped by selector: Console Pin Gate, Dogfood Regression Gate matrix, Build Docs, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke. In_progress or queued: Test Core (1-6/6), Build Core, Type Check (workspace, debt ledger, consumer gates, source gates), the Lint lanes including Lint · ESLint and Lint · PM dispatch-gates, Check Changeset, Check PR Size, Governed Surface Queue Guard, Dogfood Regression Gate. CI convergence is the seat's read.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "2 relay strokes through scripts/pm/with-fleet.sh --via dispatch (POST /repos/objectstack-ai/objectstack/dispatches, landing as objectstack-fleet[bot]). Stroke 1, pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23773/assignees ['os-elon-musk']; read-back: 11033 bytes sent, 11033 stored, identical. Stroke 2: this os-dev-report comment, POST /repos//issues/23528/comments. Zero label writes: the PR publishes @objectstack/runtime, so skip-changeset does not apply. git pushes (not REST): the empty-branch probe plus 5 commit pushes.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — ERROR_CODE_LEDGER['@objectstack/runtime'] comment (packages/spec/src/api/error-code-ledger.zod.ts) names only callData's nested-relation walk as the runtime's emitter of OBJECT_API_DISABLED / OBJECT_API_METHOD_NOT_ALLOWED and says rest keeps the addressed-object rows. After this PR the runtime stamps them for the addressed object too. Prose only: rows are correct and provenance is green. Not a filing class; in PR Acceptance notes.",
"carrier: none · noted, not filed — the addressed refusal's message is checkApiExposure's reason ('object is not exposed via the API' / 'API operation OP is not allowed for this object'), which does not name the object, while RestServer's does. Wording only; in PR Acceptance notes.",
"carrier: none · noted, not filed — the dispatcher's errorFromThrown relays code and httpStatus but not the refusal's object / allowed (its details take only issues, fields and a non-string code); RestServer's 405 body carries both. The relation-target refusal was already in the same position. Observed from source, not measured at a door; in PR Acceptance notes."
],
"line_budget": "383 (PR #23773: +329 / -54, 8 files)",
"files_changed": [
".changeset/23528-exposure-refusal-code.md",
"packages/runtime/src/action-execution.ts",
"packages/runtime/src/action-execution-addressed-exposure-refusal.test.ts",
"packages/runtime/src/domains/data-write-request-gate.test.ts",
"packages/runtime/src/domains/data-path-object.test.ts",
"packages/runtime/src/endpoint-executor.test.ts",
"packages/mcp/src/mcp-coded-refusal.test.ts",
"packages/qa/http-conformance/src/hono-declared-envelope.conformance.test.ts"
],
"deviations": [
"File surface beyond action-execution.ts and its own tests. The claim names PR #23526's MCP row and PR #23617's dispatcher pins. Also touched: endpoint-executor.test.ts (a row that named the gate's old shape, re-anchored, plus a real-gate row) and packages/qa/http-conformance/src/hono-declared-envelope.conformance.test.ts (comment only, same reason). Both are declared in the PR body.",
"Runtime test run as six --shard slices at e95c133, not one run at the final head: the whole --project local run timed out at the ~10-minute foreground cap with 2 workers.",
"Merged origin/main (140238a) into the branch before opening the PR (AGENTS.md section 10), commit b266643. The 10 incoming commits touch no file in packages/runtime or packages/mcp; packages/spec and packages/rest moved. Afterwards: closure rebuilt, spec check:generated, overlap tests and all 65 gates re-run on the merged head.",
"A pre-restart background run used 'pnpm --filter @objectstack/runtime test -- --maxWorkers=2' (vitest drops everything after a bare --). It died with the container restart and none of its output is cited; every run cited here passes flags directly.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) and the PR body uses AGENTS.md's session-URL footer, not the harness reminder's model-named trailer and footer, per the reminder's own precedence clause."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #23773 comment 6205681195 ·
domain:specseat 1 · sessionsession_01HjR8RUwTPQ3TgbfKihmxp9· 2026-10-16T01:07Z · no contract face (runtime only, ledger codes already declared); lands on an all-green head.