Skip to content

Commit 6e9bee6

Browse files
os-trumpclaude
andauthored
feat(tooling): gate the route-ledger row census so a silent deletion reds (#17155)
`packages/runtime/src/route-ledger.ts` is census-shaped: its value is the completeness of a list, and nothing in this tree read its row COUNT. An index-slice edit meant to add two rows removed 105 lines -- route rows plus the whole `/actions` section -- and exited 0, caught only because an unrelated gate happened to redden. Measured before writing anything: * The file's own suite (route-ledger.conformance.test.ts) is domain-level -- "every registered dispatcher domain has at least one ledger entry" -- which a shorter list satisfies. Positive control: deleting a whole live-registry domain's rows DOES redden it, so "no count gate" is a reading and not an artefact of the search. * The cross-ledger guards (live-mount parity, client-url-conformance) compare against the UNION of five ledgers, which absorbs deletions: 26 of ROUTE_LEDGER's 82 rows have their wire pattern declared by another ledger, by the `* /mcp/**` wildcard, or via `servedBy` -- including two rows in the very `/actions` section the incident removed. * 10 of the 11 `*-route-ledger.ts` files carry a per-route completeness pair in their own package suite; this one is the only member of the family that does not. So the gate carries one occurrence, measured rather than assumed. The count is GENERATED (`--fix` writes the digits, nobody's memory does), the anchor must resolve to exactly one match or report ROTTED_ANCHOR, and the shape is check-lockstep-package-count.mjs's. Deliberately a deletion detector and not a route-granularity cross-check against the dispatcher registry -- that question is open in #17041 and a census stays correct whichever way it is ruled. Claude-Session: https://claude.ai/code/session_012zTkyNHJ7TkuN2oXtP5x37 Co-authored-by: Claude <noreply@anthropic.com>
1 parent fd5cff2 commit 6e9bee6

4 files changed

Lines changed: 457 additions & 0 deletions

File tree

.github/workflows/lint.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3042,6 +3042,21 @@ jobs:
30423042
- name: Lockstep package-count guard
30433043
run: pnpm check:lockstep-package-count
30443044

3045+
# Route-ledger census guard (#16758). `packages/runtime/src/route-ledger.ts`
3046+
# is census-shaped: its value is the completeness of a list, and nothing in
3047+
# this tree read its row COUNT. An index-slice edit meant to add two rows
3048+
# removed 105 lines — route rows plus the whole `/actions` section — and
3049+
# exited 0; it was caught only because an unrelated gate happened to redden.
3050+
# The file's own package suite is domain-level ("every registered dispatcher
3051+
# domain has at least one ledger entry"), which a shorter list satisfies, and
3052+
# the cross-ledger guards compare against a UNION that still holds 26 of the
3053+
# 82 rows' wire patterns from other ledgers. This gate holds the GENERATED
3054+
# census sentence above the array to the array's real length, so a deletion
3055+
# reds and a deliberate change shows its digits moving in the same diff.
3056+
# Full-repo state, not diff-shaped, so it runs its real check unconditionally.
3057+
- name: Route-ledger census guard
3058+
run: pnpm check:route-ledger-census
3059+
30453060
# Release-notes drift guard: the platform is one version-locked train, so
30463061
# every released @objectstack/spec major must have a curated, navigable
30473062
# release page at content/docs/releases/v<major>.mdx. Catches the gap that

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
"check:adr-0087-registration": "node scripts/check-adr-0087-registration.mjs --self-test && node scripts/check-adr-0087-registration.mjs",
129129
"check:changeset-gate-self-tests": "node scripts/check-empty-changeset.mjs --self-test && node scripts/check-adr-0087-registration.mjs --self-test && node scripts/check-changeset-no-major.mjs --self-test",
130130
"check:lockstep-package-count": "node scripts/check-lockstep-package-count.mjs --self-test && node scripts/check-lockstep-package-count.mjs",
131+
"check:route-ledger-census": "node scripts/check-route-ledger-census.mjs --self-test && node scripts/check-route-ledger-census.mjs",
131132
"check:override-consistency": "node scripts/check-override-consistency.mjs --self-test && node scripts/check-override-consistency.mjs",
132133
"check:vendor-export-contract": "node scripts/check-vendor-export-contract.mjs --self-test && node scripts/check-vendor-export-contract.mjs",
133134
"check:vendor-export-contract-resolve": "node scripts/check-vendor-export-contract.mjs --self-test && node scripts/check-vendor-export-contract.mjs --resolve",

packages/runtime/src/route-ledger.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,32 @@ export const NON_DISPATCH_MOUNT_PREFIXES = [
270270
'/.well-known/objectstack',
271271
] as const;
272272

273+
/**
274+
* The ledger.
275+
*
276+
* CENSUS (generated): this list holds 82 rows.
277+
*
278+
* ⛔ THAT NUMBER IS WRITTEN BY A TOOL — never by hand.
279+
* `pnpm check:route-ledger-census` counts the rows below and fails when the two
280+
* disagree; `node scripts/check-route-ledger-census.mjs --fix` rewrites the
281+
* digits and nothing else. It exists because this list is CENSUS-SHAPED: its
282+
* value is its completeness, and a WRONG row fails a gate that reads rows while
283+
* a MISSING row fails only a gate that knows how many rows there should be.
284+
* #16758 filed the second kind, after an index-slice edit meant to add two rows
285+
* removed 105 lines — route rows plus the whole `/actions` section — and exited
286+
* 0, caught only because an unrelated gate happened to redden.
287+
*
288+
* So a PR that really adds two rows moves this number by two, in the same diff
289+
* as the rows; one that claims to and removes twenty-six cannot. ⛔ Do not
290+
* "correct" the digits to make a build green — regenerate them, and read what
291+
* they moved by.
292+
*
293+
* It is a DELETION detector and deliberately NOT a route-granularity check
294+
* against what the dispatcher registers: whether this ledger should be checked
295+
* per route, given `DomainHandlerRegistry.list()` exposes domains and not
296+
* routes, is an open maintainer decision in #17041. A census stays correct
297+
* whichever way that is ruled.
298+
*/
273299
export const ROUTE_LEDGER: readonly RouteLedgerEntry[] = [
274300
// ── well-known ────────────────────────────────────────────────────────────
275301
{ route: 'GET /.well-known/objectstack', domain: '/.well-known/objectstack', absolute: true,

0 commit comments

Comments
 (0)