Skip to content

Commit bb26f2d

Browse files
committed
Tooling: Add claude-md-length check to keep auto-injected CLAUDE.md docs lean
The CLAUDE.md/DETAILS.md split moved depth into the pull tier, but nothing stopped the push-tier `CLAUDE.md` files (auto-injected into agent context, so every word costs tokens in every session that touches the area) from regrowing. This warn-only check guards the ~600-word budget. - New `claude-md-length` check (warn-only, never fails the suite, `IsFast`, `NotInCI`): warns when any `CLAUDE.md` exceeds 600 words. `DETAILS.md` is the deliberately-unlimited pull tier and is NOT scanned. Reuses `findClaudeMdFiles` and `strings.Fields` (matches `wc -w`). - `claude-md-length-allowlist.json` seeded with the 66 `CLAUDE.md` files currently over 600 words at their current counts. Mirrors file-length's allowlist semantics exactly: suppress up to recorded count + 10% buffer, local-run shrink-wrap (drop dead/under-threshold, ratchet >10% slack down), CI report-only. Adding/raising an entry needs explicit consent; the fix for an oversized doc is to move depth into `DETAILS.md`. - Go tests cover word counting, detection, DETAILS.md exclusion, allowlist suppress/buffer/exceed, and every shrink-wrap verdict (dead/under-threshold/ratchet/small-slack/CI-report-only). - Wired per the registry↔CI contract via a `NotInCI` reason (warn-only metrics carry no workflow step), so `ci-coverage` stays green. Extended `.claude/rules/file-length-allowlist.md` and the check-authoring docs; AGENTS.md now notes the budget is watched.
1 parent f89dab0 commit bb26f2d

9 files changed

Lines changed: 598 additions & 8 deletions

File tree

.claude/rules/file-length-allowlist.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,9 @@ The `exempt` section is for generated files whose length is not actionable (for
1414
reason and the same consent rule applies to adding one.
1515

1616
The file-length check is warn-only — it doesn't fail the suite — so leaving the warning is always safe.
17+
18+
The same contract applies to `scripts/check/checks/claude-md-length-allowlist.json` (the `claude-md-length` check, which
19+
caps push-tier CLAUDE.md word counts): the check shrink-wraps stale `files` entries on local runs (remove gone /
20+
under-threshold, ratchet >10% slack down), and adding or raising an entry needs explicit user consent. If a CLAUDE.md
21+
you're touching exceeds its allowlisted word count, move depth into the colocated `DETAILS.md` rather than bumping the
22+
number; leaving the warn is safe (it's warn-only too).

AGENTS.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,11 @@ Core structure:
100100
directory are read, so every word costs tokens in every session that touches the area. `DETAILS.md` is the pull tier:
101101
the area's real docs, read on demand. The litmus: could an agent editing a random file here silently break something
102102
without this line? Then it's `CLAUDE.md`. Everything else is `DETAILS.md`. So `CLAUDE.md` = invariants, gotchas,
103-
don't-do-X-because-Y, a 2–3 line module map, and a pointer; target ~400–600 words. `DETAILS.md` = architecture
104-
narrative, data flows, decision rationale with depth, edge-case catalogs. Read it in whole before structural changes
105-
in an area. Never `@`-import `DETAILS.md` from a `CLAUDE.md` (that would rebuild the auto-load cost the split exists
106-
to remove). See `docs/architecture.md` for the full map.
103+
don't-do-X-because-Y, a 2–3 line module map, and a pointer; target ~400–600 words, watched by the warn-only
104+
`claude-md-length` check (its allowlist tracks files already over 600 words). `DETAILS.md` = architecture narrative,
105+
data flows, decision rationale with depth, edge-case catalogs. Read it in whole before structural changes in an area.
106+
Never `@`-import `DETAILS.md` from a `CLAUDE.md` (that would rebuild the auto-load cost the split exists to remove).
107+
See `docs/architecture.md` for the full map.
107108

108109
## Testing and checking
109110

@@ -141,7 +142,7 @@ always runs fresh. See the input-fingerprint cache section in `scripts/check/CLA
141142
- Go: `go-vet`, `staticcheck`, `ineffassign`, `misspell`, `gocyclo`, `go-tests`.
142143
- API server: `typecheck`, `tests`.
143144
- Website: `html-validate`, `bundle-size` (both self-skip when `dist/` is absent).
144-
- Warn-only metrics: `file-length`, `claude-md-reminder`, `changelog-links`.
145+
- Warn-only metrics: `file-length`, `claude-md-reminder`, `claude-md-length`, `changelog-links`.
145146
- **Does NOT cover**: `clippy`, Rust tests, `cargo-audit`, `cargo-deny`, `jscpd`, `bindings-fresh`, desktop ESLint /
146147
`svelte-check` / Svelte tests, website ESLint / typecheck / build / e2e, `docker-build`, or any E2E suite.
147148
- **`pnpm check` — before every commit.** The full default suite (everything not marked `IsSlow`). Catches what `--fast`

scripts/check/checks/CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ see [`../CLAUDE.md`](../CLAUDE.md).
1616
`scripts-go-*`).
1717
- `inputs.go`: shared `Inputs` building blocks. `allowlist.go` / `directives.go`: allowlist shrink-wrap + opt-out
1818
tracking plumbing.
19-
- Warn-only scanners with their JSON allowlists: `file-length.go`, `e2e-durations.go`, `website-bundle-size.go`.
19+
- Warn-only scanners with their JSON allowlists: `file-length.go`, `claude-md-length.go`, `e2e-durations.go`,
20+
`website-bundle-size.go`.
2021

2122
## Must-knows
2223

scripts/check/checks/DETAILS.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ freestyle.sh remote execution), see [`../CLAUDE.md`](../CLAUDE.md).
1616
| `website-*.go`, `api-server-*.go`, `scripts-go-*.go` | One file per check |
1717
| `file-length.go` | Informational file-length scanner (warn-only, never fails). Supports an allowlist and shrink-wraps it on local runs. |
1818
| `file-length-allowlist.json` | Allowlist for file-length check: `{ "exempt": { "path": reason }, "files": { "path": lineCount } }`. See § File-length allowlist. |
19+
| `claude-md-length.go` | Warn-only push-tier scanner: warns when a `CLAUDE.md` exceeds 600 words (`DETAILS.md` is the unlimited pull tier, not scanned). Allowlist with the same shrink-wrap semantics as file-length. See § CLAUDE.md length. |
20+
| `claude-md-length-allowlist.json` | Allowlist for claude-md-length: `{ "files": { "path": wordCount } }`. Same ratchet/consent rules as file-length. |
1921
| `e2e-durations.go` | E2E test duration flagger (warn-only): parses the Playwright JSON reports after each E2E run and flags tests over the 2 s budget. Embedded in both E2E checks, not a registry check. See § E2E test duration flagger. |
2022
| `e2e-duration-allowlist.json` | Per-platform (`macos` / `linux`) allowlist for the duration flagger: `{ "<spec>::<describe chain>::<title>": reason }`. Entries need a reason; new entries need David's OK. |
2123
| `website-bundle-size.go` | Warn-only website `dist/` size budget: warns when the total grows >10% over the committed baseline. Self-skips without `dist/`. See § Website bundle-size baseline. |
@@ -169,6 +171,20 @@ mirrors the growth buffer so routine small edits don't churn the JSON.
169171
See `.claude/rules/file-length-allowlist.md` (repo-level) for when an entry may be raised vs lowered without user
170172
consent.
171173

174+
## CLAUDE.md length
175+
176+
`claude-md-length` (warn-only, `IsFast`) keeps the push tier lean: it warns when any `CLAUDE.md` exceeds 600 words. Each
177+
`CLAUDE.md` is auto-injected into every agent session that touches its directory, so words there cost tokens repeatedly;
178+
depth belongs in the colocated `DETAILS.md` (the pull tier), which is deliberately unlimited and NOT scanned. The check
179+
reuses `findClaudeMdFiles` (same walk as `claude-md-reminder`, so only files named `CLAUDE.md` count) and
180+
`strings.Fields` for the word count (matches `wc -w`, so seeded counts and the check agree).
181+
182+
`claude-md-length-allowlist.json` has one `files` section mapping path → accepted word count, with the exact same
183+
shrink-wrap and consent discipline as file-length: a file is suppressed up to its recorded count plus a 10% buffer;
184+
local runs drop dead/under-threshold entries and ratchet >10%-slack entries down; CI reports what a local run would
185+
change. Adding or raising an entry needs David's OK (`.claude/rules/file-length-allowlist.md`); the fix for an oversized
186+
`CLAUDE.md` is to move depth into `DETAILS.md`, not bump the number.
187+
172188
## E2E test duration flagger
173189

174190
The E2E suites were hard-won down to under 2 s per test; `e2e-durations.go` defends that. After a successful E2E run,
@@ -245,7 +261,7 @@ RUSTSEC ignores — that's a quarterly task in `docs/maintenance.md`.
245261
| Website | Docker | docker-build |
246262
| API server | TS | oxfmt, eslint, typecheck, tests |
247263
| Scripts | Go | gofmt, go-vet, staticcheck, ineffassign, misspell, gocyclo, nilaway, deadcode, go-tests, govulncheck |
248-
| Other | Metrics | file-length (warn-only), CLAUDE.md-reminder (warn-only), changelog-commit-links, workflows-rustup (forbids `rustup target/component add` in workflows), ci-coverage (registry-to-workflows contract) |
264+
| Other | Metrics | file-length (warn-only), CLAUDE.md-reminder (warn-only), claude-md-length (warn-only), changelog-commit-links, workflows-rustup (forbids `rustup target/component add` in workflows), ci-coverage (registry-to-workflows contract) |
249265
| Other | Security | workflows-hardening (SHA-pinning, no `pull_request_target`, job-scoped `id-token: write`) |
250266

251267
## Key decisions
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"$comment": "Files under `files` are exempt from the claude-md-length warning up to their recorded word count (plus a 10% growth buffer). The push-tier CLAUDE.md is auto-injected into agent context, so it has a ~600-word budget; deeper docs belong in the pull-tier DETAILS.md, which is unlimited and not checked. The check shrink-wraps stale entries on local runs: dead entries and files back under the threshold are removed, and entries with more than 10% slack are ratcheted down to the file's current count. See `.claude/rules/file-length-allowlist.md`.",
3+
"files": {
4+
"apps/analytics-dashboard/CLAUDE.md": 1059,
5+
"apps/desktop/scripts/CLAUDE.md": 703,
6+
"apps/desktop/src-tauri/capabilities/CLAUDE.md": 1136,
7+
"apps/desktop/src-tauri/src/analytics/CLAUDE.md": 1518,
8+
"apps/desktop/src-tauri/src/commands/CLAUDE.md": 2229,
9+
"apps/desktop/src-tauri/src/crash_reporter/CLAUDE.md": 950,
10+
"apps/desktop/src-tauri/src/downloads/CLAUDE.md": 929,
11+
"apps/desktop/src-tauri/src/file_system/listing/CLAUDE.md": 2395,
12+
"apps/desktop/src-tauri/src/file_system/volume/friendly_error/CLAUDE.md": 1409,
13+
"apps/desktop/src-tauri/src/file_system/write_operations/delete/CLAUDE.md": 1231,
14+
"apps/desktop/src-tauri/src/file_system/write_operations/transfer/CLAUDE.md": 602,
15+
"apps/desktop/src-tauri/src/font_metrics/CLAUDE.md": 756,
16+
"apps/desktop/src-tauri/src/go_to_path/CLAUDE.md": 676,
17+
"apps/desktop/src-tauri/src/icons/CLAUDE.md": 1322,
18+
"apps/desktop/src-tauri/src/indexing/CLAUDE.md": 681,
19+
"apps/desktop/src-tauri/src/licensing/CLAUDE.md": 1584,
20+
"apps/desktop/src-tauri/src/logging/CLAUDE.md": 1234,
21+
"apps/desktop/src-tauri/src/mcp/executor/CLAUDE.md": 1352,
22+
"apps/desktop/src-tauri/src/mtp/CLAUDE.md": 1221,
23+
"apps/desktop/src-tauri/src/mtp/connection/CLAUDE.md": 683,
24+
"apps/desktop/src-tauri/src/native_drag/CLAUDE.md": 1654,
25+
"apps/desktop/src-tauri/src/network/CLAUDE.md": 663,
26+
"apps/desktop/src-tauri/src/quick_look/CLAUDE.md": 1218,
27+
"apps/desktop/src-tauri/src/redact/CLAUDE.md": 1023,
28+
"apps/desktop/src-tauri/src/search/CLAUDE.md": 1115,
29+
"apps/desktop/src-tauri/src/search/ai/CLAUDE.md": 733,
30+
"apps/desktop/src-tauri/src/secrets/CLAUDE.md": 752,
31+
"apps/desktop/src-tauri/src/selection/CLAUDE.md": 785,
32+
"apps/desktop/src-tauri/src/selection/ai/CLAUDE.md": 770,
33+
"apps/desktop/src-tauri/src/settings/CLAUDE.md": 835,
34+
"apps/desktop/src-tauri/src/updater/CLAUDE.md": 811,
35+
"apps/desktop/src-tauri/src/volumes/CLAUDE.md": 2101,
36+
"apps/desktop/src-tauri/src/volumes_linux/CLAUDE.md": 807,
37+
"apps/desktop/src/lib/ai/CLAUDE.md": 959,
38+
"apps/desktop/src/lib/command-palette/CLAUDE.md": 1486,
39+
"apps/desktop/src/lib/commands/CLAUDE.md": 2355,
40+
"apps/desktop/src/lib/downloads/CLAUDE.md": 2165,
41+
"apps/desktop/src/lib/error-reporter/CLAUDE.md": 964,
42+
"apps/desktop/src/lib/file-explorer/git/CLAUDE.md": 855,
43+
"apps/desktop/src/lib/file-explorer/network/CLAUDE.md": 2421,
44+
"apps/desktop/src/lib/file-explorer/pane/CLAUDE.md": 753,
45+
"apps/desktop/src/lib/file-explorer/rename/CLAUDE.md": 992,
46+
"apps/desktop/src/lib/file-explorer/selection/CLAUDE.md": 1634,
47+
"apps/desktop/src/lib/file-explorer/tabs/CLAUDE.md": 1405,
48+
"apps/desktop/src/lib/file-operations/delete/CLAUDE.md": 807,
49+
"apps/desktop/src/lib/file-viewer/CLAUDE.md": 1129,
50+
"apps/desktop/src/lib/font-metrics/CLAUDE.md": 649,
51+
"apps/desktop/src/lib/go-to-path/CLAUDE.md": 1223,
52+
"apps/desktop/src/lib/indexing/CLAUDE.md": 1535,
53+
"apps/desktop/src/lib/licensing/CLAUDE.md": 1063,
54+
"apps/desktop/src/lib/logging/CLAUDE.md": 707,
55+
"apps/desktop/src/lib/query-ui/filter-chips/CLAUDE.md": 1586,
56+
"apps/desktop/src/lib/selection-dialog/CLAUDE.md": 1306,
57+
"apps/desktop/src/lib/settings/components/CLAUDE.md": 1103,
58+
"apps/desktop/src/lib/tauri-commands/CLAUDE.md": 889,
59+
"apps/desktop/src/lib/ui/CLAUDE.md": 654,
60+
"apps/desktop/src/lib/updates/CLAUDE.md": 1391,
61+
"apps/desktop/src/lib/utils/CLAUDE.md": 1147,
62+
"apps/desktop/src/routes/(main)/CLAUDE.md": 1736,
63+
"apps/desktop/src/routes/viewer/CLAUDE.md": 2461,
64+
"apps/desktop/test/CLAUDE.md": 723,
65+
"apps/desktop/test/e2e-linux/CLAUDE.md": 2381,
66+
"apps/desktop/test/e2e-playwright/CLAUDE.md": 666,
67+
"apps/desktop/test/e2e-shared/CLAUDE.md": 777,
68+
"apps/website/CLAUDE.md": 1196,
69+
"apps/website/public/hero/CLAUDE.md": 940
70+
}
71+
}

0 commit comments

Comments
 (0)