scripts/audit-docs.ts — executable codification of the doc-quality audit checklist
Spec:
.specify/features/audit-docs-script.mdStatus: ✅ RESOLVED (iteration 149, 2026-04-27). Spec + plan
- implementation landed in the same cron tick. Iterations referenced: 145 (codify the audit checklist in
AGENTS.md), 146 (miss-target found in iter-145 regex), 147 (regex tightened + ran inline as canonical audit), 148 (cross-file consistency block added — 6th drift class).
Why
The codify-then-execute meta-pattern from iters 145 → 148 has
matured to the point where the per-iteration cost of running 6
separate grep blocks by hand is the dominant friction. The greps
are mechanically deterministic — the running has no human
judgment, only the interpretation does. Codifying the runner
as a TypeScript script collapses the audit into one
pnpm audit:docs call.
Steps
Step 0 — Re-confirm baseline
Iter-148's manual greps returned 6/6 PASS / 0 hits. The new
script should reproduce this exactly on the iter-149 lockfile
state (same project state + same audit semantics → same result).
If the script's first run flags any class as FAIL, that is a
real regression in the script (a regex was transcribed
incorrectly or a whitelist was forgotten), not a real drift.
Step 1 — Author scripts/audit-docs.ts
Create the file at the repo root (scripts/ directory is new —
this is the first root-level script). The file:
-
Defines an
AuditClassinterface:interface AuditClass {name: string;description: string;run: () => AuditResult;}interface AuditResult {pass: boolean;hits: string[]; // each entry: "file:line:context"notes?: string[]; // explanatory notes for whitelisted matches} -
Implements 6 audit classes as functions, each wrapping the exact regex from
AGENTS.md § Doc-Quality Audit Checklist:auditStatusDriftLineAnchored()— iter-145 regex^Status:.*PLANNED|^Status:.*SPECIFIED|^Status:.*DRAFTauditStatusDriftBlockquoteTolerant()— iter-147 regex^>?\s*\*?\*?Status:\s+\*?\*?[^✅]with[^🗄]post-filterauditValueDrift()— count parity for15 R-rules/17 numbered items/31 specs/48 CT cases/1122 Vitest testsauditToolchainVersionDrift()—Astro [0-9]\.[0-9]\.[0-9]\|Vitest [0-9]\.[0-9]\.[0-9]\|...vs current pinned versions parsed fromapps/web/package.jsonauditISRWordingDrift()—fully static\|no SSR\|Fully staticwith whitelisted paths (the AGENTS.md checklist itself contains the literal pattern; sample-basic'sastro.config.tsis permanently pure-static so its docs correctly say "fully static"; etc.)auditStructuralLinkDrift()—\](\.\./relative-link regex with whitelist for known iter-N narrative refs indocs/log.md(the iter-142 fix narrative cites the fixed-then-broken links inline)auditCrossFileConsistency()— readAGENTS.mdandCLAUDE.mddirectly withnode:fs, count matches of^### R[0-9]+:and^[0-9]+\.\s+\*\*respectively, and assert the pair is15 vs 17per iter-148
-
Runs each class in sequence, prints a one-line summary per class (
PASS/FAIL <N hits>), and a final aggregate summary (6/6 PASS / 0 hitsorM/6 PASS / N hits). -
Exits
0on all-pass,1on any-fail.
Step 2 — Wire pnpm audit:docs
Add to root package.json scripts block:
"audit:docs": "tsx scripts/audit-docs.ts"
tsx is already a workspace devDependency via apps/web and
apps/sample-*. The root doesn't currently depend on tsx
directly, but pnpm's workspace hoisting puts tsx in the root
node_modules/.bin so the root-level script resolves
correctly.
Step 3 — First run
Run pnpm audit:docs from the repo root. Expected output:
[1/6] Status drift (line-anchored, iter-145) PASS — 0 hits
[2/6] Status drift (blockquote-tolerant, iter-147) PASS — 0 hits
[3/6] Value drift (count parity) PASS — 0 hits
[4/6] Toolchain version drift PASS — 0 hits
[5/6] ISR wording drift PASS — 0 hits
[6/6] Structural / link drift PASS — 0 hits
[*] Cross-file consistency (AGENTS R-rules vs CLAUDE) PASS — 15 vs 17 (expected)
6/6 PASS — no documentation drift detected.
(Class numbering: the cross-file class is presented as [*]
because it's a parity check rather than a hit-list class — the
expected output is 15 vs 17, not zero.)
Step 4 — Documentation propagation
Update the following files in the same commit:
-
AGENTS.md § Doc-Quality Audit Checklist— add a new "## Runner" subsection at the top of the checklist that reads:Runner:
pnpm audit:docsruns all 6 classes below in one shot viascripts/audit-docs.ts. Use this as the one-liner replacement for the manual grep blocks below; the manual greps remain the canonical reference and the script is the canonical runner. -
CLAUDE.md § Common Commands— add the line:pnpm audit:docs # Run the doc-quality audit checklist (6 classes; iter-149 codification of AGENTS.md greps) -
README.md § Commandstable — add row:| pnpm audit:docs | Run the doc-quality audit checklist (6 classes; iter-149 codification of AGENTS.md greps; exits non-zero on drift). | -
.specify/project.md § Current State— bump iteration header to 149; add a line under "All dependencies" stating thatscripts/audit-docs.tsis the new tool artefact. -
docs/index.md— bump iteration descriptor 148 → 149 with a 1-paragraph summary of the iter-149 work. -
docs/log.md— add a full iter-149 entry following the established format (Headline / What was fixed / What was NOT touched / Verification / Files touched / Saga status / Next Steps).
Step 5 — Verification
pnpm typecheck # Expected: 23/23 FULL TURBO (script is at root, not under any tsconfig include scope)
pnpm lint # Expected: 18/18 FULL TURBO + 0 warnings + 0 errors
pnpm audit:docs # Expected: 6/6 PASS / 0 hits (matches iter-148 baseline)
Step 6 — Commit
Single commit with all 8 file changes:
- NEW
scripts/audit-docs.ts - NEW
.specify/features/audit-docs-script.md - NEW
docs/plans/audit-docs-script.md - MOD
package.json(1 line: scripts.audit:docs) - MOD
AGENTS.md(Runner subsection added) - MOD
CLAUDE.md(1 line in Common Commands) - MOD
README.md(1 row in Commands table) - MOD
.specify/project.md(iteration header + 1 line) - MOD
docs/index.md(iteration descriptor) - MOD
docs/log.md(iter-149 entry)
Acceptance Criteria
(Mirrors .specify/features/audit-docs-script.md AC #1-#12.
Critical ones for plan execution:)
- AC #1 —
pnpm audit:docsruns and exits 0 on the iter-149 baseline. - AC #2 — Implementation is TypeScript via
tsx. - AC #3 — Each audit class wraps the exact AGENTS.md regex.
- AC #4 — All 6 classes implemented.
- AC #5 — Whitelist semantics with iter-citation comments.
- AC #6 — Exit
0on all-pass,1on any-fail. - AC #7 — Hit reporting via
file:line:contextlines. - AC #8 — Cross-file rule-count parity asserts
15 vs 17. - AC #9 — Wired into root
package.json. - AC #10 — Documentation propagation across 6 files.
- AC #11 — Baseline run returns
6/6 PASS. - AC #12 — Lockfile delta zero.
Outcome
(To be filled at iter-149 commit time.)