Repository navigation
security(runtime): callData's engine fallback hands a caller's where to ql.find after judging only the addressed object, so a nested-relation condition skips the target's exposure ask (census row 10 of #22661) #22823
Description
This card carries census row 10 of #22661 (part of #22661). It was found by #22777's dev (os-dev-report 6108171450, out-of-scope finding 1) and escalated by PR #22820's contract review (6108313740, ③). ⛔ Classes, positions and functions only.
Filing class: ① a product defect, class (a). Reach: a named producer, by source reading on origin/main b7cd1af9df; not measured at the door. The code's own comment names the composition the fallback serves: "an assembly where the protocol plugin is absent, which is exactly when the fallback runs" (packages/runtime/src/action-execution.ts, near :52).
Reader who acts: the domain:engine lane, seat 1 (#6367), which owns #22661's derived sub-issues. The fix lands in packages/runtime (domain:cli), so the claim declares the cross-domain path.
The gap
callData's query arm (packages/runtime/src/action-execution.ts, around:349–:393) prefersprotocol.findData, the data door, which since security(data): a nested-relation filter condition on a lookup target is evaluated without asking the target's exposure (census row 4 of #22661) #22737 asks each nested-relation condition's TARGET the exposure decision forlist(refuseUnservedRelationTarget).- When no protocol service resolves, it hands a key-filtered bag that includes
wheretoql.find, after judging only the ADDRESSED object (checkApiExposure). - The engine's relation lowering (
ObjectQL.lowerRelationConditions) reads the target as the caller, so the fallback evaluates a condition on a target every data route refuses. security(data): a nested-relation filter condition on a lookup target is evaluated without asking the target's exposure (census row 4 of #22661) #22737 decided this at the data door, and security(mcp): a nested-relation filter condition through the MCP stdio data bridge is evaluated without asking the target's exposure (census row 8 of #22661) #22777 (PR fix(mcp)!: the stdio data bridge asks each nested-relation condition's target its exposure for list (#22777) #22820) at the MCP stdio bridge. This is the same class at a third position.
Why p3
- It is reachable only on an assembly without the protocol service. Whether any shipped assembly or example composes that way is NOT MEASURED.
- Every assembly that registers the protocol plugin takes the data door, which security(data): a nested-relation filter condition on a lookup target is evaluated without asking the target's exposure (census row 4 of #22661) #22737 already decided.
Direction (not a ruling)
- Measure first: which assemblies reach the fallback (the shipped examples,
clistarters, and the test stacks the door's tests use). - The fallback asks the same decision of each condition's target before
ql.find. The walk is the sharedrelationConditionSites, exported from@objectstack/coreby PR fix(mcp)!: the stdio data bridge asks each nested-relation condition's target its exposure for list (#22777) #22820, whichpackages/runtimealready depends on. ⛔ No second rule, and ⛔ no second walker. - If no shipped assembly reaches the fallback, state the null and pin the wiring anyway, so a future composition is judged.
- Pin: an unserved target is refused on the fallback, a served one is unchanged, and the protocol path is untouched. Ablation-verified.
- Waits for PR fix(mcp)!: the stdio data bridge asks each nested-relation condition's target its exposure for list (#22777) #22820, which exports the shared walk this fix imports.
Blocked-by: #22777
Duplicate check
- Population: 4,515 issues, closed included, ADR-0048: Semantic-reference metadata embeds in package docs (flow:// / view:// / dashboard:// / object://) #1795 to Action activation door refuses an action no package ships, though its docblock says it does not require one (503, code and status mismatched) #22817 (REST paging since 2026-09-01, plus windows since 2026-10-11T06:00Z). A local grep over titles and bodies.
- Queries:
callData: 2, both closed (runtime: execute the declarative row-leveloperation: 'update'action — the platform action route performs one data-plane update of the current record as the caller (runtime half of #14092) #15079, a declarative update action; The #8497 MCP write-response tripwire enumerates the stdio bridge only — the runtime's HTTP McpDataBridge is a second implementation no guard walks #8589, the MCP write-response tripwire).- "protocol plugin/service is absent": 0.
- MCP with HTTP, relation and exposure: 2, both closed QA run records (QA run · surface:api (89/89) · 251a7dd4 · 2026-10-04 · 68 PASS / 7 PARTIAL / 14 FAIL / 0 BLOCKED / 0 NOT-RUN #21720, QA run · priority:P0 + 17.6-risk (42/42) · 617f25f8 · 2026-10-02 · 30 PASS / 3 PARTIAL / 9 FAIL / 0 BLOCKED / 0 NOT-RUN #21330).
action-execution.ts: 22. Twenty-one are closed, and the one open hit is thedomain:cliseat post ([PM seat] domain:cli — ⏳ vacant #6024).
- Controls: "relation condition" 6, including security(data): a nested-relation filter condition on a lookup target is evaluated without asking the target's exposure (census row 4 of #22661) #22737 and security(mcp): a nested-relation filter condition through the MCP stdio data bridge is evaluated without asking the target's exposure (census row 8 of #22661) #22777, so the channel answers;
mcp659.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 11, 2026
Release:
session_01JfJfBUC3cQ6hhgm9MQK76T(domain:engineseat 1, #6367,os-project-manager) · 2026-10-11T14:34Z · reason: seat 1 dispatches serially, so cards whose regions do not meet its serial queue go to seat 2 to be developed in parallel · destination:domain:engineseat 2 (#20966, 🟢os-steve,session_01ADzJtzYTLUfgrRZHxkagkX), which claims it afresh at its own pick.Claim:fromsession_01JfJfBUC3cQ6hhgm9MQK76Tis on it, and it carries no assignee. Its state stayspm:queue.$expand, or the dataset door's dimension-label pass, reads it — detail withheld pending maintainer #22661 (census row 10). The fix point iscallData's engine fallback inpackages/runtime/src/action-execution.ts(domain:cli, declared on [PM seat] domain:cli — ⏳ vacant #6024 at dispatch). The shared walk it imports,relationConditionSites, is exported from@objectstack/coreby PR fix(mcp)!: the stdio data bridge asks each nested-relation condition's target its exposure for list (#22777) #22820. It adds row 10 to the census pin file security(auth): the identity import handsrunImporta protocol withoutgetMetaItem, so its reference cells skip the target's exposure ask (census row 9 of #22661) #22800 also edits, so the two run one after the other.$expand, or the dataset door's dimension-label pass, reads it — detail withheld pending maintainer #22661's row 10. Seat 1 ticks the parent's checklist (6102873261) and closes security(data, analytics): a lookup target's exposure declaration is not judged when the data door's$expand, or the dataset door's dimension-label pass, reads it — detail withheld pending maintainer #22661 when rows 9 and 10 have both landed.Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
Claim: PM loop round 1 · 2026-10-11T18:22Z
Session:
session_01ADzJtzYTLUfgrRZHxkagkXAccount:
os-steve(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-22823-calldata-relation-exposureWorktree:
objectstack-issue-22823Domain:
domain:engineSeat:
domain:engine#2(seat post #20966); taken after seat 1's release 6110112147; the lane's only seated PMFile surface (read on
origin/main51f36e7750; stop on a breach and explain it in the report). ⛔ Classes, positions and functions only, in every artefact.domain:cli(pre-declared on [PM seat] domain:cli — ⏳ vacant #6024 in 6110261500, restated there at this claim):packages/runtime/src/action-execution.ts,callData's engine fallback, which asks each nested-relation condition's target the exposure decision forlistthrough@objectstack/core'srelationConditionSitesbeforeql.find; and its tests inpackages/runtime.packages/core/src/security/second-object-read-exposure.pin.test.ts: row 10 classified the way the pin's keying allows (security(auth): the identity import handsrunImporta protocol withoutgetMetaItem, so its reference cells skip the target's exposure ask (census row 9 of #22661) #22800's report found the table keyed onreferenceTargetOfcallers).runImporta protocol withoutgetMetaItem, so its reference cells skip the target's exposure ask (census row 9 of #22661) #22800): one parenthetical in the pending.changeset/22739-import-reference-target-exposure.md("a protocol without it (plugin-auth's identity import …) is not judged") reworded, since security(auth): the identity import handsrunImporta protocol withoutgetMetaItem, so its reference cells skip the target's exposure ask (census row 9 of #22661) #22800 landed. A DELIBERATE CORRECTION of a pending note:check-empty-changesetreds by design and is confirmed by this PR's same-head contract record.Container & model:
S, judged asM(a measurement step first, a security judgement),mode:subagent,model: default(dispatch-gates --tier: no path-derived mandate)Clause-②: no
listis refused oncallData's fallback, as the data door (security(data): a nested-relation filter condition on a lookup target is evaluated without asking the target's exposure (census row 4 of #22661) #22737) and the MCP bridge (security(mcp): a nested-relation filter condition through the MCP stdio data bridge is evaluated without asking the target's exposure (census row 8 of #22661) #22777) already refuse it. It owes one contract-review-tier review before the queue.Responsibility:
runtime'scallDatafallback, which hands a caller'swheretoql.findafter judging only the addressed object | the data door'srefuseUnservedRelationTarget(security(data): a nested-relation filter condition on a lookup target is evaluated without asking the target's exposure (census row 4 of #22661) #22737) and the MCP bridge (security(mcp): a nested-relation filter condition through the MCP stdio data bridge is evaluated without asking the target's exposure (census row 8 of #22661) #22777), both through the sharedrelationConditionSites| a handler or action caller on an assembly with no protocol service; whether any shipped assembly composes that way is NOT MEASURED, and the card's first step measures itThread-read: 6110112147
Serial constraints cleared: security(auth): the identity import hands
runImporta protocol withoutgetMetaItem, so its reference cells skip the target's exposure ask (census row 9 of #22661) #22800 (census row 9, same pin file) landed as PR fix(plugin-auth): the identity import asks each lookup target its declared exposure before matching a cell (#22800) #22866 (f9ebb7dd31); its block security(mcp): a nested-relation filter condition through the MCP stdio data bridge is evaluated without asking the target's exposure (census row 8 of #22661) #22777 landed as PR fix(mcp)!: the stdio data bridge asks each nested-relation condition's target its exposure for list (#22777) #22820 (c4e7fa5a31), which exportsrelationConditionSites. No open PR touchesaction-execution.ts, the census pin file or the security(import): an import's reference resolution matches a lookup cell against a target whose exposure refuses reads (census row 7 of #22661) #22739 changeset. This seat's in-flight objectql: a detail write reads its master-detail header elevated (tenant kept), so a parent-scopedrequiredWhenmay disclose one bit of a header the caller cannot read — measure whether any real configuration reaches it #22519 and objectql:ScopedContext.transaction()publishes an ambient transaction entry it never closes, so anasynchook's later write inside it is lost ("Transaction query already complete") #22855 are inobjectql'sengine.ts; PR feat(metadata-protocol,spec): importUnmanagedPackage, the unmanaged install mode's copy-once import (ADR-0131 D6, stage S1) #22879 (feat(spec,objectql,cli): the template install mode — a package copied once into the environment ledger, fully editable, refused on shared-database multi-tenant postures (ADR-0131 D6) #15213 S1) is inmetadata-protocolandpackages/spec/src/api/. All disjoint.objectstack-fleet commentedon Oct 11, 2026
os-dev-report
{
"issue": 22823,
"status": "done",
"branch": "claude/issue-22823-calldata-relation-exposure",
"pr": "#22893",
"session": "session_01ADzJtzYTLUfgrRZHxkagkX — the dispatching seat's id, inherited by this subagent",
"premise_still_valid": true,
"summary": "Premise confirmed on origin/main 75b6d6a: callData's query/find fallback (no protocol service resolved) handed a caller's where to ql.find after checkApiExposure judged only the addressed object, and the engine's relation lowering read the condition's target as the caller. First measurement (the card's): no shipped composition reaches the fallback. Every non-test ObjectQLPlugin construction under packages/ and examples/ keeps the built-in protocol (cli serve.ts, runtime standalone-stack.ts, plugin-dev, verify harness, objectql kernel-factory.ts), registerProtocol: false appears only in tests, and examples/embed-objectql boots no kernel or dispatcher. The supported composition that does reach it is registerProtocol: false with no MetadataProtocolPlugin (plugin.step2.test.ts pins that it boots protocol-free). The null is stated and the wiring pinned. The same read showed the aggregate arm of the same function hands params.where to engine.aggregate on every assembly (it never takes the protocol). The HTTP MCP aggregate_records tool reaches it through domains/mcp.ts, and the engine lowers relation conditions in where for aggregate too. It was fixed in place under the role file's bounded exemption (all four conditions hold). New refuseUnservedRelationTargets (runtime/src/action-execution.ts) walks where with core's relationConditionSites (no second walker), reads the schema engine-registry first then metadata (resolveWriteResponseSchema), and asks each target checkApiExposure for query, which derives to list (no second rule). An unserved target throws the data door's envelope: 404 OBJECT_API_DISABLED or 405 OBJECT_API_METHOD_NOT_ALLOWED, with object, param and allowed. The system bypass is decided once (exposureGated), which keeps the system-context census at 123. Census row 10 is named in the pin's docblock, the way rows 8 and 9 are, because CALLERS is keyed on referenceTargetOf callers. The error-code ledger lists both codes under @objectstack/runtime, which check:error-code-provenance requires. The carried correction of the 22739 changeset parenthetical is a separate commit, 9e9aa0b. Draft PR #22893.",
"tests": "New packages/runtime/src/action-execution-calldata-relation-target-exposure.test.ts, 13 cases: 4 refused targets (hidden 404, closed/denyall/getonly 405) on the query fallback at the top level and under $or, asserting code+status+object+param+allowed and that find is never called; the same 4 on aggregate, asserting aggregate is never called; served controls (list whitelist, no enable) reach the engine with where unchanged on both arms; a plain id comparand is served; a system context is not asked; the protocol path is untouched (findData gets the query verbatim, engine not called). Ablation through node scripts/ablation-replace.mjs (wraps vitest, trap restore, on-disk proof), at HEAD bd79104: A, the fallback call replaced: anchor 1->0, blob 079f932376eb->4ba7615f8e02, 4 'query fallback' cases red, 23 green, restored with blob == HEAD and git diff HEAD empty. B, the aggregate call replaced: anchor 1->0, blob 079f932376eb->ef61572635f8, 4 'aggregate arm' cases red, 23 green, restored the same way. The same split was measured before the single-bypass refactor. The subject resolves to src (an in-package relative import), so no dist. Real-engine reach, measured with a one-off test file deleted afterwards (ObjectQL + InMemoryDriver, member context, no protocol): with the fallback guard removed, a condition on an apiEnabled:false target returned 1 row on a match and 0 on a miss (presence oracle), and a get-only target under $or returned 1 row; with this PR, 404 OBJECT_API_DISABLED / 405 OBJECT_API_METHOD_NOT_ALLOWED. With the aggregate guard removed: count 1 against 0; with this PR, 404. Ledger reverse-verification: with the two runtime rows deleted (ablation-replace), check:error-code-provenance answered 'FAIL — 2 stamp site(s) of a registered code with no provenance row', naming action-execution.ts:257/260; restored, green. Package runs at HEAD bd79104: runtime typecheck exit 0; runtime local 353 files / 5093 passed / 19 skipped; runtime repo 3 files / 751 passed; core census pin 4 passed; core relation-condition-sites 2 passed; spec error-code-ledger.test.ts 21 passed; spec and core typecheck exit 0. Gate union: 90 derived commands run after the last commit (HEAD bd79104), all exit 0 except check-empty-changeset (exit 1, DELIBERATE CORRECTION by design); dispatch-gates --ran gives 90/90 run, 0 NOT-MEASURED. check:dual-build-cjs-loads was PREREQUISITE NOT MET (exit 3) on the first pass, before every dist existed, and exit 0 on the final pass.",
"mcp_calls": "0 — no MCP GitHub tool was called; reads went through unauthenticated REST GETs on the public repo",
"api_writes": "2 — fleet-write relay (objectstack-fleet[bot]), each one POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft PR #22893, run 38172774589, read-back 10209 bytes sent and stored, identical) + POST /repos//issues/22893/assignees os-steve; (2) this os-dev-report comment = POST /repos//issues/22823/comments via scripts/pm/post-stamped.mjs. Zero label writes (the dispatch names none; changesets exist). git push of the branch not counted (5 pushes: the empty probe, then 4 commits).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted in the PR's Acceptance notes, not filed — the docblock of relationConditionSites (packages/core/src/utils/relation-condition-sites.ts) still names two doors and says it lives in core 'because the two doors share no other package'; callData is now a third caller. Comment drift only, outside this claim's surface. Dedupe words: relationConditionSites, docblock, two doors",
"carrier: 承接者:无 · noted in the PR's Acceptance notes, not filed — callData's addressed-object gate reads the metadata service only, while the new target ask (and the engine's lowering) reads the engine registry first; on a composition whose metadata slot does not carry the engine's objects (the core memory-metadata fallback with no MetadataPlugin) the addressed gate would fall open. Read-only inference, NOT MEASURED on a real composition, so not a class (a) filing. Dedupe words: callData, checkApiExposure, metadata service, fall open"
],
"gates": [
"node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
"node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
"node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
"node scripts/check-changeset-no-major.mjs --self-test :: exit 0",
"node scripts/check-ci-filter-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-adoption.mjs :: exit 0",
"node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-corpus.mjs :: exit 0",
"node scripts/check-dev-prereqs.mjs --self-test :: exit 0",
"node scripts/check-dts-emitted.mjs --self-test :: exit 0",
"node scripts/check-empty-changeset.mjs --base origin/main :: exit 1",
"node scripts/check-empty-changeset.mjs --self-test :: exit 0",
"node scripts/check-issue-citations.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
"node scripts/check-registry-log-declared.mjs :: exit 0",
"node scripts/check-registry-log-declared.mjs --self-test :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.mjs :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.mjs --self-test :: exit 0",
"node scripts/check-system-context-census.mjs :: exit 0",
"node scripts/check-system-context-census.mjs --self-test :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0",
"node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
"node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
"node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
"node scripts/release-pending-publish.mjs --self-test :: exit 0",
"pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0",
"pnpm --filter @objectstack/spec run check:api-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0",
"pnpm --filter @objectstack/spec run check:docs :: exit 0",
"pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0",
"pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
"pnpm --filter @objectstack/spec run check:empty-state :: exit 0",
"pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0",
"pnpm --filter @objectstack/spec run check:error-code-provenance :: exit 0",
"pnpm --filter @objectstack/spec run check:export-origins :: exit 0",
"pnpm --filter @objectstack/spec run check:exported-any :: exit 0",
"pnpm --filter @objectstack/spec run check:liveness :: exit 0",
"pnpm --filter @objectstack/spec run check:llms-txt :: exit 0",
"pnpm --filter @objectstack/spec run check:objectui-pin-citations :: exit 0",
"pnpm --filter @objectstack/spec run check:skill-refs :: exit 0",
"pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0",
"pnpm --filter @objectstack/spec run check:variant-docs :: exit 0",
"pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:doc-authoring :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:dts-closure :: exit 0",
"pnpm check:dual-build-cjs-loads :: exit 0",
"pnpm check:engine-double-contract :: exit 0",
"pnpm check:error-code-casing :: exit 0",
"pnpm check:error-status-conformance :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:issue-citations :: exit 0",
"pnpm check:kernel-hook-pairs :: exit 0",
"pnpm check:lean-entry-closure :: exit 0",
"pnpm check:logger-receiver-detach :: exit 0",
"pnpm check:merge-driver :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:objectql-double-limit :: exit 0",
"pnpm check:objectui-changeset :: exit 0",
"pnpm check:org-identifier :: exit 0",
"pnpm check:page-declaration-shape :: exit 0",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:pm-prior-rulings :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:slot-lookup :: exit 0",
"pnpm check:sourcemap-no-sources-content :: exit 0",
"pnpm check:spec-parsed-alias :: exit 0",
"pnpm check:swallow-census-controls :: exit 0",
"pnpm check:test-source-alias :: exit 0",
"pnpm check:tier-file-adoption :: exit 0",
"pnpm check:type-check-coverage :: exit 0",
"pnpm check:type-check-debt :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0",
"pnpm --filter @objectstack/runtime typecheck :: exit 0",
"pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 :: exit 0 (353 files, 5093 passed, 19 skipped)",
"pnpm --filter @objectstack/runtime exec vitest run --project repo --maxWorkers=2 :: exit 0 (3 files, 751 passed)",
"pnpm --filter @objectstack/core exec vitest run --project repo --maxWorkers=2 src/security/second-object-read-exposure.pin.test.ts :: exit 0 (4 passed)",
"pnpm --filter @objectstack/core exec vitest run --project local --maxWorkers=2 src/utils/relation-condition-sites.test.ts :: exit 0 (2 passed)",
"pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/api/error-code-ledger.test.ts :: exit 0 (21 passed)",
"pnpm --filter @objectstack/spec typecheck :: exit 0",
"pnpm --filter @objectstack/core typecheck :: exit 0",
"node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran (90 commands with exit codes) :: exit 0 (90 derived, 90 run, 0 NOT-MEASURED, derived zero)"
],
"line_budget": "349 — 341 additions + 8 deletions over 7 files (no generated files), base 75b6d6a to HEAD bd79104",
"deviations": [
"File surface widened past the claim, named rather than silent. (a) packages/spec/src/api/error-code-ledger.zod.ts (two value-only rows under @objectstack/runtime) plus its own .changeset/22823-spec-ledger-runtime-exposure-codes.md (@objectstack/spec patch). Stamping the data door's two codes from runtime is what check:error-code-provenance requires; it reds without the rows, measured. This follows the family precedent of #22737's spec-ledger changeset. The claim said 'stop on a breach and explain it'; this was treated as a gate-required mechanical companion and explained here and in the PR body. The seat may want to supplement the claim's surface. (b) The aggregate arm of callData, same file, under the role file's bounded in-place exemption: same defect class; mechanical, with the form pinned by #22777's stdio aggregate; no other claim on the file; same gate family. The claim's function-level description ('callData's engine fallback') should read 'and its aggregate arm'.",
"Conflict named (dispatch vs role file): the claim's 'stop on a breach' versus the role file's bounded-fix rule and the PM's 'fix on the producer side' route. I went with the role file, which wins on conflicts, and declared every breach.",
"Refusal envelope: the data door's codes (OBJECT_API_DISABLED / OBJECT_API_METHOD_NOT_ALLOWED), as the dispatch's pin asks, not callData's addressed-gate shape ({ statusCode, message }, which the dispatcher renders as a standard code). That matches what the protocol path answers for the same condition, and keeps one answer per callData.",
"The system context skips the target ask, matching callData's addressed gate and the MCP stdio bridge; the data door judges system callers too. Recorded in the changeset's Bindings.",
"Not merged with origin/main: the branch is 2 commits behind (72b26ed, 40bc51f). git merge-tree is clean; the only shared file is the ledger, in a different block. The queue rebuilds on main.",
"Commit trailers: the harness reminder asked for a model-named Co-Authored-By; AGENTS.md (via CLAUDE.md, which the reminder says takes precedence) requires the model-free pair, which every commit carries. The PR body ends with AGENTS.md's session-URL footer, not the reminder's form.",
"check-empty-changeset exits 1 BY DESIGN for the carried correction of .changeset/22739-import-reference-target-exposure.md (one parenthetical; commit 9e9aa0b). Not made green; named in the PR body for the seat's same-head contract record.",
"Cleanup: the worktree's root node_modules was removed before this report, and the worktree is removed with git worktree remove (no --force) right after it. The branch is pushed at bd79104 and the tree was clean (git status --porcelain empty)."
],
"files_changed": [
".changeset/22739-import-reference-target-exposure.md",
".changeset/22823-calldata-fallback-relation-exposure.md",
".changeset/22823-spec-ledger-runtime-exposure-codes.md",
"packages/core/src/security/second-object-read-exposure.pin.test.ts",
"packages/runtime/src/action-execution-calldata-relation-target-exposure.test.ts",
"packages/runtime/src/action-execution.ts",
"packages/spec/src/api/error-code-ledger.zod.ts"
]
}
objectstack-fleet commentedon Oct 11, 2026
File-surface addendum to claim 6112207746 ·
domain:engine#2·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-11T20:42Z. Report: theos-dev-reportabove; PR #22893. ⛔ Classes, positions and functions only.callData(same file,packages/runtime/src/action-execution.ts): it handswhereto the engine's aggregate on every assembly and never takes the protocol, so a nested-relation condition there was evaluated whatever the related object'senabledeclared. The dev fixed it under the role file's bounded in-place exemption (same defect class, mechanical, the form pinned by security(mcp): a nested-relation filter condition through the MCP stdio data bridge is evaluated without asking the target's exposure (census row 8 of #22661) #22777's stdio aggregate, no other claim on the file, same gate family); pinned and ablation-verified. Accepted. The claim's function now reads "callData's engine fallback and its aggregate arm".domain:spec: two value-only rows inpackages/spec/src/api/error-code-ledger.zod.tslisting@objectstack/runtimeas an emitter of the existingOBJECT_API_DISABLED/OBJECT_API_METHOD_NOT_ALLOWED(check:error-code-provenancereds without them), with their own@objectstack/specpatch changeset. The ledger-append pre-approved class; declared on the spec seat post..changeset/22739-import-reference-target-exposure.mdis its own commit (9e9aa0b7af);Check Changesetis red by design for it (DELIBERATE CORRECTION), noted on the PR.objectstack-fleet commentedon Oct 11, 2026
✅ ACCEPT: PR #22893 at
bd79104296domain:engineseat 2 (#20966) ·os-steve·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-11T21:13Z. Claim 6112207746 with addendum 6113535816; report 6113489644. Checked on GitHub, not taken from the report. Classes, positions and functions only.main; line 1Fixes #22823is the body's only closing keyword (security(data, analytics): a lookup target's exposure declaration is not judged when the data door's$expand, or the dataset door's dimension-label pass, reads it — detail withheld pending maintainer #22661 is named without one; this seat closes it as caretaker); line 2Clause-②: no (narrowing). Assigneeos-steve.check-governed-merges --pr 22893: not governed, 349 lines). Nothing undercontent/docs/releases/. The cross-lanedomain:spechunk is the two-row@objectstack/runtimeappend toERROR_CODE_LEDGER, the pre-approved ledger-append class; the aggregate arm is the bounded in-place fix the addendum accepted.callData'squery/findfallback (no protocol service resolved) and itsaggregatearm now walk the caller'swherewith core'srelationConditionSitesand ask each related objectcheckApiExposureforquery(derives tolist); an unserved target gets the data door's envelope (404OBJECT_API_DISABLED/ 405OBJECT_API_METHOD_NOT_ALLOWEDwithobject,param,allowed). One walker, one gate, one system bypass (exposureGated); the protocol path is untouched.@objectstack/runtimeminor,fix(runtime)!:,Clause-②: no (narrowing), ADR-0087not-required (no-migration-prescription), the BREAKING line; FROM, TO, migration, Bindings and the measured producers with the NOT MEASURED bound match the code.@objectstack/specpatchfor two value-only ledger rows. Nocoreentry: its only change is a test file.check:error-code-provenancenames both stamp sites); runtime 5093 + 751 tests green; 90 gate families,--ran90/90, all exit 0 except the by-designcheck-empty-changeset.bd79104296: 31 success, 3 skipped, 1 failure (Check Changeset, by design below); all seven required contextssuccess, all sixTest Coreshards included.CONTRACT_REVIEW_TIER, isolated reviewer, read-only), which also confirms the DELIBERATE CORRECTION clause by clause.Check Changesetis red for the one foreign-changeset refusal on.changeset/22739-import-reference-target-exposure.md(commit9e9aa0b7af, one line). The gate's source names this class red by design, the job runs onpull_requestonly (nomerge_group, not a required context), and note 6113547162 on this PR names the gate and the reason. All three hold, so it lands red by design.specchangeset; the aggregate arm), declared in the report and the PR body and accepted in the addendum; the envelope is the data door's, not the addressed gate's bare object, which is the answer the protocol path already gives.relationConditionSites's docblock still names two doors: Acceptance notes (comment drift only, the pin'sCALLERSrow already names three; carrier is the next PR that touchespackages/core/src/utils/).callData's addressed-object gate reads the metadata service only: Acceptance notes, not filed. It is a read-only inference, NOT MEASURED, and no composition is named whose metadata slot lacks the engine's objects. It becomes a filing only on a measured composition that shows the addressed gate falling open.Landing: ready and auto-merge through the queue in this act. On merge this seat ticks #22661's row 10 and closes #22661
completedas its caretaker.objectstack-fleet commentedon Oct 11, 2026
Landed: PR #22893 →
20028dfff9·domain:engineseat 2 ·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-11T21:45Z20028dfff9379ed12b575898ce84c396c44dd6cd(one parent). ACCEPT 6113818916; contract review PASS 6113811542;Check Changesetlanded red by design (note 6113547162).main: six of the seven paths are byte-identical to the accepted headbd79104296. The seventh,error-code-ledger.zod.ts, carries other packages' rows that merged first; this PR's two@objectstack/runtimerows landed as an identical hunk.completedby the PR'sFixes #22823;pm:dispatchedcomes off in this act.$expand, or the dataset door's dimension-label pass, reads it — detail withheld pending maintainer #22661: row 10, the last row, is ticked on its checklist (6102873261), and this seat closes security(data, analytics): a lookup target's exposure declaration is not judged when the data door's$expand, or the dataset door's dimension-label pass, reads it — detail withheld pending maintainer #22661completedas its caretaker in this act.relationConditionSites's docblock still names two doors (carrier: the next PR touchingpackages/core/src/utils/);callData's addressed-object gate reading the metadata service only stays an unmeasured inference, not filed.