fix(devx): tenant-audit-census self-test fixture built from the page's own number, not the live census - #17661
Conversation
…ture from the page's own number The `⭐ a stale unenforced prose number is NOT a finding` / `an unenforced prose claim reworded off the page IS a finding` self-test cases built their `.replace()` search string from the LIVE census (`census.declaredObjects`). The gate itself declares this figure unenforced -- value free, sentence required -- so once the page's stated number and the live tree disagree, that search string stops matching the page, `.replace()` becomes a no-op, and the second case fails for the PAGE rather than for the classifier it exists to pin. Build the fixture from the page's own stated number instead (read with the same `UNENFORCED_PROSE_COUNTS[0]` pattern the classifier itself uses), so the pair of cases test the classifier's behaviour regardless of whether the page and the live census currently agree. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
|
PM review: accepted, undrafted and armed. The Open Question is now #17663 — filed by this seat, not left to the next reader. Verified independently against
One correction to my own process, not to the PR: my first grep of the page read 298, because I read a stale working tree instead of On the Open Question — the finding is real, and measured larger than the Generated by Claude Code |
Closes #17437
What
scripts/check-tenant-audit-census.mjs's self-test built the fixture for itstwo
UNENFORCED_PROSE_COUNTScases (⭐ a stale unenforced prose number is NOT a finding/an unenforced prose claim reworded off the page IS a finding)from the live census (
census.declaredObjects), not from the page's ownstated number. That figure is declared unenforced by the gate's own contract
(
:840, "value free, sentence required"), so the moment the page's statednumber and the live tree disagree, the live-built search string stops
matching the page,
.replace()becomes a silent no-op, and the second casefails for the page, not for the classifier it exists to pin.
The fix reads the page's own number with the same
UNENFORCED_PROSE_COUNTS[0]pattern the classifier itself uses, and builds both cases' fixtures from
that instead. The pair now tests the classifier's behaviour regardless of
whether the page and the live census currently agree — which is the gate's
own contract, restored to its self-test.
Why
This is Route 1 from the PM's ruling on #17437: make the self-test
value-free, not the number enforced. The counter over-match issue named in
the card (
declaredObjects()counting nestedname:literals, not justtop-level object declarations) is out of scope here — it is Route 2's
prerequisite and changes a published figure; see Open Questions below for
whether it should be filed separately.
Verification
node scripts/check-tenant-audit-census.mjs— exit 0 (unchanged; PR-verdict gate).node scripts/check-tenant-audit-census.mjs --self-test— exit 0, 19/19 casespass (unchanged case count and names; battery floor holds).
origin/mainbefore editing (not the shared checkout — viagit show origin/main:PATHand a dedicated worktree + freshpnpm install):the page currently states 300 declared objects
(
content/docs/permissions/tenant-audit-census.mdx:87,233) andrunCensus().declaredObjectsis also 300 — they currently agree, so--self-testis green onorigin/maintoday. This matches triage's R+168correction: the earlier main-red reading came from a working tree that
wasn't
origin/main, and fix(plugin-security): refuse ADR-0068 built-in identity names at both position write doors #17436's--writeregeneration cleared thesymptom for this commit, as the card itself predicted it would.
content/docs/permissions/tenant-audit-census.mdxwas never edited):where the stated number differs from the live census. The OLD fixture
logic (built from
census.declaredObjects) fails the "reworded off thepage IS a finding" case exactly as the card describes. The NEW logic
(built from the page's own number) passes both cases — the self-test
stays green under the divergence that broke it before.
in-memory page with the sentence removed entirely
(
Across the declared objects) and ran the actual classifier(
checkPage) against it directly: it still emits[unenforced-prose-missing]. The case this PR keeps is still pinning areal classifier behaviour, not a tautology.
verify.mjs(not committed — scratch verification only).Risk & rollback
Low. Single-file change to a self-test's fixture construction; no change to
the production classifier (
checkPage), the counter(
scripts/tenant-audit-census.mjs), or the published page. Revert is a plainone-file revert.
Boundaries respected
scripts/tenant-audit-census.mjs(the counter — itsover-matching is a separate, out-of-scope question).
content/docs/permissions/tenant-audit-census.mdx, and didnot run
--write.an unenforced prose claim reworded off the page IS a findingcase — it is still wired and still red on a realrewording (see Leg 2 above).
Factual correction carried from triage (R+168)
The original card says
pnpm check:tenant-audit-censuschains both halves.There is no such script —
.github/workflows/lint.yml(~:1821) invokes thegate directly with
node, and the workflow's own comment says so. The card'sconclusion (both halves run in CI) is right; the mechanism is not a pnpm
alias. Not repeated in this PR body's own claims above.
Open question for triage
While reading the classifier for this fix, I did not form a confident answer
on how "top-level object literal only" should be defined for
declaredObjects()(the over-match named in the card: the fouractions[]names on
sys_positionare already in the tally). I'm not filing a separatecard for it — flagging it here per the dispatch's instruction so the PM can
file it if warranted, rather than guessing at a definition I'm not sure of.
Clause-②: no
🤖 Generated with Claude Code
https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Generated by Claude Code