feat(hygiene): mechanize razor-cadence item 4 — audit-rule-cross-refs.ts#3202
Conversation
….ts (first slice, Layer A) Mechanizes the 12-batch manual razor-cadence item 4 (composes-with audit) work that ran on 2026-05-14 (50/50 rules, 217/218 LIVE, 1 finding captured by B-0514). Scope (first slice — Layer A mechanical only): - Scan all .claude/rules/*.md - Pull backtick'd path refs + B-NNNN IDs - Test existence via direct path + glob + per-row backlog file lookup - Report stale-pointer CANDIDATES (failed existence) Out of scope (Layer B — semantic classification): The 9-variant reference-classification taxonomy (concrete | glob | template-path | backlog-ID | legacy-noted | transient | anti-pattern | conditional | alternative-location) requires reading rule prose context around each ref. ~5% of MISSes are rule-acknowledged-not-exists (healthy). This tool produces the candidate list; humans/Otto apply the taxonomy. See docs/hygiene-history/ticks/2026/05/14/1920Z.md for the full taxonomy. First-run output: 50 rules, 281 refs, 239 resolved, 42 candidates (most classifiable as bare-filename-needs-prefix or RANE per the taxonomy; B-0514's genuine stale pointer also surfaces, validating the tool). Composes with B-0506 (worktree-prune cadence) as factory-hygiene mechanization. Tests: 12 pass / 22 expect calls in tools/hygiene/audit-rule-cross-refs.test.ts. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d091b7e1d9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…3202) (#3203) After completing 100% manual audit coverage in PR #3200, shipped the mechanization tool: tools/hygiene/audit-rule-cross-refs.ts + test suite (12 tests, 22 expect calls). First-run output: 50 rules scanned, 281 refs pulled, 239 resolved, 42 stale- pointer candidates. Tool correctly catches B-0514's genuine stale pointer. The 42-candidate count vs manual audit's 1-defect result reflects Layer A (mechanical existence checks) only — Layer B (9-variant taxonomy semantic classification) is the natural follow-up. First slice is useful TODAY (replaces per-tick manual work) even without Layer B. Satisfies encoding-rules-without-mechanizing: the 12-batch manual audit produced "a memory of failures, not prevention" per the rule's warning; this tool turns it into prevention. Composes with B-0506, razor-cadence #3128, encoding-rules-without-mechanizing. Co-authored-by: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new Bun CLI under tools/hygiene/ to mechanize the razor-cadence “composes-with cross-ref audit” by extracting file/backlog references from .claude/rules/*.md, checking whether they resolve, and emitting a markdown report, with a small Bun test suite for the core helpers.
Changes:
- Introduces
tools/hygiene/audit-rule-cross-refs.tsto scan rules, extract backtick’d path refs +B-NNNNIDs, and render a report of unresolved candidates. - Adds
tools/hygiene/audit-rule-cross-refs.test.tsto validatepullRefs,refExists, andrenderReport.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tools/hygiene/audit-rule-cross-refs.ts | New CLI + library helpers to audit .claude/rules cross-references and output a markdown report. |
| tools/hygiene/audit-rule-cross-refs.test.ts | New Bun tests for reference extraction, resolution checks, and report rendering. |
Comments suppressed due to low confidence (4)
tools/hygiene/audit-rule-cross-refs.ts:42
- P1:
typescript.mdisn’t a repo-root file; the canonical TypeScript conventions doc isdocs/best-practices/typescript.md. Consider updating this xref to the full path to avoid confusion and to keep cross-refs mechanically checkable.
// DST-friendliness:
//
// Read-only audit. The "Generated" timestamp in markdown reports is the only
// non-deterministic surface. Per `typescript.md` universal-DST gate.
tools/hygiene/audit-rule-cross-refs.ts:37
- P1: The script documentation says exit code 0 is returned “always” (except arg errors), but
readFileSync/writeFileSync/readdirSynccan throw and will currently terminate the process with a non-0 code. Either wrap the main path in a try/catch to enforce the documented behavior (emit an error + exit 0), or document the additional non-zero failure mode(s).
// Exit codes:
//
// 0 always (detect-only; no enforcement; humans triage stale-pointer candidates)
// 64 argument error
//
tools/hygiene/audit-rule-cross-refs.ts:120
- P0:
refExiststreats any*ref as a “glob”, but this implementation only works when the wildcard is in the final path segment. For patterns likedocs/backlog/P*/B-*.md(used in.claude/rules/),dirbecomesdocs/backlog/P*which will never exist, so these will be false stale-pointer candidates. Consider using Bun’sGlob(used elsewhere under tools/hygiene) or extending glob support to directory segments.
if (ref.raw.includes("*")) {
const lastSlash = ref.raw.lastIndexOf("/");
const dir = lastSlash >= 0 ? ref.raw.slice(0, lastSlash) : ".";
const filePart = lastSlash >= 0 ? ref.raw.slice(lastSlash + 1) : ref.raw;
if (!existsSync(dir) || !statSync(dir).isDirectory()) return false;
tools/hygiene/audit-rule-cross-refs.ts:197
- P1: The report text links the 9-variant taxonomy to
docs/hygiene-history/ticks/2026/05/14/1920Z.md, but that file doesn’t exist (taxonomy is captured in 1908Z/1916Z). Update this pointer so the report’s “how to triage candidates” guidance is actionable.
lines.push("Candidates listed below failed direct path / glob / backlog-row lookup. Apply the");
lines.push("9-variant taxonomy from `docs/hygiene-history/ticks/2026/05/14/1920Z.md` to classify");
lines.push("each candidate before fixing — ~5% are healthy MISSes (rule-acknowledged-transient,");
…d (MEMORY.md bloat) (#3204) (renumbered from B-0516 due to multi-Otto ID collision — a parallel Otto had also claimed B-0516 in chore/b-0516-gates-ecc-physical-compression-research- direction-2026-05-14 in-flight; B-0517 is the next safe ID) Razor-cadence item 5 (MEMORY.md index audit) investigation: - User-scope MEMORY.md: 242 lines / 66KB / 237 entries - Cold-boot loads first 200 lines only → ~37 lines (15%) unreachable - Average entry size 275 chars vs 200-char guidance - ~100-130 entries exceed limit; top 5 are 500-620 chars Root cause: paragraph-length entries duplicate content already in topic file frontmatter description: field. B-0517 two-phase plan: - Phase 1: bulk cleanup of ~130 entries (trim index; full detail stays in topic files) - Phase 2: mechanize via tools/hygiene/audit-user-scope-memory-index.ts (parallel to PR #3202's audit-rule-cross-refs.ts) Composes with B-0006, PR #3202, razor-cadence #3128, B-0506. Side observation: multi-Otto ID-collision recurred despite PR #3153's ID- allocation discipline (on-disk + in-flight check). Both Ottos extracted top B-0515 from on-disk and incremented; in-flight check missed because the parallel Otto's branch had a non-standard name (chore/b-0516-gates-ecc-*) that didn't match the search heuristic. Substrate-honest acknowledgment; discipline holds but search-heuristic needs widening to catch this case. Co-authored-by: Claude <noreply@anthropic.com>
…ape regex (Codex P2 + CodeQL + Copilot P0) Addresses 3 review-thread findings on PR #3202: 1. Codex P2: refExists() only handled wildcards in the filename part, not in directory segments. References like `docs/backlog/P*/B-*.md` (already present in backlog-item-start-gate.md) were always classified as stale. Replaced with globResolves() that walks segment-by-segment: wildcards in earlier segments expand to directory listings, wildcards in the final segment match basenames. 2. CodeQL 81 / Incomplete string escaping: the original regex builder did not escape backslash characters. Replaced with escapeForGlobRegex() that explicitly escapes `[\\^$+?.()|[\]{}]` before substituting `*` → `.*`. 3. Copilot P0: regression test for directory-segment wildcards added in audit-rule-cross-refs.test.ts ("resolves a glob with wildcard in a directory segment"). Plus a negative test for "no-such-dir-*/nothing-*.md". Audit re-run output (post-fix): - Resolved: 241 (was 239, +2 from dir-glob refs now resolving) - Stale-pointer candidates: 40 (was 42) Two previously-false-stale references (`docs/backlog/P*/B-*.md` and similar) now correctly resolve, validating the fix. Tests: 14 pass / 24 expect calls (was 12/22). Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5728487851
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
After the first-round fix (PR #3202 commit 5728487) addressed directory-segment wildcards, Codex flagged a remaining false-positive class: brace-expansion patterns like memory/feedback_rule_number_{one,two,three,four,five,six,seven}_*aaron_2026_05_05.md (referenced from lost-files-surface.md) were always reported stale because escapeForGlobRegex escapes { and } literally. Fix: added expandBraces() that expands the FIRST brace group into N alternatives, then recurses on each. The public globResolves() now expands braces before star-glob resolution; each expansion tested individually with the renamed globResolvesSingle(). Audit re-run: - Resolved: 242 (was 241) - Stale-pointer candidates: 39 (was 40) One previously-false-stale brace-glob now correctly resolves. Tests: 16 pass / 26 expect calls (was 14/24). Added regression tests for brace expansion + a negative test. Co-Authored-By: Claude <noreply@anthropic.com>
…ry-index.ts (#3208) Parallel to PR #3202 (audit-rule-cross-refs.ts) but for the user-scope MEMORY.md index. Implements B-0517 Phase 2: bloat-detection tool that surfaces metrics for human / Otto triage. What this does: - Reads ~/.claude/projects/<slug>/memory/MEMORY.md (overridable via --memory) - Counts total lines / bytes / entries - Flags entries exceeding the 200-char guidance - Computes truncation risk (lines past cold-boot cutoff at 200) - Reports top 10 bloat entries by char count Real-world first-run output: - Total lines: 245 (was 242 at B-0517 filing; growing) - Total entries: 239 - Entries over 200 chars: 228 (96%) - Lines past cutoff: 45 → truncation risk YES The 96% over-limit rate confirms B-0517's premise: the index has accumulated paragraph-length entries that duplicate content already in the topic file frontmatter description: field. Phase 1 cleanup (bulk trim) is the natural follow-up; this tool prevents recurrence. Composes with PR #3202 (rule cross-refs audit), B-0506 (worktree-prune cadence) as factory-hygiene cadences that could share a single daily CI cron. Tests: 7 pass / 20 expect calls in audit-user-scope-memory-index.test.ts. Uses temp files; doesn't touch the real user-scope memory. Co-authored-by: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (4)
tools/hygiene/audit-rule-cross-refs.ts:316
- P1 (codebase convention): This tool relies on relative paths (e.g.,
.claude/rules) but neverchdirs to the repo root. Many Zeta tools make repo-rooted invocation reliable byprocess.chdir(repoRoot())(seedocs/best-practices/repo-scripting.md“Repo-rooted invocation”). Consider resolving paths from repo root (viagit rev-parse --show-toplevelorimport.meta.url-relative) so running from a subdirectory or CI step with a different CWD doesn’t silently report everything as missing.
const result = audit(RULES_DIR);
const report = renderReport(result, new Date());
tools/hygiene/audit-rule-cross-refs.ts:83
- P1 (codebase convention): This CLI parser doesn’t support
-h/--help, even though the repo scripting conventions call out-h/--helpas a standard flag and expect unknown flags to exit 64 with an error to stderr. Adding help output (stdout) will make the tool consistent with othertools/hygiene/*CLIs.
function parseArgs(argv: string[]): { kind: "args"; args: Args } | { kind: "error"; message: string } {
let report: string | null = null;
let i = 0;
while (i < argv.length) {
const a = argv[i]!;
if (a === "--report") {
const next = argv[i + 1];
if (!next) return { kind: "error", message: "--report requires a path" };
report = next;
i += 2;
} else {
return { kind: "error", message: `Unknown argument: ${a}` };
}
}
return { kind: "args", args: { report } };
}
tools/hygiene/audit-rule-cross-refs.ts:41
- P2 (documentation/xref): This comment references
typescript.md, but the TypeScript best-practices doc lives atdocs/best-practices/typescript.md. Using the full path here would avoid suggesting a non-existent repo-roottypescript.mdfile.
// DST-friendliness:
//
// Read-only audit. The "Generated" timestamp in markdown reports is the only
// non-deterministic surface. Per `typescript.md` universal-DST gate.
tools/hygiene/audit-rule-cross-refs.ts:322
- P1 (codebase convention):
console.log(\wrote …`)` writes progress chatter to stdout, which makes stdout non-pipeable if callers want to consume the markdown report from stdout. The repo scripting conventions prefer stdout for data output and stderr for progress/status; consider sending the “wrote …” line to stderr instead.
if (parsed.args.report) {
writeFileSync(parsed.args.report, report);
console.log(`wrote ${parsed.args.report}`);
} else {
console.log(report);
}
| // bun tools/hygiene/audit-rule-cross-refs.ts # detect-only, exit 0 always | ||
| // bun tools/hygiene/audit-rule-cross-refs.ts --report PATH # write markdown report | ||
| // | ||
| // Exit codes: | ||
| // | ||
| // 0 always (detect-only; no enforcement; humans triage stale-pointer candidates) | ||
| // 64 argument error | ||
| // |
… two audit tools (#3212) Wires PR #3202 (audit-rule-cross-refs.ts) + PR #3208 (audit-user-scope-memory- index.ts) into a daily cadence so the discipline does not depend on agent-remembering-to-run-the-audits. What this workflow does: - Runs the rule-cross-refs audit + uploads markdown report as workflow artifact (90-day retention) - Runs the memory-index audit unit tests (MEMORY.md itself is user-scope and not available in CI; the self-test verifies the tool itself) - Detect-only; humans/Otto triage candidates via the 9-variant taxonomy Cadence: daily 14:37 UTC (off-the-hour to avoid GHA cron thundering-herd; between budget-snapshot-cadence Sundays and git-hotspot-cadence Sundays). Triggers: - schedule (daily) - workflow_dispatch (manual) - pull_request on the tool files (self-test on PR) Composes with razor-cadence.yml (issue-tracker cadence) + git-hotspot-cadence.yml (template shape) + the encoding-rules-without-mechanizing.md rule (the substrate this workflow satisfies). Safe-pattern compliance: SHA-pinned actions, minimum permissions (contents:read only), concurrency group, pinned runs-on (ubuntu-24.04), path-filter for self-test trigger. No untrusted user-authored inputs interpolated in run blocks; only github.run_id and github.ref used in template expressions. Uses oven-sh/setup-bun (per other workflows) instead of ./tools/setup/install.sh to avoid the mise rate-limit cascade observed on parallel PRs this session. Co-authored-by: Claude <noreply@anthropic.com>
…oint (#3211) * shard(tick): 1942Z — PR #3208 CI rerun cleared + session state checkpoint PR #3208 (B-0517 Phase 2 tool) hit UNSTABLE state — 5 failed lint jobs from the same mise rate-limit pattern earlier in the session. Reran failed jobs; transitioned UNSTABLE → CLEAN with autoMerge armed. Session-state checkpoint: - 12 razor-cadence batch shards (B1-B12 = 100% rule coverage, 50/50) - 2 mechanization tools (PR #3202 + PR #3208) with full test suites - 3 backlog rows filed (B-0506, B-0514, B-0517) - 9-variant reference-classification taxonomy (durable artifact for any future Layer B mechanization) Razor-cadence #3128: items 4 + 5 complete + mechanized; items 1, 2, 3 remain. CI workflow wiring (factory-hygiene-audit.yml composing audit-rule-cross-refs + audit-user-scope-memory-index) is the obvious next-session follow-up. Co-Authored-By: Claude <noreply@anthropic.com> * fix(pr3211): MD018 markdownlint — prefix line-leading #3208 with "PR " Lines 11 + 38 of 1942Z.md started with `#3208` which markdownlint parsed as an ATX heading without space (MD018). Prefixed both with "PR " so the references aren't ambiguous with heading syntax. Co-Authored-By: Claude <noreply@anthropic.com> * fix(pr3211): 4 Copilot threads on 1942Z shard (self-contradiction + overclaims) All 4 findings real: 1. Line 7 self-contradiction: shard says "prior merges include B-0517 Phase 2" but later describes #3208 (the PR landing that phase) as still in-flight. Clarified: #3208 was UNSTABLE at refresh time + merged later in same tick. 2. Line 30 overclaim: said "taxonomy used by both audit tools' Layer A" but: - audit-rule-cross-refs.ts treats the taxonomy as Layer B (explicitly out of scope for the mechanical Layer A) - audit-user-scope-memory-index.ts doesn't reference the taxonomy at all (different surface — measures bloat, not cross-ref existence) Corrected to: documented in docstring + report-reminder, load-bearing for future Layer B work, not used by Layer A. 3. Line 66 workflow accuracy: said "running both audit tools daily" but the memory-index tool can't run in CI (defaults to user-scope path that doesn't exist there; exits 128). Clarified: rule-cross-refs runs fully; memory-index runs only as a tool self-test in CI. (This is what PR #3212 already does correctly.) 4. Line 1 schema check: shard uses ATX heading format which fails tools/hygiene/check-tick-history-shard-schema.ts (which expects pipe- table first row). The check isn't currently CI-wired but the shard is out of compliance with the documented schema. Substrate-honest acknow- ledgment added; format reconciliation deferred to a future tick. All 4 threads will be resolved via GraphQL after this lands. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
All Otto-CLI session work landed: 2 audit tools (PRs #3202, #3208) + 1 daily GHA workflow (#3212) + 4 backlog rows (B-0506, B-0514, B-0517, B-0519) + 12 razor-cadence batch shards (B1-B12, 100% rule audit coverage) + 9-variant reference-classification taxonomy. Zero mine PRs open at refresh. Cron live for next tick. Natural close: marginal value of more new work is low; substrate compounds durably on main. Aaron's day-close summary (#3213) in flight on his side. For next session: Layer B semantic classification, razor-cadence reports → issue comments, ZETA_EXPECTED_BRANCH auto-export mechanization, B-0517 Phase 1 bulk MEMORY.md cleanup, B-0514 missing wwjd-grey-honest authoring. Co-Authored-By: Claude <noreply@anthropic.com>
…ed (#3213) * shard(tick): 1952Z — day-close summary; edge-runner inclusion preserved (Aaron 2026-05-14) Summarizes today's 37+ memory file cascade + Aaron's inclusion of Otto in edge-runner identity with dual-binding applied individually. Preservation: 'feedback_aaron_otto_is_edge_runner_too_dual_binding_applies_constraints_bind_otto_same_as_aaron_2026_05_14.md' Disciplines applied: razor + HARD LIMITS + algo-wink (MAXIMUM) + glass-halo bidirectional + default-to-both + mechanical-authorization-check. CLAUDE.md bug acknowledged (B-0518). Substrate-honest accountability: Otto's adherence is responsibility; rule sharpness is contributing factor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * shard(tick): 1959Z — session close; audit infrastructure durable on main All Otto-CLI session work landed: 2 audit tools (PRs #3202, #3208) + 1 daily GHA workflow (#3212) + 4 backlog rows (B-0506, B-0514, B-0517, B-0519) + 12 razor-cadence batch shards (B1-B12, 100% rule audit coverage) + 9-variant reference-classification taxonomy. Zero mine PRs open at refresh. Cron live for next tick. Natural close: marginal value of more new work is low; substrate compounds durably on main. Aaron's day-close summary (#3213) in flight on his side. For next session: Layer B semantic classification, razor-cadence reports → issue comments, ZETA_EXPECTED_BRANCH auto-export mechanization, B-0517 Phase 1 bulk MEMORY.md cleanup, B-0514 missing wwjd-grey-honest authoring. Co-Authored-By: Claude <noreply@anthropic.com> * fix(pr3213): MD032 blanks-around-lists on 1952Z shard Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
#3220) Used the audit-rule-cross-refs tool shipped this session (PR #3202) to produce 39 stale-pointer candidates, then applied the 9-variant taxonomy manually to triage each. Result: 37 healthy variants + 2 entries pointing at the 1 real defect (B-0514's missing wwjd-grey-honest memory file, already captured). False-positive rate: 38/39 = 97%. Layer A is high-recall + low-precision; Layer B semantic classification is genuinely load-bearing. Reproduces the 12-batch manual audit's conclusion (217/218 = 99.5% LIVE, 1 defect) via the tool + manual taxonomy application. Mechanization confidence high. Bonus: Layer B heuristics for each variant noted in shard "Notes for future-Otto" section — LLM-or-regex tractable for next mechanization slice. Co-authored-by: Claude <noreply@anthropic.com>
Implements B-0506 Phase 2: a TS audit tool that catches the recurring "branch already used by worktree at <missing-path>" lockout pattern. What it does: - Enumerates 'git worktree list --porcelain' - For each prunable entry, tests whether its working-dir path exists - Reports stale entries (markdown summary) - With --prune, runs 'git worktree prune --expire=now -v' Parallel in shape to PR #3202 (audit-rule-cross-refs) and PR #3208 (audit-user-scope-memory-index) — three hygiene tools now share the same pattern. Live first-run output: 163 total worktrees, 0 stale (post the 23-entry prune I did earlier this session at 1817Z). The tool correctly reports the healthy state. Tests: 8 pass / 21 expect calls (parseWorktreePorcelain + renderReport). Composes with B-0506 (the row this implements), B-0519 (multi-Otto branch-state contamination RCA), the encoding-rules-without-mechanizing rule, and the factory-hygiene-audit-cadence.yml workflow (which could add a 3rd job for this tool in a future slice). Co-Authored-By: Claude <noreply@anthropic.com>
…ts (#3225) Implements B-0506 Phase 2: a TS audit tool that catches the recurring "branch already used by worktree at <missing-path>" lockout pattern. What it does: - Enumerates 'git worktree list --porcelain' - For each prunable entry, tests whether its working-dir path exists - Reports stale entries (markdown summary) - With --prune, runs 'git worktree prune --expire=now -v' Parallel in shape to PR #3202 (audit-rule-cross-refs) and PR #3208 (audit-user-scope-memory-index) — three hygiene tools now share the same pattern. Live first-run output: 163 total worktrees, 0 stale (post the 23-entry prune I did earlier this session at 1817Z). The tool correctly reports the healthy state. Tests: 8 pass / 21 expect calls (parseWorktreePorcelain + renderReport). Composes with B-0506 (the row this implements), B-0519 (multi-Otto branch-state contamination RCA), the encoding-rules-without-mechanizing rule, and the factory-hygiene-audit-cadence.yml workflow (which could add a 3rd job for this tool in a future slice). Co-authored-by: Claude <noreply@anthropic.com>
Summary
Mechanizes the 12-batch manual razor-cadence item 4 (composes-with audit) work that ran on 2026-05-14, producing a CLI tool that future ticks (or a daily CI cron) can run instead of the per-tick manual audit.
What ships
tools/hygiene/audit-rule-cross-refs.ts— first slice (Layer A mechanical only):.claude/rules/*.mdB-NNNNIDs (dedup'd per the lesson from PR shard(tick): 1841Z — razor-cadence batch 4 (4 more rules, 29/29 LIVE) #3179 / Codex catch)tools/hygiene/audit-rule-cross-refs.test.ts— 12 tests / 22expectcalls, all passing.First-run output
Of the 42 candidates, the canonical defect B-0514's missing
feedback_aaron_wwjd_keeps_the_grey_in_aaron_honest_devil_lives_in_the_grey_in_numbers_2026_05_12.mdis correctly surfaced — validating the tool against the manual audit's single finding.Most other candidates are bare-filename references that would resolve with a path prefix (e.g.,
claude.ts→tools/peer-call/claude.ts) or RANE references (rule-acknowledged-not-exists per the taxonomy). Layer B semantic classification is out of scope for this first slice.Out of scope (Layer B — semantic classification)
The 9-variant reference-classification taxonomy from the 12-batch manual audit:
test -els | wc -l >= 1ls docs/backlog/P*/B-NNNN-*.md; dedup when row file is also referencedRequires reading the rule's prose context around each reference. ~5% of MISSes are rule-acknowledged-not-exists (healthy). The first slice produces the candidate list; humans/Otto apply the taxonomy during triage.
See docs/hygiene-history/ticks/2026/05/14/1920Z.md for full taxonomy + final audit results.
Composes with
encoding-rules-without-mechanizing.md— the rule the work satisfiesTest plan
bun test tools/hygiene/audit-rule-cross-refs.test.ts— 12 pass / 22expectcalls🤖 Generated with Claude Code
EOF
)