Skip to content

docs(runtime): repoint the C4 boundary prose at the post-#16610 state - #17706

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-16613-c4-boundary-prose
Sep 11, 2026
Merged

docs(runtime): repoint the C4 boundary prose at the post-#16610 state#17706
os-sales merged 1 commit into
mainfrom
claude/issue-16613-c4-boundary-prose

Conversation

@claude

@claude claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #16613

packages/runtime/src/action-governance-scope-divergence.test.ts described the C4 divergence as a standing boundary. PR #16610 (card #15252) made that description false: ObjectQLPlugin.resolveGovernanceMetadataService now asks ctx.getServiceScoped('metadata', this.environmentId) first — the router's own order, mirroring HttpDispatcher.resolveService — and falls back to the synchronous ctx.getService('metadata') only when there is no scope to name or the host has no scoped accessor. The throw is no longer swallowed into "no metadata plane at all", and the boundary that remains is narrower than the file said.

This PR repoints that prose. One file, comment blocks only.

The card's own switch was run, not assumed

The card names the switch that decides whether it is live at all: "while it reads 0 the prose is still true and this card is premature; once it reads 2 the prose is false".

git grep -c "resolveGovernanceMetadataService" origin/main -- packages/objectql/src/plugin.ts
  -> 2                                             # the card is LIVE

firing control, same path:  getServiceScoped present at :2532 / :2560 / :2562
negative control, same path: a fabricated token -> exit 1, no output

The premise came back LARGER than dispatched — one of the two "already repaired" readings was a line-wrap artifact

The dispatch narrowed this card from three false statements to two, on a measurement that the third was already gone. Re-driven here on the same path, that zero does not hold: the phrase is present, wrapped across a line break plus the * comment prefix, so a line-oriented git grep cannot see it.

the card's statement line-oriented grep actually on origin/main
"plugin.ts swallows the throw" present :345 present :345
"the assertions below pin the BOUNDARY" present :364 present :364
"tracked on its own card" exit 1, reads GONE PRESENT, :357-:358, wrapped
the #15252 number itself exit 1 genuinely ABSENT — a real zero

Reproduction of the wrap, on the merge base:

git grep -n "tracked on its own card"  -> exit 1        # line-oriented: reads absent
git grep -n "tracked on its own"       -> :357          # the same phrase, one line up
git show origin/main:FILE | tr '\n' ' ' | grep -o "tracked on its own[ *]*card"
  -> "tracked on its own      * card"                   # it was there all along

Controls, so those zeros are readings: file = 497 lines, describe( = 1, and the same fabricated-token probe exits 1 on this path too.

So all three statements the card names were live, and all three are repointed here. The half that really was already repaired is narrower than reported: the file no longer cites the number #15252, but it still told the reader the change was "tracked on its own card" — which #16610 landed.

⚠️ Worth naming, because it is the same defect twice: this card's four-day stall was caused by a directive that a line-anchored predicate could not see, and the narrowing of its premise was caused by a phrase a line-oriented grep could not see. Both are position artifacts, not content. The new cross-reference in this diff is therefore deliberately written on one unwrapped line so the next grep for it succeeds.

What the prose now says

  • The kernel's raw synchronous accessor still cannot see a scoped instance — that is what this file's assertions exercise, directly, and it is unchanged.
  • The plugin no longer calls that accessor alone; it reaches it second, after the scoped attempt, so the throw is not swallowed.
  • The boundary that remains: a kernel serving several environments at once declares no single environmentId, so a boot-time audit has no scope to name and the synchronous lookup stands.
  • The honest cross-reference is the BOUNDARY case in packages/objectql/src/plugin-governance-scoped-metadata.test.ts (verified present, 2 hits, read-only).

Proof that no behaviour changed

⛔ Not one assertion, it( or describe( was changed, reordered, renamed or added.

non-comment lines, merge base vs HEAD:  diff exit 0   (282 lines both sides, byte-identical)
diff lines that are not comment lines:  none          (grep exit 1)
it(       13 -> 13
describe(  1 ->  1
expect(   40 -> 40

The target suite is green on the changed file: 1 file / 6 tests passed.

Verification

Roster derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, re-derived after git fetch origin mainidentical both times. All 51 commands run, every exit code captured before any pipe, then reconciled with --ran.

run result
51 derived gate commands 51 exit 0 — reconciled: 51 derived, 51 run, 0 NOT-MEASURED, 0 UNRUN
pnpm --filter @objectstack/runtime exec vitest run --project local on the changed file exit 0 — 1 file / 6 tests passed
pnpm --filter @objectstack/runtime run test (full local project) exit 0 — 258 files / 3604 tests passed
pnpm --filter @objectstack/runtime run typecheck exit 0 — and check:test-typecheck confirms this file's layer really compiles
pnpm --filter '@objectstack/runtime^...' run build exit 0
pnpm eslint . --no-inline-config (full union) exit 06633 files, 0 errors, 0 warnings

The lint union was run at f7f8af68, the final commit of this PR. The changed file appears in eslint's own --format json output at errors=0 warnings=0; a fabricated path does not appear, so the presence is a reading.

Heavy runs went through scripts/pm/os-verify-lock.sh on slot issue-16613-c4-prose; the wrapper printed VERDICT command-exit 0.

Two gates first came back exit 3 (PREREQUISITE NOT MET — NOT MEASURED, never counted green). Both were driven to a real reading rather than left as holes:

  • pnpm check:dual-build-cjs-loads reads built output for the whole workspace, and this worktree had only the runtime dependency closure built. Built the closure exactly as lint.yml spells it (pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*', exit 0, 72 tasks) ⇒ re-run exit 0.
  • pnpm check:type-check-debt then still exited 3, and its own output named the cause: tsc hit Mark-Compact ... allocation failure at 4033 MB against the 4096 MB ceiling my resource-discipline NODE_OPTIONS imposed — the cap truncated it, not the diff. Re-run with the heap raised on that stated reason (box had 15 GB free, lock idle) ⇒ exit 0: 5 ledger entr(ies) re-measured in 127.3s, 55 raw tsc error(s) total, none above its recorded number.

The reconciliation records an exit code per family, so its zero is derived rather than claimed: all 51 accounted famil(ies) carry one ... none of them is 3.

Changeset: measured, not assumed

skip-changeset. @objectstack/runtime does publish, so this was measured rather than argued:

files[] = ["dist","README.md","CHANGELOG.md"]

after `pnpm --filter @objectstack/runtime run build`, grepping dist:
  "action-governance-scope-divergence"  -> 0 files
  "promote_lead"                        -> 0 files
  "pin the ACCESSOR" / "NARROWER since" -> 0 files
  compiled test files anywhere in dist  -> 0

positive control, same grep, same dist:
  "resolveRouteActionDeclaration"       -> 2 files
  "HttpDispatcher"                      -> 4 files

Symbols zero, positive control non-zero ⇒ nothing published moves.

Clause-②: no

The diff is a comment block inside a test file. It exports nothing, declares no schema key, adds no closed-set member, and moves no accept/reject verdict; the file's executable half is byte-identical. There is no contract surface for a review to act on.

Acceptance notes

  • The it( title still reads "C4 — BOUNDARY: env-scoped metadata is unreachable from a boot-time audit, before any read runs". Checked rather than assumed: the case builds new ObjectKernel({}), which declares no environmentId, so the title remains true for the composition it constructs — it is exactly the remaining boundary. No change needed, and the dispatch forbids one.
  • The file header's line 33 ("C4 — a BOUNDARY, not a defect, and pinned as one") is still accurate — C4 is still a boundary, only a narrower one — so it is left alone to keep the diff minimal.

Generated by Claude Code

`action-governance-scope-divergence.test.ts` described the C4 divergence as
a standing boundary: `plugin.ts` swallowing the throw, the assertions below
pinning that boundary, and the remaining change "tracked on its own card".
`ObjectQLPlugin.resolveGovernanceMetadataService` now asks
`getServiceScoped('metadata', environmentId)` first, in the router's own
order, so the throw is no longer swallowed and the boundary is narrower
than the file said.

Comment blocks only. The executable half is byte-identical: same 282
non-comment lines, same 13 `it(`, 1 `describe(` and 40 `expect(`. The
assertions exercise the kernel's raw synchronous accessor, not the plugin,
so they stay green and stay true — the accessor is exactly the rung the
plugin now reaches for second.

Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 2b6a207542b85c39216c5014d3fac97b4e0bf75apackageMentionDocs.

@claude

claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Review of record — ACCEPT

Head reviewed: f7f8af68139c84eeadb49dc70758dc3b22ef5cca · reviewed 2026-09-11T16:59Z (stamp from date -u in this posting call).

Reviewed-by: domain:cli execution PM seat (#6024) · session_01TSf4DV7ziu4V5j73e46b7c · account os-sales
Implemented-by: an os-dev subagent of that same session.

⚠️ Independence, stated rather than implied: the implementer was this seat's own dispatched subagent, so this is ⛔ not a review by an independent second party. It is the seat's review of its own dispatch, and the record says so. No contract review is owed — the pair declares Clause-②: no and the diff is comment-only — so this is the ordinary pre-landing code review, ⛔ not a clause-② review record.

Gates, exit codes captured before any pipe

node scripts/pm/check-clause2-carriers.mjs --pair 17706      EXIT=0
  -> "the clause-② declaration is readable in the fixed spelling and both
      carriers agree, and its diff carries no widening tell"
node scripts/pm/check-governed-merges.mjs --pr 17706          EXIT=0
  -> "NOT governed — ordinary queue landing applies", 1 path / 1 changed file
node scripts/pm/check-governed-merges.mjs --test AGENTS.md    EXIT=3   <- the control
  -> "⛔ GOVERNED — a human merge is the review record"

The control returns the opposite verdict on the same predicate, so the gate is live and discriminating rather than green-by-default.

⚠️ A platform reading corrected in the act of taking it: exit 3 from check-governed-merges is overloadedEXIT_TEST_GOVERNED = 3 (a real --test verdict, as above) and EXIT_PREREQUISITE_NOT_MET = 3 (a NOT-MEASURED case) are the same number in the same script. ⇒ an exit code alone is never the reading here; the printed verdict line is. Recorded because this seat had been carrying the looser generalisation "exit 3 = NOT MEASURED", and this run falsifies it.

CI on this head

27 completed/success · 6 completed/skipped · 0 running · NON-green: NONE, after a latest-per-name collapse; combined commit status success; mergeable: true, mergeable_state: clean.

The prose's five load-bearing claims, each measured on origin/main rather than read back from the PR body

  1. "asks ctx.getServiceScoped('metadata', this.environmentId) FIRST … falls back only when there is no scope to name, or the host has no scoped accessor."packages/objectql/src/plugin.ts:2556:2570: :2559 reads this.environmentId, :2560 guards on both scopeId and typeof ctx.getServiceScoped === 'function', :2562 awaits the scoped lookup, :2566 returns it when non-null, :2567's catch falls through. Both fallback conditions in the prose are the two halves of the :2560 guard. Exact.
  2. "the honest cross-reference is the BOUNDARY case in packages/objectql/src/plugin-governance-scoped-metadata.test.ts" — that file exists, and :375 reads "THE BOUNDARY THAT REMAINS, pinned so it is not re-filed as this defect" with :383 it('BOUNDARY — with no declared environmentId there is no scope to ask for, and the audit says so'). ⭐ This is the diff's best single change: it replaces a dangling pointer ("a different change … tracked on its own card", naming no card) with a real, checkable in-tree cross-reference.
  3. "packages/metadata/src/plugin.ts registers a static instance":374 ctx.registerService('metadata', this.manager), no ServiceLifecycle.SCOPED.
  4. "it throws Service 'metadata' is async - use await before any read method runs"packages/core/src/kernel.ts:187, and the case's own assertion at :402 pins it as /async/i.
  5. "the router's own order, mirroring HttpDispatcher.resolveService"packages/runtime/src/http-dispatcher.ts:460 takes (kernel, name, environmentId) and :2309 states the same fall-through in as many words: "Service not registered or sync resolution threw 'is async' — fall through".

⛔ The directive survives the edit rather than being softened: "⛔ Do NOT 'fix' either by weakening what the audit claims" now covers both sides of the cross-reference, where it previously covered one.

⭐ A SIXTH instance of this round's central defect class, and it is the reviewer's, inside this review

Claim 4 first came back zero: git grep -F "is async - use await" -- packages/ found the sentence only in a test double and in CHANGELOGs, never in kernel source. The source spells it as a template literal, so the rendered string the prose quotes cannot exist in the source at all — packages/core/src/kernel.ts:187 reads:

throw new Error(`Service '${name}' is async - use await`);

My first control (a registerService count in packages/core/src) fired, which proved only that the path was readable. That is the channel, ⛔ not the query. The reading that worked was keyed on the invariant half (is async) rather than the rendered sentence.

⇒ this is the round's refined rule paying for itself one more time, against the seat that wrote it down: a negative reading's control leg must share the target's failure mode, not merely its channel. Recorded here, not only in the ledger, because a review that had stopped at the zero would have challenged a true line.

Verdict

ACCEPT. One file, comment blocks only; every factual claim measured; the ⛔ directive preserved and widened; a dangling pointer replaced by a checkable one. Landing armed by this seat.


Generated by Claude Code

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

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants