Skip to content

Rule 3 root extension: a ledgered cross-package prose-id leg on check-doc-authoring, sized by its own AST census - #13355

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-13297-tracker-id-root-widening
Aug 30, 2026
Merged

Rule 3 root extension: a ledgered cross-package prose-id leg on check-doc-authoring, sized by its own AST census#13355
os-project-manager merged 2 commits into
mainfrom
claude/issue-13297-tracker-id-root-widening

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

The #13179 adjudication deferred extending check-doc-authoring Rule 3's scan root beyond packages/spec/src and codified its own revival condition; #13297 measured the condition fired. This PR is the instrument card's delivery: the AST re-census, the architecture choice it prices, and the widened gate.

The AST re-census (the instrument the card demanded)

node scripts/check-doc-authoring.mjs --census now enumerates every internal-issue-id occurrence in the string literals of non-test package sources outside packages/spec, with a consumer-shape classification per site (thrown, callee, property key). Reading at the branch point (74a7804, post-#13298):

reading value
id-bearing string sites 831
id occurrences 974
files carrying one 231
(file, id) pairs 632
grep-shaped estimate on the card ~11 (about 75x under)

The card's own caveat was the reason: this tree's prose is concatenation-split, so line-oriented patterns structurally miss it (re-proven in the new self-test with a package-file fixture). All 11 card sites were re-found by the instrument, at drifted line numbers for protocol.ts. Rough audience families over the 831 (census classification, R+33 criterion, shape-level): about 327 author/customer-directed (lint messages and hints, thrown and factory-built refusals, UI help text, surface descriptions), about 217 out-of-audience (internal route/conformance ledgers, info logs, qa fixtures), about 287 mixed shapes (warn and error log calls and unclassified consumers) where only the text test decides. Even the conservative floor is two orders past the codified threshold of 3.

Route chosen: root widening, ledgered — not per-package guards

Selection evidence, per the four-axis framing:

  • Single-source criterion. One id regex (INTERNAL_ID_SOURCE), one walker, one baseline. Per-package guards would copy the criterion into about 20 packages, each rotting on its own schedule, and a new package would start uncovered; under the widened root a new package is covered the day it appears. This is the repo's own "a second source of truth while the first is still reachable" lesson.
  • Total-string coverage instead of a position climb. The spec leg's closed position list exists to keep VALUES from being reported as prose. An id match has no value-vs-prose ambiguity, and the R+33 audience boundary is explicitly not decidable from call shape (a warn can be in-audience, a throw out). So the cross-package leg counts every id in every non-test string and holds the counts against a pinned baseline; the audience judgment lives in the baseline diff where a reviewer sees it, not in a heuristic. Measured residual false-positive family: 3-digit all-numeric CSS colours — 3 strings in one file, absorbed by the baseline; the red message names the 6-digit spelling as the remedy for a new one.
  • Nothing is relaxed. The spec leg, its per-bucket floors and its no-exemption regime are untouched; the two deliberate test twins are untouched; test files stay out of scope. Coverage only widens (previous cross-package coverage was zero), so the triage boundary condition (escalate on any loosening) is not met.
  • AI-authorship ergonomics. The forward rule is one sentence — no tracker ids in package-source strings; a // comment is the sanctioned home — which is structurally hard to get wrong at authoring time.

The ratchet

scripts/doc-authoring-prose-id.baseline.json pins the census (231 files / 632 pairs / 974 occurrences), generated by the instrument itself.

  • Growth (a new or additional id in a file's strings) is red, naming file, id and site line. Remedy: strip the id or move it to a comment. Adding a baseline entry is marked MAINTAINER-ONLY per the check:engine-double-contract fires at CI time, not authoring time — four independent PRs tripped it on brand-new test files in one shift, and a pre-warning in the brief did not prevent it #8435 convention — check-ratchet-remedy-authority now classifies this gate as marked (CONTROL updated in this PR).
  • Shrink (pinned above measured) is red as stale, remedied by --census-ledger regeneration in the same PR, so burn-down is recorded in review. --census-ledger itself REFUSES to print a baseline that grows any pair beyond the checked-in one, so the shrink remedy cannot silently absorb growth.
  • While the baseline is non-empty the stale arm doubles as the leg's blindness floor: a dormant walker measures 0 against 632 pinned pairs and reds. The spec leg's per-bucket floors keep guarding the shared id regex independently.

The gate's boundary output now states the widened scan (root, exclusion, baseline path) derived from the same constants the scan reads, and the deferral's "siblings are NOT scanned" line is gone — pinned in the self-test in both directions. Cost: about 5s added to check:doc-authoring (1047 parsed sources).

Ablation (real tree, committed baseline)

Injected a historical-shape refusal string carrying (#13999) into packages/objectql/src/having-filter.ts (a newly covered file). Mutation confirmed on disk by grep (marker 1, id 1) before reading any result. Gate: exit 1, printing packages/objectql/src/having-filter.ts #13999 (0 pinned, 1 measured) and the site line and text. Restore leg: git checkout HEAD -- FILE with an absolute path inside an EXIT/INT/TERM trap; proven by marker grep 0, git diff HEAD empty, and git hash-object of the worktree file equal to the HEAD blob (afd1278a...). Gate green again after restore. No build step is involved — the gate runs from source via node, so no dist/exports resolution applies to either leg.

Gate obligations (all at f53336a, the final commit; exit codes captured before any pipe)

  • pnpm check:doc-authoring — self-test AND main run green: ✓ check-doc-authoring self-test: … all hold. and ✓ doc authoring guard: sibling-package prose ids hold the baseline — 831 pinned site(s) across 231 file(s), 79286 string(s) read in 1047 parsed source(s), no growth, no burn-down unrecorded. New battery covers the concatenation-split founding shape, comment/test/spec exclusions, template determinism (an id in an embedded expression counts once), the colour and cross-repo edge cases, the prefilter-superset property, and all three ledger verdicts; a canary red proved the battery executes.
  • pnpm check:ratchet-remedy-authorityOK … 10 mark the expanding remedy ⛔ MAINTAINER-ONLY … Control corpus: 24 hand-classified scripts, set-equality audited both ways.
  • pnpm check:pm-dispatch-gates✓ dispatch-gates self-test: 897 cases pass. (two stale negative pins updated to the new truth: packages/** is positive coverage; spec-outside-src is a recorded bounded over-claim, the tolerated carve-out shape).
  • Full derived family from dispatch-gates.mjs (no paths passed; derivation stderr named this repo at f53336a): agent-test-spelling, bash32-floor, cli-command-ids, cross-package-test-inputs, entry-guard, parse-guard, pnpm-filter-targets, watch-hint-literal, ci-filter-parity, self-test-wired, shard-attestation, bare-root-worklist --self-test, nul-bytes — all exit 0. check-test-completeness exits 3 locally (PREREQUISITE NOT MET, grades a CI-saved turbo log): recorded as NOT MEASURED per its own text; CI runs it with the log.
  • Convention-triggered obligations for gate-script edits both ran: bare-root-worklist --self-test (the bare packages root is declared via the packages/** subtree spelling beside the constant) and check:pm-dispatch-gates.

Scope notes

Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N

Fixes #13297

Generated by Claude Code


Generated by Claude Code

claude added 2 commits August 30, 2026 06:02
…age prose-id leg (#13297)

The #13179 deferral's own revival condition fired: the AST re-census reads
831 id-bearing string sites (974 occurrences, 231 files, 632 (file,id)
pairs) outside packages/spec — the card's grep-shaped ~11 was ~75x under.

Route: root widening over per-package guards — one criterion
(INTERNAL_ID_SOURCE), one walker, one baseline; a new package is covered on
arrival. Coverage is TOTAL over non-test string literals (the audience
question is not mechanizable from the AST; it lives in the baseline diff
where review can see it), held against scripts/doc-authoring-prose-id
.baseline.json: growth reds (strip the id or move it to a comment; baseline
entries are maintainer-only per #8435), shrink reds as stale (regenerate
with --census-ledger, which refuses to grow the file). While non-empty the
ratchet doubles as the leg's blindness floor.

--census prints the instrument's full enumeration with consumer shapes;
--census-ledger prints/refreshes the baseline. Self-test battery covers the
concatenation-split founding shape, comment/test/spec exclusions, template
determinism, the colour and cross-repo edge cases, prefilter superset, and
all three ledger verdicts. Boundary output and the dispatch-gates
declaration (packages/**) updated; check-ratchet-remedy-authority CONTROL
reclassifies this gate as marked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
…ges/** is positive coverage, the spec-non-src residual is a recorded over-claim

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 30, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 30, 2026 06:29
@os-project-manager
os-project-manager added this pull request to the merge queue Aug 30, 2026
Merged via the queue into main with commit 3f54efd Aug 30, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-13297-tracker-id-root-widening branch August 30, 2026 07:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants