feat(cml): context-window-scaled CLAUDE.md char budget (mirrors CC 40.0k warning)

Add a char-based CML finding that mirrors Claude Code's own startup warning
("Large CLAUDE.md will impact performance (X chars > 40.0k)"). CC 2.1.169 scales
that threshold with the model's context window, so the finding anchors on the
conservative 200k window (we cannot observe the user's window; the anchor fires
earliest) and discloses the relaxed ~200,000-char figure at 1M context. MEDIUM
severity (token cost, not an adherence cliff — consistent with the v5.2.0 reframe).

Keyed on chars, not lines, so it is complementary to the existing 200/500-line
checks (which stay untouched): a file can be long by lines yet under budget (short
lines, e.g. large-cascade at 37k chars / 1024 lines), or short by lines yet over it.

Extract the shared 200k/1M context-window constants to scanners/lib/context-window.mjs
(single source of truth; skill-listing-budget.mjs now imports + re-exports them).

40.0k figure and context-window scaling verified against the CC changelog (2.1.169,
2026-06-08) and the live startup-warning text. +6 tests, new fixture large-claude-chars
(48,531 chars / 100 lines). Suite 918/918, self-audit PASS configGrade A 97.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ter3E2JSi1Khgmuf2kady8
This commit is contained in:
Kjell Tore Guttormsen 2026-06-19 13:34:40 +02:00
commit b0bf8c5817
7 changed files with 253 additions and 8 deletions

View file

@ -109,7 +109,27 @@ Default: auto-detects scope from git context. Override with `/config-audit full|
node --test 'tests/**/*.test.mjs'
```
912 tests across 56 test files (17 lib + 29 scanner + 1 hook + 1 agent + 3 commands + 1 knowledge + 4 top-level). Test fixtures in `tests/fixtures/`. Top-level humanizer tests: `json-backcompat.test.mjs`, `raw-backcompat.test.mjs`, `scenario-read-test.test.mjs`, `snapshot-default-output.test.mjs`.
918 tests across 56 test files (17 lib + 29 scanner + 1 hook + 1 agent + 3 commands + 1 knowledge + 4 top-level). Test fixtures in `tests/fixtures/`. Top-level humanizer tests: `json-backcompat.test.mjs`, `raw-backcompat.test.mjs`, `scenario-read-test.test.mjs`, `snapshot-default-output.test.mjs`.
### CML scanner — context-window-scaled char budget
Beyond the line-count checks (200/500 lines, both MEDIUM), the CML scanner mirrors
Claude Code's own startup warning — *"Large CLAUDE.md will impact performance
(X chars > 40.0k)"* — as a `char`-based finding:
- **Char budget** — flags a CLAUDE.md over **~40.0k chars** (CC's startup-warning
figure at a 200k-context model). CC 2.1.169 scales that threshold with the model's
context window, so the finding anchors on the conservative 200k window (we cannot
observe the user's window; the anchor fires earliest) and discloses the relaxed
~200,000-char figure at 1M context. Severity MEDIUM (token cost, not an adherence
cliff). New `CA-CML` finding.
It keys on chars, not lines, so it is complementary to the line checks: a file can be
long by lines yet under budget (short lines), or short by lines yet over it (long lines).
The 200k/1M window constants live in the shared `scanners/lib/context-window.mjs`
(single source of truth, also re-exported by `skill-listing-budget.mjs`). The 40.0k
figure and context-window scaling are verified against the CC changelog (2.1.169) and
the live startup-warning text.
### DIS scanner — permission-rule hygiene

View file

@ -12,7 +12,7 @@
![Commands](https://img.shields.io/badge/commands-18-green)
![Agents](https://img.shields.io/badge/agents-6-orange)
![Hooks](https://img.shields.io/badge/hooks-4-red)
![Tests](https://img.shields.io/badge/tests-912+-brightgreen)
![Tests](https://img.shields.io/badge/tests-918+-brightgreen)
![License](https://img.shields.io/badge/license-MIT-lightgrey)
A Claude Code plugin that checks configuration health, suggests context-aware improvements, and auto-fixes issues — `CLAUDE.md`, `settings.json`, hooks, rules, MCP servers, `@imports`, and plugins. 13 deterministic scanners across 10 quality areas, context-aware feature recommendations, auto-fix with backup/rollback, a prompt-cache-aware Token Hotspots scanner with optional API-calibrated `--accurate-tokens` mode, plus cache-prefix stability, dead-tool, and cross-plugin collision detection. Zero external dependencies.
@ -299,7 +299,7 @@ By default, `/config-audit` auto-detects scope from your git context. Override w
| Scanner | Prefix | What It Catches |
|---------|--------|-----------------|
| `claude-md-linter.mjs` | CML | Oversized files, missing sections, broken @imports, duplicates, stale TODOs |
| `claude-md-linter.mjs` | CML | Oversized files (line count **plus** a context-window-scaled char budget mirroring Claude Code's ~40.0k-char startup warning), missing sections, broken @imports, duplicates, stale TODOs |
| `settings-validator.mjs` | SET | Schema violations, unknown/deprecated keys, type mismatches, permission issues |
| `hook-validator.mjs` | HKV | Invalid format, missing scripts, wrong event names, timeout risks |
| `rules-validator.mjs` | RUL | Bad glob patterns, orphaned rules, deprecated fields, unscoped rules |
@ -321,6 +321,15 @@ By default, `/config-audit` auto-detects scope from your git context. Override w
> It fires only under measured pressure, so it stays an opportunity rather than noise. Both
> scanners share one budget definition (`scanners/lib/skill-listing-budget.mjs`).
> **CLAUDE.md size — two complementary signals.** CML checks line count (200/500, for
> readability) **and** a character budget that mirrors Claude Code's own startup warning
> — *"Large CLAUDE.md will impact performance (X chars > 40.0k)."* CC 2.1.169 scales that
> threshold with the model's context window, so the char finding anchors on a conservative
> 200k window and discloses the relaxed ~200,000-char figure at 1M context. A file can be
> long by lines yet under the char budget (short lines), or short by lines yet over it — so
> both signals earn their place. The 200k/1M window constants live in the shared
> `scanners/lib/context-window.mjs` (single source of truth with the skill-listing budget).
### CLI Tools
All tools work standalone — no Claude Code session needed:

View file

@ -9,11 +9,21 @@ import { finding, scannerResult, resetCounter } from './lib/output.mjs';
import { SEVERITY } from './lib/severity.mjs';
import { parseFrontmatter, extractSections, findImports } from './lib/yaml-parser.mjs';
import { lineCount, truncate } from './lib/string-utils.mjs';
import { LARGE_CONTEXT_WINDOW, LARGE_CONTEXT_SCALE, withCommas } from './lib/context-window.mjs';
const SCANNER = 'CML';
const MAX_RECOMMENDED_LINES = 200;
const MAX_ABSOLUTE_LINES = 500;
// Claude Code's own startup warning ("Large CLAUDE.md will impact performance
// (X chars > 40.0k)") fires once a CLAUDE.md passes ~40.0k chars on a
// 200k-context model. CC 2.1.169 made that threshold scale with the model's
// context window. We mirror it in the same unit CC uses (chars, not lines):
// anchor on the conservative 200k window (we cannot observe the user's window,
// and the anchor fires earliest) and disclose the relaxed 1M figure.
const CLAUDE_MD_CHAR_WARN_ANCHOR = 40_000; // chars @ 200k context (CC startup warning)
const CLAUDE_MD_CHAR_WARN_LARGE = CLAUDE_MD_CHAR_WARN_ANCHOR * LARGE_CONTEXT_SCALE; // 200,000 @ 1M
/** Recommended sections for a project CLAUDE.md */
const RECOMMENDED_SECTIONS = [
{ pattern: /project|overview|description|what/i, label: 'Project overview' },
@ -87,6 +97,26 @@ export async function scan(targetPath, discovery) {
}));
}
// --- Char budget (mirrors Claude Code's own startup warning) ---
// Keyed on chars, not lines: CC's "Large CLAUDE.md will impact performance"
// warning is char-based (~40.0k @ 200k context) and CC 2.1.169 scales that
// threshold with the context window. A file can be long by lines yet under
// this budget (short lines), or short by lines yet over it (long lines), so
// this is complementary to the line-count checks above.
const chars = content.length;
if (chars > CLAUDE_MD_CHAR_WARN_ANCHOR) {
findings.push(finding({
scanner: SCANNER,
severity: SEVERITY.medium,
title: 'CLAUDE.md exceeds Claude Code\'s performance-warning threshold',
description: `${file.relPath} is ${withCommas(chars)} chars. Claude Code shows a startup warning ("Large CLAUDE.md will impact performance ... chars > 40.0k") once a CLAUDE.md passes ~40.0k chars on a 200k-context model — it loads in full on every turn. CC 2.1.169 scales that threshold with the context window, so on a ${withCommas(LARGE_CONTEXT_WINDOW)}-token model it relaxes to ~${withCommas(CLAUDE_MD_CHAR_WARN_LARGE)} chars and you are likely within it.`,
file: file.absPath,
evidence: `${withCommas(chars)} chars > 40.0k (200k-context anchor; ~${withCommas(CLAUDE_MD_CHAR_WARN_LARGE)} at ${withCommas(LARGE_CONTEXT_WINDOW)} context). This is an estimate, not measured telemetry.`,
recommendation: 'Split detail into @imports and .claude/rules/ files so only the relevant rules load, and keep the top of CLAUDE.md byte-stable for cache hits.',
autoFixable: false,
}));
}
// --- Empty file ---
if (lines < 3) {
findings.push(finding({

View file

@ -0,0 +1,28 @@
/**
* Context-window constants single source of truth.
*
* Several Claude Code budgets scale with the model's context window:
* - the skill listing is allotted ~2% of context (CC 2.1.32, changelog L2860);
* - the "CLAUDE.md is too long" warning threshold scales with it (CC 2.1.169).
*
* We cannot observe the user's actual context window, so scanners anchor on a
* conservative 200k window (the smallest common size it fires earliest, the
* safe default when the window is unknown) and disclose the relaxed 1M figure.
*
* Zero external dependencies.
*/
// Conservative anchor: the smallest common context window. Budgets anchored
// here fire earliest, which is the safe default when the window is unknown.
export const CONTEXT_WINDOW_ANCHOR = 200_000;
// Large context window (Opus/Sonnet 1M tier). Used to disclose how a budget
// relaxes on large-context models.
export const LARGE_CONTEXT_WINDOW = 1_000_000;
// How much larger the 1M window is than the 200k anchor (= 5). A budget that
// scales linearly with the window relaxes by this factor at 1M.
export const LARGE_CONTEXT_SCALE = LARGE_CONTEXT_WINDOW / CONTEXT_WINDOW_ANCHOR;
// Dependency-free thousands separator (repo invariant: zero external deps).
export const withCommas = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ',');

View file

@ -23,6 +23,7 @@ import { join } from 'node:path';
import { estimateTokens, enumeratePlugins, enumerateSkills } from './active-config-reader.mjs';
import { readTextFile } from './file-discovery.mjs';
import { parseFrontmatter, parseJson } from './yaml-parser.mjs';
import { CONTEXT_WINDOW_ANCHOR, LARGE_CONTEXT_WINDOW, withCommas } from './context-window.mjs';
// Verified per-description skill-listing cap (CC 2.1.105, changelog L1502).
// Descriptions longer than this are truncated in the listing the model sees.
@ -32,14 +33,13 @@ export const DESCRIPTION_CAP = 1536;
// model reads is allotted ~2% of the context window. The context window is
// unknown, so we anchor on a conservative 200k window — the smallest common
// size, which fires earliest — and disclose the assumption in the evidence.
// The 200k/1M window constants live in context-window.mjs (single source of
// truth, shared with the CML CLAUDE.md char-budget check); re-exported here so
// existing importers of this module keep working.
export const BUDGET_FRACTION = 0.02;
export const CONTEXT_WINDOW_ANCHOR = 200_000;
export const AGGREGATE_BUDGET_TOKENS = Math.round(BUDGET_FRACTION * CONTEXT_WINDOW_ANCHOR); // 4000
export const LARGE_CONTEXT_WINDOW = 1_000_000;
export const LARGE_CONTEXT_BUDGET_TOKENS = Math.round(BUDGET_FRACTION * LARGE_CONTEXT_WINDOW); // 20000
// Dependency-free thousands separator (repo invariant: zero external deps).
export const withCommas = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
export { CONTEXT_WINDOW_ANCHOR, LARGE_CONTEXT_WINDOW, withCommas };
// The honest framing required because the budget depends on a context window we
// cannot observe (jf. TOK CALIBRATION_NOTE). Appended to budget-overflow evidence.

View file

@ -0,0 +1,100 @@
# Large CLAUDE.md fixture — chars over 40k, lines under 200
## Project overview
This fixture exercises the CML char-budget check (CA-CML char-budget): it
crosses Claude Code's ~40.0k-char startup-warning threshold while staying well
under 200 lines, so it isolates the char check from the line-count check.
## Commands and workflow
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 00.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 01.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 02.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 03.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 04.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 05.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 06.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 07.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 08.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 09.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 10.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 11.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 12.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 13.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 14.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 15.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 16.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 17.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 18.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 19.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 20.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 21.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 22.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 23.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 24.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 25.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 26.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 27.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 28.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 29.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 30.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 31.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 32.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 33.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 34.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 35.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 36.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 37.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 38.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 39.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 40.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 41.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 42.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 43.
The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. The conventions for this project are written out at length in flowing prose rather than being split into @imports or .claude/rules files, which is the exact shape that drives a CLAUDE.md past its character budget while the line count stays small and unremarkable. Paragraph 44.

View file

@ -129,6 +129,64 @@ describe('CML scanner — large cascade (>500 lines): reframed, not absolute-adh
});
});
describe('CML scanner — char budget mirrors CC startup warning (CC 2.1.169)', () => {
// large-claude-chars/CLAUDE.md is 48,531 chars across 100 lines: it crosses
// Claude Code's ~40.0k-char startup-warning threshold while staying under the
// 200-line count, so it isolates the char-budget check from the line check.
let result;
beforeEach(async () => {
resetCounter();
const discovery = await discoverConfigFiles(resolve(FIXTURES, 'large-claude-chars'));
result = await scan(resolve(FIXTURES, 'large-claude-chars'), discovery);
});
const charFinding = (r) =>
r.findings.find((f) => /performance-warning threshold/i.test(f.title || ''));
it('flags a CLAUDE.md over ~40k chars', () => {
assert.ok(charFinding(result), 'expected a char-budget finding for a >40k-char CLAUDE.md');
});
it('the char-budget finding is MEDIUM (token cost, not an adherence cliff)', () => {
assert.strictEqual(charFinding(result)?.severity, 'medium');
});
it('anchors on CC\'s 40.0k figure and discloses context-window scaling', () => {
const f = charFinding(result);
const text = `${f?.description || ''} ${f?.evidence || ''}`;
assert.match(text, /40\.0k/, 'should mirror CC\'s 40.0k startup-warning figure');
assert.match(text, /context window|scales|1,000,000/i, 'should disclose context-window scaling');
// Lock the scaling arithmetic: 40.0k anchor x (1M / 200k) = 200,000 chars @ 1M.
assert.match(text, /200,000/, 'should disclose the ~200,000-char relaxed threshold at 1M context');
});
it('does NOT fire the line-count findings (chars high, lines under 200)', () => {
const lineFinding = result.findings.find((f) => /exceeds (recommended 200|500)/.test(f.title || ''));
assert.ok(!lineFinding, `char fixture should not trip a line finding: ${lineFinding?.title || ''}`);
});
it('the char-budget finding ID matches CA-CML-NNN', () => {
assert.match(charFinding(result)?.id || '', /^CA-CML-\d{3}$/);
});
});
describe('CML scanner — large-by-lines but under the char budget (no false char finding)', () => {
// large-cascade/CLAUDE.md is 1024 lines but only 37,393 chars (short lines):
// under CC's 40.0k char threshold, so the char-budget finding must NOT fire —
// proving the check keys on chars, not raw size or line count.
let result;
beforeEach(async () => {
resetCounter();
const discovery = await discoverConfigFiles(resolve(FIXTURES, 'large-cascade'));
result = await scan(resolve(FIXTURES, 'large-cascade'), discovery);
});
it('does not emit a char-budget finding under 40k chars', () => {
const f = result.findings.find((x) => /performance-warning threshold/i.test(x.title || ''));
assert.ok(!f, 'a 37k-char file (under 40.0k) must not trip the char-budget finding');
});
});
describe('CML scanner — empty project', () => {
let result;
beforeEach(async () => {