Skip to content

fix(qa): derive the authz matrix docblock's ledger figures instead of hand-typing them - #17719

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-17111-authz-matrix-ledger-count
Sep 11, 2026
Merged

fix(qa): derive the authz matrix docblock's ledger figures instead of hand-typing them#17719
os-sales merged 1 commit into
mainfrom
claude/issue-17111-authz-matrix-ledger-count

Conversation

@claude

@claude claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #17111

authz-conformance.matrix.ts stated the REST route ledger holds "94 rows / 19 families" in the present tense and undated. The families count was right; the row count was not. This PR corrects it, records the real cause, and — because triage asked for the derivation rather than the constant — pins all four figures in that sentence to the ledgers they name, so the next drift is red instead of silent.

Today's figure, derived (not taken from the card)

The card's reading was dated 2026-09-09 and the PM seat recorded the row count as NOT MEASURED, so it was re-derived from the ledger's own structure. Two independent derivations, chosen to share no failure mode:

derivation REST ledger runtime ledger
the ledger's own row vocabulary (route: / family: / domain:), scoped to the exported table 91 rows / 19 families 82 rows / 21 domains
brace-depth scan counting top-level object literals — blind to field spelling 91 rows 82 rows

Both agree. The brace-depth leg also returns to depth 0, which is the control on the scope itself: a slice truncated at a premature \n]; would end unbalanced. route: and family: occurrences are equal at 91 (and route: and domain: equal at 82), which is the control on prose leaking into the scope — both fields are one-per-row.

The REST row count is 91 today. The runtime figures in the same sentence measured CORRECT and are unchanged.

The PM seat's failed attempt (grep -c "path:", which returned 1) missed because the ledger spells a row route:, not path:.

The cause is #14503, not the gate move next to it

Re-derived rather than inherited, by measuring the ledger at four refs through the contents API:

commit rows families sdk server-only public
83863b2dfe — parent of #14503 94 19 84 7 3
73709893f1#14503 itself 91 19 81 7 3
f6b7c53db7 — the card's 2026-09-09 reading 91 19 81 7 3
7173d7d1 — this PR's base, today 91 19 81 7 3

The three rows that left, and nothing arrived:

- GET    /api/v1/packages          [family: packages]
- GET    /api/v1/packages/:id      [family: packages]
- DELETE /api/v1/packages/:id      [family: packages]

73709893f1 is titled "fix(rest): the dispatcher's /packages domain is the one implementation of the package read and delete routes (#14503)". Families stayed at 19 because all three carried family: packages, which survives on POST /api/v1/packages/publish.

⭐ So the drift was not the route-guarding move (cc837dbfec) whose own stale figures sat seven lines below it in the same docblock: guarding a route never changes its ledger disposition. Two stale figures, one docblock, two entirely different causes. The docblock now carries that attribution, because a reader who corrects the number without it learns the wrong lesson.

Pinned, not dated — and why

Triage: "this is the sixth card in this round on a hand-typed count that went stale. If a pin is cheap here, that is the better deliverable." It is cheap, so the figure is pinned, not dated.

The companion test already read both ledger files as source text through probeSource, scoped to the exported table. That scoping is now extracted as scopedSource and reused, so there is one scoping rule rather than two — two copies would let a rename widen one while the other stayed correct, and the wider one would still mint plausible numbers.

Three deliberate choices:

  • The pin reads the PROSE, not an exported constant. The defect is a sentence that went false. Lifting the number into a symbol would leave the sentence free to rot again while the symbol stayed green — the same trade that produced this card.
  • It folds the // continuations before matching, and that fold is the instrument. Every figure in that docblock wraps mid-phrase, so a line-at-a-time literal search for the row/family phrase reads zero against the very file that carries it. A control case pins that failure.
  • The anchor is the backticked ledger PATH followed by the parenthesised pair, and exactly one of each is allowed. Keying on bare numbers would have pinned the dated "94 rows to 91" attribution as if it were a claim about today, and would not have refused a second unpinned copy.

⚠️ No widening. Nothing is exported from packages/rest or packages/runtime for this. Both ledgers are only read, through the same mechanism already in place. Clause-②: no still holds, as declared on the card. Hold #13776's two trigger files were read and not touched.

The pin bites — measured both ways

Eight cases, each doctored from the measured value so no case hand-types a count of its own:

case role
both ledger tables readable and non-trivial; renamed export reads EMPTY CONTROL
the figures WRAP, so folding is load-bearing CONTROL
every stated figure equals what its ledger holds today the claim
a stale ROW count is red — the #14503 drift replayed bite
a stale GROUP count is red too bite
DELETING the figure is red — the pin cannot be silenced by dropping its subject bite
a SECOND present-tense copy is red — a new carrier cannot arrive unpinned bite
the anchor is the PATH, so the dated drift note is not pinned as present tense bite

Reverse verification on the real file, from the committed state, with the mutation proved on disk before the run and the restore proved byte-identical after:

mutation  (91 -> 94)  on disk: injected=1 removed=0, blob differs from HEAD: YES
  vitest  ->  exit 1   Tests  6 failed | 49 passed (55)
          ->  "packages/rest/src/rest-route-ledger.ts: docblock says 94 rows, the table holds 91"
restore   git checkout HEAD -- (path)
          ->  blob matches HEAD: YES ; git diff HEAD empty: YES
  vitest  ->  exit 0   Tests  55 passed (55)

Six of the eight fired; the two CONTROL cases stayed green, which is what makes them controls. Both legs ran under a trap ... EXIT INT TERM with an absolute path, and the restore is proved by content, not by an exit code.

The third site, and the dated sibling

authz-probe-blind-spot.census.ts:151 is a non-carrier and is left untouched: under a RE-MEASURED 2026-09-09 heading it writes "sdk 84 -> 81 when #14503 took the three REST package read/delete rows out of the ledger (94 rows -> 91, already recorded ...)" — a dated transition record that names 91 as the current value and #14503 as the cause, so it asserts no stale present-tense figure and already teaches exactly the lesson this card exists to teach.

authz-ledger-population.baseline.ts:61 is left untouched too, per the fence's requirement to state a reason either way: its figure sits under an explicit MEASURED 2026-08-31, and the measurement re-derived above confirms it was true as written on that date (94 rows at 83863b2dfe, pre-#14503). A dated reading of a past state is materially different from a false present-tense claim, and its own arithmetic is unmoved.

The card's executable criterion

grep -n '94 rows' packages/qa/dogfood/test/
  authz-probe-blind-spot.census.ts:151   dated 2026-09-09 transition record, #14503 named
  authz-ledger-population.baseline.ts:61 explicitly "MEASURED 2026-08-31"
  authz-conformance.matrix.ts:56         past tense, #14503 named  (added here)
  authz-conformance.test.ts:1478,1722    the pin's own rationale   (added here)

Zero undated present-tense statements in that directory claim a ledger row count the ledger does not hold, and the ones that remain can no longer go stale unnoticed, because the pin now holds every present-tense figure equal to its ledger.

Verification

Full derived gate union from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (51 commands), run at 75ddfd7e, each exit code captured before any pipe:

  • 50 of 51 exit 0.
  • pnpm check:dual-build-cjs-loads exits 3 = PREREQUISITE NOT MET — it reads built output and eight unrelated packages have no dist/. Its own text: "This is NOT a pass: nothing was measured." NOT MEASURED, unrelated to this diff, left to CI.
  • pnpm --filter @objectstack/dogfood typecheck — exit 0.
  • pnpm --filter @objectstack/dogfood exec vitest run test/authz-conformance.test.ts — exit 0, Tests 55 passed (55) (47 on origin/main, so all 8 new cases ran).
  • pnpm --filter '@objectstack/dogfood^...' build — exit 0.
  • pnpm lint — the whole repo, exactly as the repo spells it (eslint . --no-inline-config): exit 0. No narrowing was needed, so no narrowing is claimed.

pnpm check:cross-package-test-inputs exits 0: the new ledger reads reuse the existing readFileSync(join(REPO_ROOT, ...)) seam, which the gate already recognises.

Not run locally: the sharded Dogfood Regression Gate and Test Core jobs, which boot example apps — declared to CI. The matrix-file half of this diff is comment-only, so the only executable change is in authz-conformance.test.ts.

Changeset

skip-changeset. @objectstack/dogfood is private: true with no files[], so it publishes nothing; both changed files are test-layer, and the matrix change is entirely inside // comments. No released package's shipped contents move. ⛔ No behaviour change is claimed.

Acceptance notes

Noted, not filed:

  • The same docblock carries 22 of 29 / 76% / population 80. All three measured correct today and are the post-docs(qa): re-measure the two route-census figures #16955 left behind, and split the two moves inside one sentence #17110 corrected values — deliberately untouched, and the card is explicit that the population is still 80.
  • scopedSource takes the first \n]; after the export, so a ledger array containing a nested array terminated at column 0 would truncate the scope silently. Not reachable today (both tables end balanced, asserted by the new CONTROL case) and not this card's subject. Successor: none currently in flight; whoever next adds a probe with within would be the one to meet it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c


Generated by Claude Code

… hand-typing them

`authz-conformance.matrix.ts` stated the REST route ledger holds "94 rows /
19 families" in the present tense and undated. Measured today the table holds
91 rows / 19 families: the families count was right, the row count was not.

The cause was NOT the route-guarding move (`cc837dbfec`) whose own stale
figures sat seven lines below it in the same docblock — guarding a route never
changes its ledger disposition. It moved when #14503 took the three REST
package read/delete rows out of the ledger (94 -> 91, `sdk` 84 -> 81; families
unmoved at 19 because all three departing rows carried `family: packages`,
which survives on the publish row). Two stale figures in one docblock with two
entirely different causes; attributing this one to the neighbour teaches the
wrong lesson, so the docblock now records the attribution.

Correcting the constant alone would leave the mechanism intact, so all four
figures in that sentence — both ledgers' rows and their families/domains — are
now DERIVED and pinned in the companion test against a scoped read of the
ledger each one names. The pin reads the PROSE rather than an exported
constant: the defect is a sentence that went false, and lifting the number into
a symbol would leave the sentence free to rot while the symbol stayed green. No
symbol is exported from `packages/rest` or `packages/runtime` for it — both
ledgers are only read, through the scoping rule `probeSource` already used,
now extracted as `scopedSource` so there is one rule rather than two.

The pin folds `//` continuations before matching, which is load-bearing rather
than tidying: every figure in that docblock wraps mid-phrase, so a
line-at-a-time literal search for the row/family phrase reads ZERO against the
very file that carries it. A control case pins that failure, because a zero
from an instrument blind to the shape is not a reading of absence. Five more
cases prove the pin bites — stale rows, stale groups, a deleted figure, a
second unpinned copy, and that the anchor is the ledger PATH so the dated
"94 -> 91" attribution is not mistaken for a present-tense claim — each
doctored from the measured value so no case hand-types a count of its own.

The runtime ledger's "82 rows / 21 domains" in the same sentence measured
CORRECT today and is pinned unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 11, 2026
@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 98c904a9b5f1ef1af961d55004efb3c6a3114e53packageMentionDocs.

@claude

claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Review of record — ACCEPT

Head reviewed: 75ddfd7e9659df392ce8aff0e7c147a8b08dc551 · reviewed 2026-09-11T18:48Z (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. The pair declares Clause-②: no and nothing is exported, so no contract review is owed — this is the ordinary pre-landing code review.

Gates on this head, exit codes captured before any pipe

check-clause2-carriers.mjs --pair 17719      EXIT=0   both carriers agree, no widening tell
check-governed-merges.mjs  --pr 17719        EXIT=0   ✅ NOT governed
check-governed-merges.mjs  --test AGENTS.md  EXIT=3   ⛔ GOVERNED    <- the control

⚠️ Exit 3 there is EXIT_TEST_GOVERNED, a real verdict — not the PREREQUISITE NOT MET meaning the same number also carries in that script. The reading is the printed verdict line, ⛔ never the status code.

CI

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

⭐ All FOUR corrected figures re-measured by this seat, independently of the pin

The card was about one stale count. The PR corrects it and pins all four figures in that sentence. Both ledgers read off origin/main with a scoping routine written separately from the PR's, so an agreement is corroboration rather than running the same code:

ledger scope chars route: rows docblock now claims grouping-field occurrences distinct groups claims
packages/rest/src/rest-route-ledger.ts 34,658 91 91 ✅ 91 (= rows) 19 19 ✅
packages/runtime/src/route-ledger.ts 43,763 82 82 ✅ 82 (= rows) 21 21 ✅
NONSENSE control  scoped(src, 'NOSUCH_LEDGER_TABLE')  ->  0 chars
FIRING   control  the real scopes                     ->  34,658 / 43,763 chars

⇒ every figure the docblock now states is what the tree holds, and rows == groupOccurrences in both, which is the PR's own trustworthiness criterion satisfied.

⚠️ One limit on my corroboration, stated rather than glossed. My scoping shares the PR's \n]; boundary, which the dev itself flagged as a boundary condition (a nested array terminated at column 0 would truncate the scope and mint plausible numbers). ⇒ on that one axis my reading is not independent. What argues it does not bite here is the guard the dev built — rows == groupOccurrences in both tables — plus scope sizes of 34 KB and 43 KB rather than a suspicious truncation. ⛔ I did not construct a nested-array fixture to test it.

What makes this a pin rather than a corrected constant — and each property checked against the diff

Triage asked for derivation over a retyped number, and this delivers the stronger thing:

  1. It reads the PROSE deliberately, not an exported constant — with the reason written down: lifting the number into a symbol would leave the sentence free to rot while the symbol stayed green, which is the trade that produced the card. Correct, and the harder choice.
  2. It folds // continuations before matching, and says why that is load-bearing rather than tidying: every figure in that docblock wraps mid-phrase, so a line-at-a-time literal search reads ZERO against the very file that carries it. A CONTROL case pins that failure. ⇒ this round's central lesson is now encoded in the treea zero from an instrument that cannot see the shape is not a reading of absence.
  3. It anchors on the backticked ledger path immediately followed by the parenthesised pair, ⛔ not on bare numbers — so the dated drift note one paragraph below is not pinned as a present-tense claim. That discrimination is the difference between a pin and a nuisance.
  4. It refuses a SECOND copy (claimed.length > 1 is a problem, not a pass) — so an unpinned duplicate reds CI instead of becoming a fresh carrier. Anti-recurrence, which no earlier card in this family had.
  5. It refuses when rows !== groupOccurrences, reporting that neither number is a reading rather than the plausible one. That is "an untrustworthy reading is not a reading" built into the gate.
  6. scopedSource is extracted so the probe reader and the figure pin share ONE scoping rule — two copies would let a rename widen one while the other stayed correct, and the wider one would still mint numbers.

The cause is recorded, and it is the half triage ranked first

The docblock now states that the drift came from #14503 taking three REST package read/delete rows out of the ledger (94 → 91, sdk 84 → 81, families unmoved at 19 because all three carried family: packages, which survives on the publish row) — and ⛔ explicitly not from the route-guarding move cc837dbfec whose own stale figures sat seven lines below. Two stale figures, one docblock, two different causes. A reader who corrected the number without that would have learned the wrong lesson, which is exactly what triage said the card's real contribution was.

⚠️ The runtime half was measured CORRECT and left unchanged, pinned as-is — so the PR does not "fix" a figure that was never broken. ⛔ And the population is still 80; that number was not rewritten.

The two sites it did NOT touch, each with a stated reason

⚠️ Hold #13776's two trigger files were read and not touched, as the claim required — rest-route-ledger.ts and route-ledger.ts are read-only here, and nothing was exported from either.

The open question was ruled before this review, not past it

The dev raised the PR-body footer divergence. Answered on the card (5638867493): A — no edit; ⛔ B refused on authority; C adopted with its reading corrected, because the dev's framing measured false — #17714 went through the same channel and carries one footer, so the append is the channel's behaviour and the doubling is the author's. Recorded as row A8 on #17710.

Verdict

ACCEPT. Two files, packages/qa/dogfood/test/ only; the stale figure corrected and all four pinned against a scoped ledger read; the cause attributed to the right change; the two sibling sites judged rather than swept; skip-changeset warranted. 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/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants