config-audit/CHANGELOG.md
Kjell Tore Guttormsen 346dfac6fa release: v5.12.4 — "Rooted rules" (M-BUG-9: RUL resolves rule glob against the rule's own project root)
Version-sync for the M-BUG-9 fix (code already in 18af5a2):
- plugin.json 5.12.3 -> 5.12.4
- README version badge -> 5.12.4, tests badge 1305 -> 1307, new version-history row
- CHANGELOG [5.12.4] section

checkReadmeBadges: passed:true (tests 1307, scanners 16, commands 21, agents 7, hooks 4 — all
match filesystem). Full suite 1307/0. Frozen v5.0.0 + default-output snapshots byte-stable
(the fix is a no-op when projectRoot === targetPath; RUL appears in no snapshot).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EnUvKEqyEa1m9gy6Aqhdqq
2026-06-26 11:04:30 +02:00

90 KiB
Raw Blame History

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[5.12.4] - 2026-06-26

Summary

"Rooted rules" — fixes M-BUG-9 (dogfooding find) in scanners/rules-validator.mjs. The RUL "Rule path pattern matches no files" check resolved a rule's paths:/globs: glob against the outer scan root instead of the rule's own project root (the directory containing its .claude/), and collectProjectFiles carried a depth>4 cutoff that never reached deep matching files. As a result, a live rule in a nested repo — e.g. a marketplace checkout under ~/.claude/plugins/marketplaces/<mkt>/.claude/rules/ — was wrongly flagged "never activates" (high severity), a false F-grade for any user with rules in a nested repo. Same scope-conflation family as M-BUG-1/2 (the scanner treats a nested repo's config as scoped to the outer scan root). The fix is a no-op for the common single-repo scan (projectRoot === targetPath), so the frozen v5.0.0 + default-output snapshots stay byte-stable; no count change (scanners 16, agents 7, commands 21). 1307 tests (+2).

Fixed

  • rules-validator glob base (M-BUG-9). The dead-rule check now resolves each rule against its own project root:
    • deriveProjectRoot(ruleAbsPath) returns the parent of the rule's .claude segment.
    • Project files are collected and globbed per project root (cached), relative to that root, so a nested repo's rule matches against its own tree where its files live. This also sidesteps the old depth>4 cutoff, because the walk now starts at the nearby project root.
    • User-global rules (projectRoot === $HOME, i.e. ~/.claude/rules/) skip the no-match check: they scope against whatever project is active at runtime, not a fixed tree, so "matches 0 files here" is not a dead-rule signal (and this avoids a $HOME-wide file walk).
    • TDD: 2 failing tests (nested-repo false-positive + HOME guard) → fix → full suite 1307/0, frozen v5.0.0 + default-output snapshots untouched (RUL findings appear in none). Real-machine verify: the two ktg-privat false positives clear and a previously-hidden genuine dead rule (a false negative) surfaces in the bundled optimal-setup example; zero new false positives.

[5.12.3] - 2026-06-26

Summary

"Phantom agents" — fixes M-BUG-3/4/5 (dogfooding finds) in scanners/lib/active-config-reader.mjs so that enumerateAgents counts only the agents Claude Code actually registers. Per the official subagents documentation, an agent file must carry valid name+description frontmatter, and CC scans the agents directory recursively while silently skipping frontmatter-less files. The reader violated all three rules: it counted every .md regardless of frontmatter (M-BUG-5), never recursed into agent subdirectories (M-BUG-3), and double-counted entries when the project directory equals the user directory — the case when the scope root is $HOME (M-BUG-4, the root cause, which also affected rules and output-styles). Real-machine verify: the user-agent count dropped 13→0 (all 12 user agents plus REMEMBER.md are frontmatter-less, so CC registers none of them) and the HOME project duplicate dropped 13→0; the corrected always-loaded baseline is ≈ 53, not 66. Agent enumeration is machine-dependent and therefore absent from the frozen snapshots, so the v5.0.0 + SC-5 + default-output snapshots stay byte-stable; no count change (scanners 16, agents 7, commands 21). 1305 tests (+4).

Fixed

  • active-config-reader agent enumeration (M-BUG-3/4/5). Three surgical fixes:
    • listMarkdownFiles gains an opt-in recursive flag so agent enumeration descends into subdirectories the way Claude Code does (M-BUG-3).
    • configDirs now de-duplicates the project and user paths when they resolve to the same directory (the case when the scope root is $HOME), the root cause that also double-counted rules and output-styles (M-BUG-4).
    • enumerateAgents requires a valid name+description frontmatter block before counting a file, matching CC's actual registration rule — frontmatter-less files are silently skipped (M-BUG-5).
    • Added a hasText frontmatter helper. TDD: 4 failing tests (one per bug) → fix → full suite 1305/0, frozen v5.0.0 + SC-5 + default-output snapshots untouched (agent enumeration is machine-dependent and never seeded into a snapshot).

[5.12.2] - 2026-06-24

Summary

"Honest census" — fixes a plugin-enumeration bug (M-BUG-1, dogfooding find) that made the always-loaded inventory untrustworthy on two common setups: machines with disabled plugins and polyrepo marketplaces. enumeratePlugins walked ~/.claude/plugins/marketplaces/<mkt>/plugins/ and ignored both enable-state and the polyrepo cache layout, so it over-counted phantom agents from disabled/unenabled plugins while missing the entire enabled polyrepo set (whose plugins live under cache/, not marketplaces/<mkt>/plugins/). It now gates on installed_plugins.json + enabledPlugins and enumerates each plugin from its active installPath, with the marketplaces walk as fallback. Affects manifest, whats-active, the agent-listing (AGT) and token-hotspots for every such user. No new finding ID or scanner (count stays 16, agents 7, commands 21); --json/--raw stay byte-stable and the frozen v5.0.0 + SC-5 + default-output snapshots are untouched. 1301 tests (+4).

Fixed

  • active-config-reader plugin enumeration (M-BUG-1). enumeratePlugins(repoPath) now honors enabledPlugins (disabled plugins no longer contribute phantom agents/skills/commands) and enumerates polyrepo plugins from their active installPath in installed_plugins.json (not only marketplaces/<mkt>/plugins/). Real-machine verify: the always-loaded agent listing dropped from 114 to 104 with the phantom ghosts gone and the true enabled set present. TDD: 4 failing tests → fix → full suite 1301/0, snapshots untouched.

[5.12.1] - 2026-06-24

Summary

"Footgun guard" — Pattern H (stale plugin-cache versions, token-hotspots) recommended deleting stale version directories without warning that a currently-running session may still hold one of those versions for its whole lifetime. "Stale" is judged against installed_plugins.json (what NEW sessions load), so the recommendation could reproduce the exact failure that broke a live session: deleting the directory pulls the files out from under the running session, which then breaks and must /exit + restart. Recommendation text only — no new finding ID or scanner (count stays 16, agents 7, commands 21), no token figures changed, so --json/--raw stay byte-stable and the frozen v5.0.0 + SC-5 + default-output snapshots are untouched. 1297 tests.

Fixed

  • Pattern H live-session caveat (token-hotspots, plugin-cache-hygiene). The stale-cache cleanup recommendation now cautions against deleting a version a running session still uses, and tells affected sessions to /exit + restart to pick up the active version — closing the footgun that broke a live session during the C4 cache cleanup.

[5.12.0] - 2026-06-23

Summary

"Auto-calibration" — completes the deferred half of B8. --context-window auto now probes the configured model and calibrates SKL/CML budgets to its real context window, instead of always falling back to the conservative advisory anchor. A 1M-tier host self-calibrates without the manual --context-window 1000000. No new finding ID or scanner (count stays 16, agents 7, commands 21); the default and explicit --context-window paths are unchanged, so --json/--raw stay byte-stable and the frozen v5.0.0 + SC-5 snapshots are untouched. 1296 tests.

Added

  • B8b — model→window auto-probe. lib/context-window.mjs gains a pure modelToContextWindow() that maps a configured model id/alias to its context window:
    • the explicit [1m] tier tag wins (the running session model surfaces as e.g. claude-opus-4-8[1m]);
    • known 1M-tier families LARGE_CONTEXT_MODEL_IDS (claude-fable-5, claude-opus-4-8, claude-opus-4-7, claude-opus-4-6, claude-sonnet-4-6 — verified June 2026 against the platform.claude.com models overview) match by substring, so dated (-20260528) and provider-prefixed (us.anthropic.…) IDs resolve too;
    • the short aliases opus / sonnet / fable / opusplan resolve to 1M.
    • Models we cannot confirm (Haiku, older 200k-era IDs, unknown) return null — the caller then keeps the conservative anchor rather than guess a relaxed budget.
  • New IO helper lib/active-model.mjs resolveActiveModel() reads the configured model the way Claude Code resolves it: the shell ANTHROPIC_MODEL override first, otherwise the settings cascade model field (user ~/.claude → project .claude → project-local, local wins). Reads the cascade files directly (like isBundledSkillsDisabled) and takes an injectable env, so it stays deterministic and hermetic under the test HOME. Returns null when no model is pinned anywhere.

Changed

  • resolveContextWindow(arg, opts) — the auto branch now probes. It maps opts.model via modelToContextWindow(): a recognized 1M-tier model calibrates to its window (source auto-probed, not advisory); an unknown or unpinned model keeps the conservative 200k anchor and stays advisory (source auto-unresolved, the pre-B8b auto behavior). scan-orchestrator resolves the active model (only when the flag is auto) and threads it in; posture inherits this via runAllScanners. The default (no flag) and explicit --context-window <n> paths ignore opts.model and are unchanged.

[5.11.0] - 2026-06-23

Summary

"Precision polish" — the two LOW-priority calibration gaps from the hardening plan, both additive. B7 flags an oversized SKILL.md body (CA-SKL-003), honestly framed as an on-demand cost. B8 lets CA-SKL-002 and the CML char-budget calibrate to a real context window via --context-window, and downgrade to advisory when the window is unknown — so the 200k anchor stops crying wolf on a 1M host. Scanner count stays 16 (both extend the existing SKL/CML scanners), agents 7, commands 21; --json/--raw stay byte-stable and the frozen v5.0.0 + SC-5 snapshots are untouched. 1279 tests.

Added

  • B7 — oversized skill body (CA-SKL-003, low). measureActiveSkillListing() now measures the SKILL.md body below the frontmatter (the file was already read in full; only the frontmatter was parsed). A body over ~5,000 tokens (BODY_TOKEN_THRESHOLD) fires CA-SKL-003, recommending a supporting-file split and context: fork for heavy skills.
    • Honest framing (Verifiseringsplikt): BODY_CALIBRATION_NOTE marks this as an on-demand cost — the body loads only when the skill is invoked, not every turn like the always-loaded listing — and an estimate (chars/4), hence low severity. Distinct from the always-loaded listing-budget findings.
  • B8 — context-window calibration (--context-window). CA-SKL-002 (skill-listing budget) and the CML char-budget threshold now calibrate to a resolved context window instead of always anchoring at 200k. lib/context-window.mjs gains resolveContextWindow() and scaleForWindow():
    • --context-window <n> calibrates the budget to n (e.g. 1000000 relaxes the SKL listing budget to ~20,000 tok, so an over-200k listing is within budget and does not fire).
    • --context-window auto keeps the conservative 200k anchor but marks the result advisory — SKL/CML emit the finding at info rather than as a budget breach (model→window auto-probing is deferred to a later B8b).
    • No flag → the conservative 200k anchor at full severity, byte-identical to the pre-B8 default.
    • Both SKL and CML keep an untouched default branch (window === 200k && !advisory) for byte-stability plus a calibrated branch. The flag is wired through scan-orchestrator and posture; runAllScanners resolves it once and threads { contextWindow } to the scanners (others ignore the third arg).
    • CPS intentionally excluded: it has no window-anchored budget (a fixed 150-line volatility heuristic), so there is nothing to calibrate.

[5.10.0] - 2026-06-23

Summary

"Deferral & injection hygiene" — three additive hardening levers that extend existing scanners toward a tighter always-loaded prefix. B4 detects config that forces full MCP tool schemas into the always-loaded prefix (deferral defeated), with a CLI-over-MCP companion lever. B5 adds a hook additionalContext-injection advisory plus a filter-before-Claude-reads lever. B6 extends the cache-prefix scanner to follow @imports and flag volatile content in imported files. Scanner count stays 16 (all three extend existing scanners), agents 7, commands 21; --json/--raw stay byte-stable and the frozen v5.0.0 + SC-5 snapshots are untouched. 1257 tests.

Added

  • B4 — MCP tool-schema deferral (CA-TOK-006; tokens patterns 7 → 8). By default Claude Code defers MCP tool schemas (names-only, ~120 tok; full schemas load on demand via tool search). CA-TOK-006 detects config-file signals that force the FULL schemas into the always-loaded prefix every turn: settings.json env.ENABLE_TOOL_SEARCH="false" (high), "ToolSearch" in permissions.deny (high), a configured Haiku model (medium), and per-server .mcp.json alwaysLoad:true (CC v2.1.121+, high). Severity scales with the aggregate forced-upfront tokens (medium-confidence reasons cap at medium). New pure engine lib/mcp-deferral.mjs (assessMcpDeferral, unit-tested, no IO) shared by TOK and GAP. A feature-gap CLI-over-MCP lever fires only as a companion to CA-TOK-006 (prefer gh/aws/gcloud over MCP for common operations — CLI adds zero context tokens until used).
    • Honest scoping (Verifiseringsplikt): triggers on config files ONLY, never process.env shell vars. Vertex / custom ANTHROPIC_BASE_URL / a runtime /model switch are launch state (would flap snapshots machine-dependently), so they are DISCLOSED in every finding, not triggered. Mechanism verified 2026-06-23 against code.claude.com/docs (context-window.md, mcp.md#configure-tool-search + #exempt-a-server-from-deferral, costs.md); the prefix-cache-invalidation claim was NOT-CONFIRMED in docs and is not asserted.
  • B5 — hook additionalContext-injection advisory + filter-before lever. HKV emits an info advisory when a hook injects unfiltered command output into additionalContext (it enters context every turn the hook fires). A feature-gap filter-before-Claude-reads companion lever cites the documented filter-test-output.sh pattern (filter at the hook, not after Claude reads).
  • B6 — CPS @import volatile scan. The cache-prefix scanner now follows @imports (one hop) and flags volatile content in the imported file that breaks the cached prefix — a new medium finding ("Volatile content in @imported file breaks cached prefix"), keyed on the resolved file with evidence "imported by (@ at line N)". Scoped to one hop (the IMP scanner owns deep chains); resolved files that are themselves discovered CLAUDE.md are skipped (own iteration).

Notes

  • Scanner count unchanged at 16 — B4/B5/B6 all extend existing scanners (TOK / HKV + GAP / CPS). --json/--raw output remains byte-stable; frozen v5.0.0 + SC-5 snapshots untouched.

[5.9.0] - 2026-06-23

Summary

"Machine-wide token lens" — the three highest-impact hardening gaps toward whole-machine token tuning (the shift from correct? to best-practice-tuned?). B1 measures the always-loaded agent listing (a new orchestrated scanner), B2 gives the campaign ledger a machine-wide always-loaded token bill that counts the shared global layer once, and B3 makes the token and conflict scans cache-aware so stale plugin-cache versions stop polluting them. Every new token figure carries an honesty caveat where the load mechanism is inferred or the number is an upper-bound estimate. --json/--raw stay byte-stable; frozen v5.0.0 + SC-5 snapshots untouched. Scanner count 15 → 16, agents 7, commands 21; 1215 tests.

Added

  • B1 — Agent-listing budget (new orchestrated scanner AGT, count 15 → 16). Claude Code injects every active agent's name+description into the system prompt on every turn; on a heavily-plugged machine this is often the dominant single always-loaded source, yet nothing measured it. New scanners/agent-listing-scanner.mjs + scanners/lib/agent-listing-budget.mjs:
    • CA-AGT-001 — per-agent description over the soft bloat cap (advisory; mirrors the TOK 500-char SKILL.md heuristic — agents have no verified per-description cap, so nothing is truncated, the description is simply re-sent in full every turn).
    • CA-AGT-002 — the summed agent-listing estimate exceeding the listing budget.
    • Intellectual-honesty contract: both findings are LOW and explicitly inferred / upper-bound — the agent-listing mechanism is undocumented (agents are absent from Claude Code's published context breakdown), the per-agent estimate is the frontmatter cap not a measurement, and the budget is a config-audit heuristic anchored on a conservative 200k window. The evidence discloses all three caveats rather than overstating certainty.
  • B2 — Machine-wide always-loaded token roll-up (campaign). The campaign ledger now carries a token bill alongside the severity roll-up. campaign refresh-tokens runs a live cross-repo sweep (readActiveConfigbuildManifest → ownership split) that factors out the shared global always-loaded layer (global CLAUDE.md + agent listing + global MCP + unscoped global rules) and counts it once, then adds each repo's delta — surfacing one machine-wide always-loaded total plus a ranked "most expensive repos" table. The shared layer is written once from the first readable repo (counted-once guard against the whats-active double-count).
  • B3 — Stale plugin-cache disk-cleanup finding (TOK). Stale ~/.claude/plugins/cache versions surface as a finding (--global) categorized Dead config (not "Wasted tokens"): they occupy disk but load on zero turns. A per-finding category override (plugin-cache-hygiene) plus a dedicated humanizer translation keep the prose honest ("safe to delete … zero tokens per turn … housekeeping, not a performance problem").

Changed

  • B3 — Cache-aware filtering (folds in B0): --exclude-cache (default ON) for token-hotspots and the orchestrator. ~/.claude/plugins/cache holds both active and stale plugin versions — installed_plugins.json's installPath points INTO the cache — so a blunt "skip all of plugins/cache" would drop active config. Discovery is now version-aware: it reads the adjacent installed_plugins.json, keeps each plugin's active version dir, and drops only stale ones. Result: stale versions no longer pollute the token-hotspot ranking, and stale cached hooks.json no longer inflate CNF "duplicate hook" findings (verified: cache exclusion measurably drops the count). --no-exclude-cache restores the full walk. When installed_plugins.json is absent or unparseable, nothing is filtered (never silently drop live config).
  • B2 — manifest ownership split. splitManifestByOwnership separates the shared-global layer from per-repo deltas; ~/.claude.json:projects MCP is correctly classified as a per-repo delta (its slice is keyed on the repo path), not a shared-global source.
  • Humanizer: a finding's category can now override the scanner-default impact label (CATEGORY_TO_IMPACT), used so a TOK finding can read as Dead config when that is the honest bucket. The raw category field is unchanged, so --json/--raw stay byte-stable.

[5.8.0] - 2026-06-23

Summary

"Campaign motor" — a durable, machine-wide audit campaign that sits ABOVE individual sessions. A single audited repo is one session; a fleet of repos is a campaign. This release adds a durable ledger (per-repo lifecycle + machine-wide roll-up), a read-only reporter and a human-approved write surface behind one /config-audit campaign command, a cross-repo prioritized backlog, and per-repo plan export — with execution deliberately reusing the existing implement/rollback engines rather than adding new machinery. The whole campaign surface is -cli/lib only, so the scanner count stays 15, agents 7, and the snapshot suite is untouched; commands 20 → 21.

Added

  • Durable campaign ledger (scanners/lib/campaign-ledger.mjs). The ledger that sits above sessions: a repo list + per-repo lifecycle (pending → audited → planned → implemented) + a machine-wide roll-up (counts by status and by severity, aggregated across repos). Persists to a single JSON file outside the plugin dir (~/.claude/config-audit/campaign-ledger.json, next to sessions/) so it survives uninstall / reinstall / upgrade. Pure transforms (createLedger / addRepo / setRepoStatus / rollUp) with now injected (never the clock) + a soft validateLedger + a thin IO shell; schemaVersion stamped from the start.
  • Cross-repo prioritized backlog (buildBacklog). One machine-wide work list — each item is a repo (the ledger tracks per-repo severity counts, not individual findings), ordered by a weighted severity score (critical:1000 / high:100 / medium:10 / low:1) with a deterministic tie-break so criticals always win. Includes only repos not yet implemented that have known findings.
  • /config-audit campaign (commands 20 → 21). A thin orchestrator that always reports first (read-only campaign-cli: status, roll-up, backlog) and, for init / add / set-status, proposes the change and mutates only on explicit human approval via campaign-write-cli — it never hand-edits the ledger JSON (verification duty). add --discover <root> finds git repos under a root to pick from; marking a repo audited attaches findings-by-severity from its session (or user-provided counts), never invented.
  • Per-repo plan export (campaign-export-cli). --repo <path> resolves the repo's linked session, reads its action-plan.md, and (under opt-in --write) drops a provenance-stamped, byte-faithful copy into that repo's own docs/config-audit-plan-<session>.md — a durable record next to the code it changes. Advisory gates for "no session linked" / "no action plan yet".

Changed

  • Execution by reuse. A campaign adds no execution engine of its own: the exported docs/ file is the durable record, while /config-audit implement (backup + apply + verify) and /config-audit rollback do the work, then campaign set-status <repo> implemented records it.
  • knowledge-refresh wired into the surface. The v5.7.0 knowledge-refresh command shipped but was never routed; it is now in the /config-audit router (argument-hint + routing) and the help command table.
  • CLAUDE.md trimmed to lean invariants. The 19 per-scanner / per-block implementation notes moved verbatim into docs/scanner-internals.md ("Implementation notes"); CLAUDE.md drops 540 → 134 lines (raising its own config grade B → A). A real "Conventions" section now points at .claude/rules/.

Tests

  • 1091 → 1168 (campaign ledger +28, read-only campaign-cli +8, campaign-write-cli +11, cross-repo backlog +12, plan-export +18).

[5.7.0] - 2026-06-21

Summary

"Optimization lens" — the first detector of the «is this config optimally shaped?» axis, on top of the existing «is it correct?» checks, plus a living machine-readable knowledge layer that keeps the lens's rules fresh. A config that works can still fit a better mechanism (a long CLAUDE.md procedure that belongs in a skill, a lifecycle instruction that belongs in a hook); this release adds a hybrid deterministic + opus motor that surfaces those as opportunities, every finding citing a provenance-stamped best-practices register entry. Scanner count 14 → 15, commands 18 → 20, agents 6 → 7.

Added

  • Best-practices register (knowledge layer). knowledge/best-practices.json — a provenance-stamped, schema-validated register (each entry: id / claim / confidence / source + optional mechanism / lensCheck), the first runtime-consumed file under knowledge/ and the source of truth for the optimization lens. Loaded/validated by scanners/lib/best-practices-register.mjs. Only confirmed entries are user-facing (verification duty).
  • Optimization-lens scanner (CA-OPT, scanner count 14 → 15). New orchestrated scanner: CA-OPT-001 (LOW, Missed opportunity) — a CLAUDE.md procedure (≥6 consecutive numbered steps) that would fit better as a skill, with recommendation + provenance from register entry BP-MECH-003. Deterministic and conservative (no false positives on the negative corpus).
  • /config-audit optimize + optimization-lens-agent (opus, agents 6 → 7). The prose-judgment half of the lens, for the cases the deterministic scanner deliberately skips: lifecycle phrasing → hook (BP-MECH-001), unscoped path-specific instruction → rule (BP-MECH-002), absolute "never" prohibition → permission (BP-MECH-004). A pre-filter (scanners/lib/lens-prefilter.mjs, unit-tested) does cheap recall; the opus agent is the precision gate, dropping low-confidence candidates and citing the register id + source for each kept one. Agent-driven → deliberately not byte-stable (own command, outside the snapshot suite).
  • /config-audit knowledge-refresh (commands 19 → 20). Keeps the register current so the lens never reads stale rules. A deterministic core (scanners/lib/knowledge-refresh.mjs, assessFreshness with an injected reference date — it never reads the clock) classifies each entry fresh/stale by the age of its source.verified stamp (default 90-day quarterly cadence); a read-only CLI (knowledge-refresh-cli.mjs, --reference-date / --stale-after / --dry-run) reports staleness without ever writing the register or hitting the network. The command layer re-verifies stale sources (WebFetch) and polls the CC changelog + Anthropic blog (WebSearch), then applies only human-approved writes (verification duty). Web/judgment-driven → deliberately not byte-stable.

Notes

  • Byte-stability. The new OPT scanner's additive entry is stripped at compare time (tests/helpers/strip-added-scanner.mjs); the original 13-scanner --json / --raw output stays byte-stable, and only the SC-5 default-output snapshots are regenerated (additive). The two non-deterministic surfaces (optimize, knowledge-refresh) live outside the snapshot suite by design. -cli / lib modules are not orchestrated scanners (self-audit excludes them), so the scanner count stays 15. Suite 1091 pass (hermetic HOME), self-audit A/A (config 94, plugin 100), readmeCheck passed.

[5.6.0] - 2026-06-20

Summary

"Steering-model II" — the per-mechanism load-pattern and compaction-survival model lands end-to-end across enumeration, the manifest / tokens token accounting, and a new output-style scanner. v5.5.0 shipped A+E (additive to existing scanners); this release adds the Foundation those features needed, plus B (load-pattern accounting) and C (the new CA-OST family, scanner count 13 → 14). Every new claim is anchored to the live Claude Code docs (code.claude.com/docs) per the project's verification duty.

Added

  • Foundation — active-config-reader enumeration + load-pattern model. active-config-reader now enumerates rules, agents, and output styles (alongside the existing CLAUDE.md / plugins / skills / hooks / MCP). Each source carries a loadPattern (always / on-demand / external), survivesCompaction, and derivationConfidence, derived from the published loading model. The frontmatter parser additionally reads YAML block sequences (paths: as a - item list), resolving a pre-existing RUL false-positive.
  • C — output-style scanner (CA-OST, scanner count 13 → 14). New orchestrated scanner: CA-OST-001 (MEDIUM) a user/project custom output style missing keep-coding-instructions: true (defaults false) silently strips Claude Code's built-in software-engineering instructions when active (V10); CA-OST-002 (LOW) a plugin output style with force-for-plugin: true overrides the user's selected outputStyle (V11); CA-OST-003 (MEDIUM) a settings outputStyle matching no built-in (Default / Explanatory / Learning / Proactive) nor discovered custom style is dead config.

Changed

  • B — load-pattern accounting in manifest and tokens. manifest now reports component-level token sources (the coarse plugin roll-up that double-counted its own skills / rules / agents is gone), tags every source with the load-pattern triple, and leads with an always-loaded subtotal ("≈X tokens enter context every turn before you type"). token-hotspots annotates each ranked hotspot with its load pattern. manifest's total changes (de-duplicated) — the intended correctness fix.

Notes

  • Byte-stability. --json / --raw stay byte-stable for the original 13 scanners. The frozen v5.0.0 snapshots are preserved: B2's additive hotspot fields and C's additive OST scanner entry are stripped at compare time (tests/helpers/strip-hotspot-load-pattern.mjs, tests/helpers/strip-added-scanner.mjs); only the SC-5 default-output snapshots are regenerated. Suite 1023 pass, self-audit A/A (config 93, plugin 100), readmeCheck passed.

[5.5.0] - 2026-06-20

Summary

"Steering-model I" — two additive findings on the compaction-durability / dead-config theme, extending the existing RUL, CML, and PLH scanners (scanner count stays 13; --json and --raw remain byte-stable). Both are anchored to the live Claude Code docs (code.claude.com/docs) per the project's verification duty. The Foundation work (active-config-reader enumeration) and feature B are deferred to v5.6 — A and E are additive to existing scanners and do not consume it.

Added

  • RUL / CML compaction-durability findings (A) — per the official "what survives compaction" model (context-window.md), only the project-root CLAUDE.md and unscoped rules are re-injected after a context compaction. Two structural findings, both severity LOW: a large (>50-line) path-scoped rule reloads only on a matching file read and is not re-injected after compaction, so a must-hold rule can silently drop mid-session (RUL); a nested (subdirectory) CLAUDE.md is not re-injected after compaction — only the project root is (CML).
  • PLH plugin-agent ignored-frontmatter finding (E) — plugin subagents silently ignore hooks/mcpServers/permissionMode frontmatter (honored only for user/project agents in .claude/agents/). Setting them in a plugin agent is dead config: permissionMode is MEDIUM (it implies a restriction Claude Code does not apply — false security); hooks/mcpServers are LOW. Additive to PLH's agent-frontmatter loop; one humanizer pattern covers the three fields.

Known limitations

  • The lightweight frontmatter parser (scanners/lib/yaml-parser.mjs) reads inline paths: "a, b" but not YAML block sequences (paths:\n - a), so a block-sequence-scoped rule is still seen as unscoped and the RUL durability check (A) does not fire on it. Pre-existing and broader than A; to be fixed in the v5.6 Foundation work.

[5.4.1] - 2026-06-20

Summary

Scanner-correctness patch. Five primary-source-verified fixes to the PLH, HKV, and RUL scanners — no new scanner, no command or output-format change (scanner count stays 13; --json and --raw remain byte-stable). All fixes are anchored to the live Claude Code docs (code.claude.com/docs, docs map 2026-06-19) per the project's verification duty.

Fixed

  • HKV hook events — added Setup, UserPromptExpansion, and PostToolBatch to the valid-event set (verified against hooks.md); a valid hook using one of these was wrongly flagged "Unknown hook event … will never fire", which could lead a user to delete a working hook. The "(N total)" hint in the recommendation is now derived from the set so it cannot drift.
  • HKV post-session — removed from the valid-event set. The 2.1.169 changelog post-session hook is a self-hosted-runner workspace-lifecycle hook (runs after the session, before the workspace is deleted), not a settings.json hook event — it is absent from hooks.md (all settings.json events are PascalCase). A bogus post-session settings hook is now correctly flagged.
  • RUL globs rule — reworded the finding. Only paths: is documented as the rule-scoping field; whether Claude Code ever read globs is unverified, so the previous "deprecated/legacy" framing overclaimed. The finding (and its humanizer entry) now steer to the documented paths: field without asserting deprecation. The coupled fix-engine title match was updated in lockstep.
  • PLH required frontmattermodel/tools/name/allowed-tools are optional per the CC agent/command docs; a missing one is no longer flagged.
  • PLH CLAUDE.md component sections — a commands/agents/hooks section is required only for a component the plugin actually ships.

[5.4.0] - 2026-06-19

Summary

Plugin-hygiene & settings-validation hardening. Three additive findings extend the existing PLH and SET scanners — no new scanner file, so the scanner count stays 13. PLH gains plugin-folder shadow detection and skills:-array validation; the settings validator now checks autoMode structure and flags it when placed in shared project settings, where Claude Code does not read it. Scanner internals only — no command, agent, or output-format changes; --json and --raw remain byte-stable.

Added

  • PLH plugin-folder shadowing (CA-PLH-015) — flags a plugin.json component-path key in the replaces set (commands/agents/outputStyles) that points at a custom path while the default folder of that name still exists on disk, so the folder is silently ignored (dead config). Mirrors Claude Code's own warning in /doctor, claude plugin list, and the /plugin detail view (v2.1.140+). Severity medium, category: 'plugin-hygiene', details: { field, ignoredDir, customPaths }. Deliberately excludes skills (adds to the default scan, never shadows) and hooks/mcpServers/lspServers (own merge rules); honors the explicit-address exception (a custom path resolving into the default folder is not flagged).
  • PLH skills:-array validation (CA-PLH-016) — validates each plugin.json skills entry (string or array) resolves to an existing directory inside the plugin root; one finding per bad entry with problemnon-string / escapes-root / not-found / not-a-directory. Mirrors claude plugin validate (~2.1.145). Severity medium, category: 'plugin-hygiene', details: { field: 'skills', entry, problem }. Path-escape detection is containment-based (resolve + plugin-root prefix), backed by the docs' path-traversal rule.
  • SET autoMode structure + dead-config (CA-SET) — autoMode, if present, must be an object whose only keys are environment/allow/soft_deny/hard_deny, each a string array (the literal "$defaults" is a valid entry); unknown sub-keys and wrong value types are flagged (problemnot-an-object / unknown-subkey / not-string-array), severity medium. Separately, autoMode placed in shared project settings (.claude/settings.json) is flagged as dead config (problem: 'shared-project-scope'), severity low — Claude Code's classifier does not read autoMode from shared project settings. The two sub-checks are independent. Both verified against code.claude.com/docs/en/auto-mode-config.

Internal

  • Test count — 936 → 949 (+13: PLH-015, PLH-016, and SET autoMode structure/dead-config).
  • Scanner count stays 13 — all three findings extend existing PLH and SET scanners; no new scanner file, no badge/table/countScannerShape change.
  • Byte-stabilityjson-backcompat + raw-backcompat + the SC-5 default-output snapshot stay green; the three findings are additive and appear only in configs that trigger them.

[5.3.0] - 2026-06-19

Summary

Permission-rule & plugin-hygiene hardening. Five additive scanner findings extend the existing DIS, CML, PLH, and feature-gap scanners — no new scanner file, so the scanner count stays 13. DIS gains forbidden-parameter and ineffective-allow-wildcard detection; CML mirrors Claude Code's own 40.0k-char "large CLAUDE.md" startup warning, context-window scaled; PLH flags two plugins that declare the same name; and feature-gap recommends disableBundledSkills under skill-listing pressure. The cross-plugin command-name finding is reframed from a HIGH conflict to a LOW ambiguity. Scanner internals only — no command, agent, or output-format changes; --json and --raw remain byte-stable.

Added

  • DIS forbidden-param rules — flags Tool(param:value) whose key is the tool's own canonicalizing field (command/file_path/path/notebook_path/url); Claude Code ignores these and emits a startup warning. Severity by intent: deny/ask = false security (medium), allow = dead config (low). Valid forms (Bash(npm:*), WebFetch(domain:host), Agent(model:opus)) are never flagged. Predicate forbiddenParamRule in permission-rules.mjs.
  • DIS ineffective allow-wildcards + Tool(*) deny-all — flags unanchored tool-name globs in permissions.allow (*, B*, mcp__*) that CC silently skips (low); treats Tool(*) as deny-all (Bash(*)Bash) so a bare allow killed by it is reported as dead config. Valid mcp__<server>__* is never flagged.
  • CML context-window-scaled char budget — new CA-CML finding mirroring CC's startup warning "Large CLAUDE.md will impact performance (X chars > 40.0k)". Anchors on the conservative 200k window; discloses the relaxed ~200,000-char figure at 1M context. Severity medium (token cost). Char-keyed, complementary to the 200/500-line checks. Window constants live in the shared scanners/lib/context-window.mjs.
  • PLH plugin namespace collision — flags 2+ discovered plugins declaring the same name in plugin.json. Namespaces collapse; CC picks an undocumented winner; the loser's commands/skills/agents go silently unreachable. Severity medium (dead config), category: 'plugin-hygiene', COL-shaped details.namespaces. Keys on the declared name, not basename(dir); name-less plugins are excluded.
  • feature-gap disableBundledSkills lever — conditional recommendation to set disableBundledSkills when the active skill listing is over budget; remediation companion to SKL CA-SKL-002.

Changed

  • PLH cross-plugin command-name overlap: HIGH → LOW — reframed from "conflict" to "ambiguity". Commands are namespaced (/name:command) so both stay reachable; only a same-name plugin collision loses components (covered by the new namespace-collision finding). Now group-first (one finding per command name listing every namespace), COL-shaped details.namespaces. The old HIGH Cross-plugin command name conflict finding and its humanizer entry are removed. Scoring impact: configs with cross-plugin command overlap score slightly higher. Not a --json-shape break — no test or consumer asserted the old HIGH.

Fixed

  • README scanner table — added the missing SKL row (12 → 13 rows); the prose table lagged the badge/self-audit count (the --check-readme gate is number-only and didn't catch it).

Internal

  • Extract skill-listing budget to shared libscanners/lib/context-window.mjs as the single source of truth for the 200k/1M context-window constants, re-exported by skill-listing-budget.mjs and consumed by the new CML char-budget finding.

Knowledge

  • Three backing entries added: disableBundledSkills as a token-efficiency lever (prompt-cache-patterns.md); the 40.0k-char "large CLAUDE.md" startup warning + CC 2.1.169 context-window scaling (claude-code-capabilities.md); and Tool(param:value) permission semantics — deny/ask-only matching plus the canonicalizing-field rules CC ignores (claude-code-capabilities.md).

Test count

  • 936 tests across 56 test files (unchanged — this release covers already-tested work on main).

Verification

  • 936/936 tests pass (node --test 'tests/**/*.test.mjs').
  • node scanners/self-audit.mjs --json --check-readmeconfigGrade: A, pluginGrade: A, readmeCheck.passed: true.
  • README version badge updated: version-5.2.0version-5.3.0. Scanner count stays 13; tests badge already 936+.

[5.2.0] - 2026-06-18

Summary

Claude Code 2.1.114→181 compatibility + skill-listing budget release. Adds a new orchestrated scanner (SKL) for the model's skill-listing token budget, refreshes five validators to recognize the settings/hook surface shipped across CC 2.1.114181, and eliminates a batch of false positives surfaced by an adversarial gap-review. Scanner internals only — no command, agent, or output-format changes.

Added

  • scanners/skill-listing-scanner.mjs (SKL) — new orchestrated deterministic scanner (→ 13 orchestrated scanners). CA-SKL-001 (medium): an active skill description over the verified 1,536-char listing cap (CC 2.1.105) is silently truncated in the model's skill listing. CA-SKL-002 (low): the summed length of all active descriptions (each counted up to the cap) over the listing budget (~2% of context, CC 2.1.32) — anchored on a conservative 200k window with a note that the budget scales 5× on 1M-context models; leads with the measured sum, an estimate not telemetry. HOME-scoped (all user + plugin skills). Remediation surfaces disableBundledSkills / skillOverrides / description trimming.

Changed

  • settings-validator — accepts CC 2.1.114181 settings keys and xhigh reasoning effort.
  • hook-validator — recognizes MessageDisplay and post-session hook events (28 events).
  • claude-md-linter — CLAUDE.md length reframed from a HIGH "adherence cliff" to a MEDIUM token-cost finding (model-neutral, context-window aware).
  • tokens — stale "Opus 4.7" framing refreshed to model-neutral with an Opus 4.8 anchor.
  • Knowledge corpus — refreshed to the Opus 4.8 era (CC 2.1.114→181).

Fixed

  • mcp-config-validator — no longer flags auto-injected or POSIX-style env vars; removed an invented trust field that does not exist in the MCP config schema.
  • permissions (DIS/CNF) — dead-allow detection and conflict matching are now parameter-aware, removing false positives on parameterized permission rules.

Internal

  • Hermetic test isolation — byte/snapshot tests and all 12 CLI-spawning test files now isolate HOME via the hermetic-home helper, closing a leak class where the real ~/.claude skills and config bled into fixture-scoped runs (notably SKL, which is HOME-scoped regardless of includeGlobal). Adversarially verified via a devil's-advocate gap-review pass.

Test count

  • 792 → 875 tests across 52 → 55 test files.

Verification

  • 875/875 tests pass (node --test 'tests/**/*.test.mjs').
  • node scanners/self-audit.mjs --json --check-readmeconfigGrade: A (96), pluginGrade: A (100), readmeCheck.passed: true.
  • README badge updated: tests-792+tests-875+.

[5.1.0] - 2026-05-01

Summary

Plain-language UX humanizer release. Default output of all 18 commands now leads with prose; technical IDs surface at end-of-line as references rather than headlines. Non-expert users — the bulk of the OSS audience — now read findings like "Fix soon: The same automation is set up more than once" instead of "[high] CA-CNF-001: Hook duplicate event registration". Scanner internals are unchanged; humanization is a pure output-time transform applied at the rendering layer. The --raw flag preserves v5.0.0 verbatim output for tooling that scrapes stderr; --json is unchanged from v5.0.0 and remains byte-stable for programmatic consumption.

Delivered across 6 waves (Wave 0 baseline → Wave 1 humanizer module → Wave 2 test re-anchoring → Wave 3 CLI wiring → Wave 4 contract tests → Wave 5 templates/agents → Wave 6 release).

Added

  • scanners/lib/humanizer.mjs — pure-function output translator: humanizeFinding, humanizeFindings, humanizeEnvelope, computeRelevanceContext. Never mutates inputs. Adds three additive fields per finding (userImpactCategory, userActionLanguage, relevanceContext) and replaces title/description/recommendation when a translation is available; falls through to originals otherwise.
  • scanners/lib/humanizer-data.mjs — TRANSLATIONS table for 13 scanner prefixes (CML, SET, HKV, RUL, MCP, IMP, CNF, COL, TOK, CPS, DIS, GAP, PLH). Three-step lookup per finding: exact title → regex pattern → _default → fall through to scanner original.
  • --raw flag threaded through every CLI: posture.mjs, scan-orchestrator.mjs, token-hotspots-cli.mjs, manifest.mjs, whats-active.mjs, fix-cli.mjs, drift-cli.mjs, self-audit.mjs. Bypasses humanizer; emits byte-stable v5.0.0 verbatim output.
  • User-impact categories (5 labels): Configuration mistake, Conflict, Wasted tokens, Missed opportunity, Dead config. Mapped from scanner prefix.
  • Action-language phrases (5 labels): Fix this now, Fix soon, Fix when convenient, Optional cleanup, FYI. Mapped from severity.
  • Relevance context (3 values): test-fixture-no-impact, affects-this-machine-only, affects-everyone. Computed from finding's file path — basenames matching *.local.* and paths containing /tests/fixtures/ are recognized.
  • Self-audit terminal humanizationformatSelfAudit() routes through humanizeEnvelope. JSON path (--json) is unchanged; humanization applies only to the prose terminal render.
  • Forbidden-words lint (tests/lint-forbidden-words.json + runner) — 3-tier vocabulary blocklist enforced over default-mode output, ensuring humanized prose stays in plain language.
  • Scenario read-test (tests/scenario-read-test.mjs + 5 scenarios) — corpus-driven readability check covering broken hook, duplicate keys, stale @import, dead tool, oversized cascade.
  • tests/snapshots/v5.0.0/ + tests/snapshots/v5.0.0-stderr/ — frozen byte-equal references for SC-6 (--json) and SC-7 (--raw) backwards-compatibility tests across 8 CLIs.
  • tests/snapshots/default-output/ — humanized-prose snapshots for SC-5 default-output stability.

Changed

  • Default output of all 18 commands now uses plain-language descriptions. Findings group by user-impact category; titles lead with prose; technical IDs (CA-CML-001, CA-TOK-005, …) surface at end-of-line as references.
  • All 21 command and agent templates updated to render humanized output by default and pass --raw through when the user requests v5.0.0 verbatim mode.
  • CLI flag inventory — every CLI now accepts --raw (new) in addition to --json (existing, unchanged). --output-file <path> still writes raw v5.0.0-shape JSON regardless of mode (humanizer-bypassed, posture-specific).

Migration

  • No action required for existing automation that consumes --json — the JSON envelope shape is byte-stable with v5.0.0 and humanizer fields are bypassed in --json and --raw paths.
  • Tooling that scrapes stderr from default mode (e.g., posture.mjs's scorecard) needs review — default stderr now uses prose vocabulary. Pass --raw for byte-stable v5.0.0 verbatim stderr.
  • No scanner-internal changes. Finding IDs, severity ladders, scoring weights, and area scorecards are unchanged. Upgrades are presentation-layer only.

Test count

  • 635 → 792 tests across 52 test files (+157 humanizer-tester through Waves 05).
  • New top-level tests: json-backcompat.test.mjs, raw-backcompat.test.mjs, scenario-read-test.test.mjs, snapshot-default-output.test.mjs.
  • New lib tests: humanizer.test.mjs, humanizer-data.test.mjs, scoring-humanizer.test.mjs.
  • New scanner tests: posture-humanizer.test.mjs, scan-orchestrator-humanizer.test.mjs, cli-humanizer.test.mjs.

Out of scope (deferred to v5.1.1+)

  • Posture --output-file humanizationposture.mjs does not call humanizeEnvelope, so files written via --output-file are raw v5.0.0-shape JSON. Future revision: drop --output-file from command templates or add a --humanized-json flag.
  • Knowledge cross-references (Step 17 of plan) — not delivered per user decision (2a).
  • Scoring scorecard JSON headline emission — currently rendered prose-side only; command templates that want to skip stderr parsing would benefit.

Verification

  • 792/792 tests pass (node --test 'tests/**/*.test.mjs')
  • node scanners/self-audit.mjs --json --check-readme returns configGrade: A (97), pluginGrade: A (100), readmeCheck.passed: true
  • README badge updated: tests-635+tests-792+

[5.0.0] - 2026-05-01

Summary

Reality-based token-optimization release. v4.0.0 shipped Opus-4.7 token surfaces aligned to a Sonnet-era cost model; v5.0.0 rebuilds the foundations against verified Opus-4.7 cost dynamics. Three pillars: honest token estimation (severity-weighted scoring, MCP estimates 15 → 500+, optional --accurate-tokens API calibration), new structural scanners (cache-prefix stability, dead tool grants, plugin collisions), and new diagnostic surfaces (/config-audit manifest, /config-audit tokens extended, knowledge-base rensing aligned to Opus 4.7 cache dynamics).

Consolidated from 5.0.0-alpha.1 (F1-F5 token-economy round), 5.0.0-alpha.2 (M1, M2, M4-M6, F6, F7 structural gaps + README self-audit), 5.0.0-beta.1 (N1-N4, N6 new scanners + manifest CLI), and 5.0.0-rc.1 (M7, M8 knowledge rensing + N5 tokenizer calibration).

Added

  • 3 new scanners (9 → 12 deterministic):
    • CPS — Cache-Prefix Stability (CA-CPS-NNN): volatile content in lines 31150 of CLAUDE.md cascade, beyond TOK Pattern A's top-30 window. Volatile-pattern set extends Pattern A with shell-exec lines (! prefix) and ${VAR} substitutions.
    • DIS — Disabled-In-Schema (CA-DIS-NNN): tools listed in BOTH permissions.deny AND permissions.allow. Tool identity uses bare name (Bash(npm:*) and Bash are the same tool). Severity low.
    • COL — Cross-Plugin Skill Collision (CA-COL-001): plugin-vs-plugin same skill name → low; user-vs-plugin → medium. details.namespaces payload identifies conflicting sources.
  • TOK extensions:
    • CA-TOK-005 MCP tool-schema budget: per-server tiered finding (< 20 none, 2049 low, 5099 medium, 100+ high; null low + "tool count unknown"). Scoped to project-local .mcp.json.
    • Pattern E — Oversized cascade: medium when activeConfig.claudeMd.estimatedTokens > 10_000.
    • Pattern F — Bloated SKILL.md description: low when frontmatter description > 500 chars (loads every turn). Scoped to discovery.files.
  • /config-audit manifest + scanners/manifest.mjs CLI — single ranked table of every system-prompt token source (CLAUDE.md cascade, plugins, skills, MCP servers, hooks) sorted DESC by estimated_tokens. CLAUDE.md per-file tokens distributed proportional to bytes.
  • --accurate-tokens flag on token-hotspots-cli.mjs (N5): when ANTHROPIC_API_KEY is set, calls Anthropic's count_tokens for the top 3 hotspots and populates output.calibration = { actual_tokens, source: 'count_tokens_api', sampled_hotspots: 3 }. When absent: calibration = { skipped: 'no-api-key' } plus stderr warning.
  • scanners/lib/tokenizer-api.mjscount_tokens wrapper. 5s AbortController timeout. Exponential backoff on 429 (3 retries: 1s/2s/4s). API key masked to ${key.slice(0,8)}... in every error; HTTP body never included in errors (it may echo the key on auth failures). maskKey() exported.
  • --with-telemetry-recipe flag on the same CLI (M7): emits telemetry_recipe_path field pointing to knowledge/cache-telemetry-recipe.md.
  • knowledge/cache-telemetry-recipe.md (M7): manual jq recipe summing cache_read_input_tokens + cache_creation_input_tokens per turn from session transcripts. Hit-rate interpretation table.
  • 'mcp' kind on estimateTokens (F2): active MCP servers estimate ≥ 500 tokens (base + schema overhead) instead of v4's flat 15. Optional {toolCount} raises to 500 + toolCount × 200.
  • MCP tool-count detection (M1): readActiveMcpServers resolves count via cache → node_modules/<pkg>/package.json{toolCount: null, toolCountUnknown: true} fallback.
  • additionalDirectories settings key (M6): added to KNOWN_KEYS; new low-severity finding when length > 2.
  • HKV verbose hook output (M5): low-severity finding when referenced hook script contains > 50 console.log/process.stdout.write lines (static, no execution).
  • self-audit --check-readme flag (F6): filesystem counts compared against README badges. Helper checkReadmeBadges(pluginDir). Step 28 of v5 plan reconciled all badges.
  • scoringVersion: 'v5' field on scoreByArea output for cross-version drift detection.
  • WEIGHTS named export from scanners/lib/severity.mjs (frozen).
  • details field on findings (output.mjs:finding()): optional structured payload for scanner-specific data (used by COL).
  • Plugin Hygiene as 10th quality area (from COL). Posture JSON now reports 10 areas.
  • TOK-readActiveConfig integration (F1): one hotspot per active MCP server; result.activeConfig summary (claudeMd cascade tokens, mcpServerCount, pluginCount, skillCount); try/catch fallback when scope-limited.

Changed

  • F3 — scoreByArea is severity-weighted. Penalty = Σ count[s] × WEIGHTS[s]; passRate = max(0, 100 penalty / max(10, findingCount × 4) × 100). Lows no longer crater an area's grade; criticals/highs do. baseline-all-a fixture remains all-A (no critical/high present).
  • F7 — TOK pattern severities recalibrated for tokens-per-turn impact: Pattern A medium → high, Pattern B low → medium, Pattern C medium → low. Each finding carries a calibration_note evidence field documenting the heuristic basis.
  • scoreByArea deduplicates by area name (N3 prep): TOK + CPS share "Token Efficiency"; SET + DIS share "Settings". Combined row with merged finding counts.
  • M8 — knowledge rensing: replaced "Keep CLAUDE.md under 200 lines" in knowledge/configuration-best-practices.md with cache-stability guidance (first 30 lines stable, volatile content below the cache threshold). Footnote explains the 200-line rule was a Sonnet-era adherence heuristic; Opus 4.7 uses prompt-cache structure as the dominant cost lever. Cross-references knowledge/opus-4.7-patterns.md.
  • commands/tokens.md next-steps: documents --with-telemetry-recipe as the cache-verification path.
  • Scanner count: 9 → 12. Command count: 17 → 18. Knowledge: 7 → 8. Quality areas: 8 → 10.
  • .gitignore — unignore rules for tests/fixtures/**/node_modules/ so the mcp-tool-heavy fixture stays under version control.

Removed

  • F4 — TOK hotspot padding loop and take dead-code. Hotspots may now contain fewer than 3 entries for tiny projects (the honest answer); contract still bounds at ≤ 10.
  • F5 — Pattern D / CA-TOK-004 (sonnet-era signature). Catalogue entry removed from knowledge/opus-4.7-patterns.md and commands/tokens.md. Suppression entries for CA-TOK-004 are now no-ops.

Breaking changes

  • F2 — MCP token estimates jump from flat 15 to ≥ 500. Token Efficiency grades for projects with MCP servers may shift. whats-active totals report higher numbers. Documented in commands/posture.md next-steps.
  • F3 — scoreByArea is severity-weighted. Posture JSON consumers reading areas[*].score will see different values for non-clean configs. Use result.scoringVersion === 'v5' to detect the change. Drift comparisons across v4↔v5 baselines may show artificial deltas — re-baseline after upgrade.
  • F5 — Pattern D / CA-TOK-004 no longer emitted. Existing exact CA-TOK-004 suppression entries are harmless but obsolete.
  • N1 suppression backward-compat — CA-TOK-* glob now also matches CA-TOK-005. To preserve prior behavior of suppressing only patterns A/B/C, replace the glob with explicit IDs:
    CA-TOK-001
    CA-TOK-002
    CA-TOK-003
    
    A one-time runtime warning for this case is a v5.0.1 candidate.
  • Posture areas count: 9 → 10 (Plugin Hygiene from COL). Consumers hard-coding 9 must update.

Migration notes

  • CA-TOK-* glob suppressions: explicit-ID list recommended if CA-TOK-005 should not be suppressed.
  • CA-TOK-004 exact-ID suppression entries: safe to remove.
  • Drift baselines created against v4 should be re-saved post-upgrade to avoid artificial F3 weighting deltas.
  • Posture JSON consumers must update any hardcoded areas.length === 8 or === 9 assertions to >= 10.

Tests

  • 543 → 635 (+92): F1-F7 (alpha rounds = +43), N1-N4 + N6 (beta = +39), M7 + M8 + N5 (rc = +10). 36 test files (12 lib + 23 scanner + 1 hook).
  • New fixtures: tok-active-config/, additional-dirs-many/, additional-dirs-ok/, large-cascade/, small-cascade/, skill-bloated/, skill-tight/, mcp-tool-heavy/ (with mocked node_modules/), hooks-verbose/, hooks-quiet/, readme-desynced/, mcp-budget/{14,25,60,120,unknown}-tools/, volatile-mid-section/{volatile-line-60,volatile-line-200}/, denied-tools-in-schema/, collision-plugins/fake-home/ (plugin-a + plugin-b + plugin-c + user-level review skill).
  • New test files: tests/scanners/manifest.test.mjs, tests/scanners/cache-prefix.test.mjs, tests/scanners/disabled-in-schema.test.mjs, tests/scanners/collision.test.mjs, tests/scanners/accurate-tokens.test.mjs.

Notes

  • mock.method against ESM module exports does not work (Node 18+ ESM read-only export bindings). v5 tests use globalThis.fetch mocking for --accurate-tokens instead — equivalent coverage at the actual external-dependency boundary.
  • Plugin-vs-built-in collision detection is intentionally not implemented. Step 22a research spike (docs/v5-namespace-research.md, gitignored) could not verify Claude Code's resolution behavior when a plugin command shares a name with a built-in. Treated as info-only; v5.0.1 candidate.
  • README/CLAUDE.md badge reconciliation done in Step 28 (this release). self-audit --check-readme PASSES against the filesystem. Test count counter switched from file-count to test-case count via subprocess node --test parse.
  • hotspot.path exposed on file-backed hotspots (Step 30 fix). The rc.1 --accurate-tokens implementation looked up hotspot.path but the scanner only emitted source. File-backed hotspots now carry path (absolute path); MCP-server hotspots leave it unset (they are virtual entries representing runtime tool-schema cost, not file content).

SC-6b release-gate result (verified 2026-05-01)

  • PASS — 0.85% under-estimation against real count_tokens API.
  • Fixture: tests/fixtures/marketplace-large/. Top-3 hotspots = 1 file-backed (CLAUDE.md) + 2 MCP virtuals. MCP entries skipped per design (no readable content; their tokens are formula-based at 500 + toolCount × 200).
  • CLAUDE.md actual: 589 tokens (Anthropic count_tokens, claude-opus-4-7). Estimated: 594 tokens (byte heuristic at 4 bytes/token via estimateTokens). Delta: 5 tokens, 0.85% — well within the ±5% gate.
  • No tuning of estimateTokens heuristic required for v5.0.0.

[5.0.0-rc.1] - 2026-05-01

Summary

Release candidate for v5.0.0 — knowledge rensing and tokenizer calibration. Three deliverables: M8 (Sonnet-era → Opus 4.7 best-practices rewrite), M7 (cache-telemetry recipe in knowledge/ plus an opt-in CLI flag), and N5 (--accurate-tokens API calibration via Anthropic's count_tokens endpoint).

Added

  • N5 — --accurate-tokens flag on scanners/token-hotspots-cli.mjs. When ANTHROPIC_API_KEY is set, the CLI calls Anthropic's count_tokens endpoint for the top 3 hotspots and populates output.calibration = { actual_tokens, source: 'count_tokens_api', sampled_hotspots: 3 }. When the key is absent, calibration = { skipped: 'no-api-key' } and a stderr warning is emitted. Designed for the manual SC-6b release-gate verification, not routine use.
  • scanners/lib/tokenizer-api.mjs — wrapper around count_tokens with a 5-second AbortController timeout, exponential-backoff retry on HTTP 429 (max 3 retries: 1s, 2s, 4s), and required headers (x-api-key, anthropic-version: 2023-06-01, content-type). API key is masked to ${key.slice(0,8)}... in every error message and every thrown error; non-429 HTTP errors throw status code only — response body is never included (it may echo the key on auth failures). maskKey() is exported for callers that need safe logging.
  • M7 — knowledge/cache-telemetry-recipe.md (new). Manual jq recipe for verifying prompt-cache hit rate from Claude Code session transcripts (~/.claude/projects/<slug>/*.jsonl). Sums cache_read_input_tokens and cache_creation_input_tokens per turn and reports a hit-rate ratio. Recipe-form (not bundled scanner) keeps the project's "no transcript-parsing as core feature" non-goal intact while giving users a runtime escape hatch.
  • M7 — --with-telemetry-recipe flag on the same CLI. When passed, emits telemetry_recipe_path in the JSON output pointing to the recipe file. Without the flag, output is unchanged. Committed as a default deliverable, opt-in at invocation time.

Changed

  • M8 — knowledge-base rensing: replaced the "Keep CLAUDE.md under 200 lines" rule in knowledge/configuration-best-practices.md with cache-stability guidance (first 30 lines stable, volatile content below the cache threshold). Added a footnote that the 200-line rule was a Sonnet-era adherence heuristic; Opus 4.7 uses prompt-cache structure as the dominant cost lever. Cross-references knowledge/opus-4.7-patterns.md.
  • commands/tokens.md next-steps: documents --with-telemetry-recipe as the cache-verification path after a structural fix.

Tests

  • 625 → 635 (+10): --with-telemetry-recipe (×2), tokenizer-api unit tests (×6 — masking, body-leak protection, AbortController signal, 429 retry, header set, fetch mock happy path), --accurate-tokens no-key subprocess test (×1), absent-flag negative test (×1).
  • New file: tests/scanners/accurate-tokens.test.mjs. No new fixtures (re-uses marketplace-large).

Notes

  • SC-6b release gate is NOT closed by these commits. Step 26's tests use mocked globalThis.fetch to verify the integration contract; ±5% accuracy against real count_tokens requires a live API key and must be verified manually before tagging v5.0.0 in Session 5.
  • The plan's specified mock.method(tokenizerApi, 'callCountTokensApi', ...) pattern collides with ESM read-only export bindings in Node 18+. Tests mock at the globalThis.fetch boundary instead — equivalent coverage, no module-export rebinding required.
  • README/CLAUDE.md badge counts and plugin.json version still target v4.0.0; Step 28+29 will sync those during the release wrap.
  • [skip-docs] tag on the N5 feat commit; M7 and M8 are docs(...) commits and don't need it.

[5.0.0-beta.1] - 2026-05-01

Summary

First v5.0.0 beta — new scanners. Five new finding sources land: MCP tool-schema budget (CA-TOK-005), system-prompt manifest CLI/command (/config-audit manifest), cache-prefix stability (CPS), disabled-tools-still-in-schema (DIS), and cross-plugin/user-vs-plugin skill collision (COL/CA-COL-001). Plugin Hygiene becomes a 10th area-scorecard column.

Added

  • N1 — CA-TOK-005 MCP tool-schema budget: per-server tiered finding inside the TOK scanner. Thresholds — < 20 no finding, 2049 low, 5099 medium, 100+ high; null (manifest unparseable) low + "tool count unknown" message. Scoped to project-local .mcp.json to keep /config-audit <path> actionable. Recommendation links to the Step 25 cache-telemetry recipe.
  • N2 — /config-audit manifest: new slash command + scanners/manifest.mjs CLI. Renders a single ranked table of every token source (CLAUDE.md cascade, plugins, skills, MCP servers, hooks) sorted DESC by estimated_tokens. Reuses readActiveConfig; CLAUDE.md per-file tokens are distributed proportional to bytes.
  • N3 — CPS scanner (CA-CPS-NNN): Cache-Prefix Stability Analyzer. Walks the CLAUDE.md cascade and flags volatile content between lines 31 and 150 — beyond TOK Pattern A's top-30 territory. Volatile-pattern set extends Pattern A with shell-exec lines (! prefix) and ${VAR} substitutions. Severity medium per finding. Skips lines 130 (Pattern A's range).
  • N4 — DIS scanner (CA-DIS-NNN): Disabled-In-Schema Detector. Detects tools that appear in BOTH permissions.deny and permissions.allow within the same settings.json. The deny list wins, so allow entries are dead config but still load every turn. Tool identity is the bare name (everything before (); Bash(npm:*) and Bash are treated as the same tool. Severity low.
  • N6 — COL scanner (CA-COL-001): Cross-Plugin Skill Collision detector. Plugin-vs-plugin same skill name → low. User-vs-plugin same skill name → medium. Findings carry details.namespaces array with {source, name, path} for every conflicting source.
  • details field on findings: output.mjs:finding() helper now passes through optional details for scanner-specific structured payloads (used by COL).
  • "Plugin Hygiene" area (10th in scorecard): COL contributes here. Posture JSON now reports 10 areas instead of 9.

Changed

  • scoreByArea deduplicates by area name: when multiple scanners share an area (TOK + CPS → "Token Efficiency", SET + DIS → "Settings"), they produce one combined row with merged finding counts. Existing 9-area contract preserved for non-Plugin-Hygiene areas.

Known breaking changes

  • Suppression backward-compat — CA-TOK-* glob now also matches CA-TOK-005. Existing .config-audit-ignore entries that suppress TOK findings via the CA-TOK-* glob will silently include CA-TOK-005 (MCP budget). To preserve the prior behavior of suppressing only patterns A/B/C, replace the glob with explicit IDs:
    CA-TOK-001
    CA-TOK-002
    CA-TOK-003
    
    A one-time runtime warning for this case is out of scope for v5.0.0 — it is a candidate for v5.0.1.
  • Plugin-vs-built-in collision is intentionally not implemented. The Step 22a research spike could not verify Claude Code's resolution behavior when a plugin command shares a name with a built-in (/help, /clear, /init, /review, /config, /cost, /security-review). Treated as info-only in this release; a follow-up v5.0.1 ticket may add an opt-in check.

Tests

  • 586 → 625 (+39): N1 (×7), N2 (×11), N3 (×7), N4 (×6), N6 (×8).
  • New fixtures: mcp-budget/{14,25,60,120,unknown}-tools/, volatile-mid-section/{volatile-line-60,volatile-line-200}/, denied-tools-in-schema/, collision-plugins/fake-home/ (plugin-a + plugin-b + plugin-c + user-level review skill).

Notes

  • [skip-docs] tag used on every feat commit — README/CLAUDE.md badge counts (scanner count, command count, test count) and the architecture sections are intentionally fenced off until Session 5 (Step 28). This keeps the v5 plan's session boundaries clean even when the Forgejo pre-commit-docs-gate hook would otherwise block these commits.

[5.0.0-alpha.2] - 2026-05-01

Summary

Second v5.0.0 alpha — structural gaps + README self-audit. TOK pattern severities recalibrated for tokens/turn impact (F7), three new findings cover settings/skills/cascade structure (M2, M4, M6), MCP tool-count detection wired (M1), HKV gains a verbose-output check (M5), and self-audit grows a --check-readme flag (F6).

Added

  • F7 — TOK severity recalibration: Pattern A (cache-breaking volatile top) medium → high, Pattern B (redundant permissions) low → medium, Pattern C (deep imports) medium → low. Each finding now carries a calibration_note evidence field documenting the heuristic basis.
  • M6 — additionalDirectories settings key: added to KNOWN_KEYS so it no longer trips "unknown settings key". New low-severity finding when additionalDirectories.length > 2.
  • M4 — TOK Pattern E: medium-severity finding when activeConfig.claudeMd.estimatedTokens > 10_000 — flags cascades that bleed budget every turn.
  • M2 — TOK Pattern F: low-severity finding for project-local SKILL.md whose frontmatter description exceeds 500 characters (description loads on every turn even when the body does not). Scoped to discovery.files; user/plugin skills out of project scope are not flagged.
  • M1 — MCP tool-count detection: readActiveMcpServers now resolves tool count via cache → node_modules/<pkg>/package.json{toolCount: null, toolCountUnknown: true} fallback. Tool count drives estimateTokens per server.
  • M5 — HKV verbose hook output: new low-severity finding when a referenced hook script contains > 50 console.log / process.stdout.write lines (static heuristic, no execution).
  • F6 — self-audit --check-readme flag: filesystem counts (scanners, commands, agents, hooks, tests, knowledge) compared against README badge values. Helper export: checkReadmeBadges(pluginDir).

Changed

  • TOK severities (F7) — see Added. Posture aggregates that depended on Pattern A being medium will now reflect the higher-impact rating.
  • .gitignore — added unignore rules so tests/fixtures/**/node_modules/ are tracked. Required by the mcp-tool-heavy fixture.

Tests

  • 563 → 586 (+23): F7 table-driven (×6), M6 (×3), M4 (×2), M2 (×2), M1 (×4), M5 (×2), F6 (×4).
  • New fixtures: additional-dirs-many/, additional-dirs-ok/, large-cascade/, small-cascade/, skill-bloated/, skill-tight/, mcp-tool-heavy/ (with mocked node_modules/), hooks-verbose/, hooks-quiet/, readme-desynced/.

Notes

  • result.readmeCheck.passed === true is not required during alpha/beta phases. The real plugin's own check is currently red (scanners 10 vs README 9, tests 31 vs README 543) — reconciliation deferred to Session 5 Step 28 (README sync).
  • [skip-docs] tag used on every commit — README/CLAUDE.md badge counts and architecture text are intentionally fenced off until Session 5.

[5.0.0-alpha.1] - 2026-05-01

Summary

First v5.0.0 alpha — token-economy round, F1-F5. The TOK scanner now consumes readActiveConfig (per-MCP-server hotspots, claudeMd cascade tokens), severity weighting replaces flat finding counts in scoreByArea, and MCP servers no longer estimate at a flat 15 tokens. Pattern D (CA-TOK-004 sonnet-era signature) removed — too noisy, not actionable.

Added

  • 'mcp' kind for estimateTokens (F2): an active MCP server now estimates ≥ 500 tokens (base protocol + schema overhead) instead of the v4 flat 15. Optional {toolCount} raises the estimate to 500 + toolCount * 200 once Step 14 wires tool-count detection.
  • TOK ↔ readActiveConfig integration (F1): the TOK scanner emits one hotspot per active MCP server, sums their tokens into total_estimated_tokens, and exposes result.activeConfig (claudeMd cascade tokens, mcpServerCount, pluginCount, skillCount).
  • scoringVersion: 'v5' field on scoreByArea output for cross-version drift detection.
  • WEIGHTS named export from scanners/lib/severity.mjs (Object.freeze).

Changed

  • BREAKING (intentional, F3): scoreByArea is now severity-weighted. Penalty = Σ count[s] * WEIGHTS[s]; passRate = max(0, 100 - penalty / max(10, findingCount * 4) * 100). Lows no longer crater an area's grade; a single high or critical consumes a large fraction of budget. baseline-all-a fixture remains all-A (no critical/high on that fixture).
  • BREAKING (intentional, F2): MCP server token estimates jump from a flat 15 to ≥ 500. whats-active totals and TOK hotspots will report higher numbers for any project with active MCP servers.
  • BREAKING (intentional, F5): Pattern D / CA-TOK-004 (sonnet-era signature) is no longer emitted. Suppression entries for CA-TOK-004 are now no-ops; downstream tools that filter on the ID should drop it. The catalogue entry was removed from knowledge/opus-4.7-patterns.md and commands/tokens.md.
  • Hotspots contract (F4): the v4 padding loop and take dead-code are gone. Hotspots may now contain fewer than 3 entries for tiny projects (the honest answer); contract still bounds at ≤ 10.

Migration notes

  • CA-TOK-* glob suppression entries continue to suppress 001-003. Existing exact CA-TOK-004 entries are harmless but obsolete — remove them at convenience.
  • Posture/JSON consumers reading areas[*].score will see different values for non-clean configs. Use result.scoringVersion === 'v5' to detect.

Tests

  • 543 → 563 across the alpha.1 commits (+9 severity-weighting/scoring, +4 estimateTokens 'mcp', +1 MCP caller migration, +3 readActiveConfig integration, +2 hotspots-uniqueness, +2 sonnet-era zero-finding).
  • New fixture tests/fixtures/tok-active-config/ — minimal repo with .mcp.json (2 servers), CLAUDE.md, plugin skeleton.

[4.0.0] - 2026-04-19

Summary

Opus 4.7 era upgrade. New TOK scanner detects token-efficiency anti-patterns (cache-breaking volatile content, redundant tool permissions, deep import chains, sonnet-era minimal setups). Token Efficiency joins the quality scorecard as the 8th area. Scanner-agent and verifier-agent migrate from haiku → sonnet per global no-haiku policy.

Added

  • token-hotspots.mjs scanner (CA-TOK-001..004) — 4 patterns aligned with Opus 4.7 token-cost dynamics:
    • CA-TOK-001 cache-breaking volatile content (timestamps/UUIDs in top 30 lines of CLAUDE.md)
    • CA-TOK-002 redundant tool permissions (duplicate or subset overlaps)
    • CA-TOK-003 deep @import chains (>2 hops on the load path)
    • CA-TOK-004 sonnet-era minimal setup (no skills/MCP/hooks/managed/plugins)
  • /config-audit tokens [path] [--global] — ranked hotspot table + per-pattern findings.
  • scanners/token-hotspots-cli.mjs — standalone CLI emitting total_estimated_tokens, hotspots, and per-finding output.
  • Token Efficiency as the 8th quality area in the posture scorecard (now 9 scanners total: CML/SET/HKV/RUL/MCP/IMP/CNF/GAP/TOK).
  • id field on every area in the scorecard payload (token_efficiency, instruction_clarity, etc.) for stable downstream lookup.
  • 13 new TOK scanner tests + 3 CLI tests + posture grade-stability test for token_efficiency.
  • Knowledge refresh: knowledge/opus-4.7-patterns.md, plus 2026-04 deltas (v2.1.83v2.1.111) added to feature-evolution.md, claude-code-capabilities.md, and hook-events-reference.md from research/03-claude-code-changes-config-surfaces.md.

Changed

  • BREAKING (additive surface): Quality areas count 7 → 8. Posture JSON consumers that hard-coded 7 areas must update.
  • BREAKING (model migration): scanner-agent and verifier-agent migrated haikusonnet. Latency and cost trade-offs accepted; deterministic scanner CLIs preferred over agent invocations.
  • Scanner count: 8 → 9 (TOK added).
  • Command count: 16 → 17 (/config-audit tokens added).
  • Version bump: 3.1.04.0.0.

[3.1.0] - 2026-04-14

Summary

New read-only command /config-audit whats-active — shows exactly what Claude Code loads for a given repo, with token estimates.

Added

  • /config-audit whats-active [path] — inventory of active plugins, skills, MCP servers, hooks, and CLAUDE.md cascade for a repo, with source attribution (user/project/plugin) and rough token estimates. Read-only, <2s.
  • scanners/lib/active-config-reader.mjs — pure async helper: readActiveConfig(), detectGitRoot(), walkClaudeMdCascade(), readClaudeJsonProjectSlice() (longest-prefix matching), enumeratePlugins(), enumerateSkills(), readActiveHooks(), readActiveMcpServers(), estimateTokens().
  • scanners/whats-active.mjs — thin CLI shim supporting --json, --output-file, --verbose, --suggest-disables.
  • Optional --suggest-disables flag surfaces deterministic disable candidates (disabled MCP servers, zero-item plugins, unreferenced plugins, orphan skills) and invites an LLM judgment pass in the command.
  • 36 new tests in tests/lib/active-config-reader.test.mjs, plus a rich-repo tmpdir fixture helper.

Changed

  • Version bump: 3.0.13.1.0 (minor, additive feature, no breaking changes).
  • Command count: 15 → 16.

[3.0.1] - 2026-04-04

Summary

Cross-platform fix — scanners, hooks, and lib now work correctly on Windows.

Fixed

  • file-discovery.mjs: depth calculation, agent/command/plugin path matching now use path.sep
  • scan-orchestrator.mjs: fixture-path filtering now uses path.sep
  • post-edit-verify.mjs: rules-dir regex handles both / and \ separators
  • auto-backup-config.mjs: rules-dir detection now uses path.sep
  • import-resolver.mjs: circular import display uses basename(), /tmp fallback replaced with os.tmpdir()
  • string-utils.mjs: normalizePath trailing separator regex handles both / and \

Added

  • 4 cross-platform path tests (total 486 tests)

[3.0.0] - 2026-04-04

Summary

Health redesign — configuration health is now quality-only. Feature utilization removed from grades entirely.

Changed

  • Health = quality only. 7 deterministic scanners (CML, SET, HKV, RUL, MCP, IMP, CNF) determine your grade. Feature Coverage is no longer a graded area.
  • Feature recommendations are opt-in. Unused features shown as "opportunities" via /config-audit feature-gap, grouped by impact (high/medium/explore), backed by Anthropic docs. No more "Feature Coverage: F" for correct minimal setups.
  • Posture output redesigned. Shows Health: {grade} ({score}/100) with 7 quality areas. Removed utilization %, maturity level, segment label.
  • Feature-gap is interactive. Users select recommendations to implement directly — no manual file editing required. Backup created automatically.
  • avgScore bug fixed. Grade letter and displayed score now computed from the same population (quality areas only).

Added

  • generateHealthScorecard() in scoring.mjs — quality-only scorecard
  • opportunitySummary() in feature-gap-scanner.mjs — groups findings by impact tier
  • opportunityCount field in posture JSON output
  • "Official Configuration Guidance" section in knowledge base (Anthropic docs, proven impacts)
  • 21 new tests (total 482 across 27 test files)

Removed

  • S2-PROMPT.md and V2-ANNOUNCEMENT.md — v2 development artifacts
  • Utilization %, maturity level, segment label from posture terminal output and reports
  • Feature Coverage row from area breakdown tables
  • "Top Actions" sourced from GAP findings (replaced by opportunities pointer)

Backward Compatibility

  • JSON output preserves all legacy fields (utilization, maturity, segment) for programmatic consumers
  • Drift baselines unaffected — GAP findings still present in envelopes
  • All existing exports maintained (calculateUtilization, determineMaturityLevel, etc.)

[2.2.0] - 2026-04-04

Summary

UX quality fix — fixture filtering, session path migration, output polish.

Added

  • Automatic test-fixture filtering in scan-orchestrator: findings from tests/, examples/, __tests__/ excluded from grades, stored in env.fixture_findings
  • --include-fixtures CLI flag for scan-orchestrator and posture to override filtering
  • scan-orchestrator.test.mjs — 20 new tests for fixture filtering and isFixturePath
  • Legacy session path detection in cleanup command

Changed

  • Session storage moved from ~/.config-audit/ to ~/.claude/config-audit/ (pathguard compatible)
  • Self-audit grade: F → A (98) after fixture filtering
  • Combined scanner + posture into single Bash call in default audit command
  • Removed "F grade is misleading" disclaimer — grades are now accurate
  • All CLI banners and envelope metadata updated to v2.2.0
  • 461 tests (up from 441), 27 test files (up from 26)

Removed

  • Manual fixture counting instruction in config-audit.md (orchestrator handles it)
  • Redundant isFixtureOrExample filter in self-audit.mjs (promoted to orchestrator)

[2.1.0] - 2026-04-03

Summary

UX redesign — auto-scope detection, zero questions, simplified command surface.

Changed

  • /config-audit now runs full audit automatically (auto-detects scope from git context)
  • Removed mode selection prompts — scope override via /config-audit full|repo|home|current
  • Simplified from 17 to 15 commands (removed quick, report, watch; added help)
  • All CLI banners and envelope metadata updated to v2.1.0

Added

  • /config-audit help command with categorized command reference
  • Auto-scope detection from git context (repo vs home vs full-machine)

Removed

  • /config-audit:quick (merged into default /config-audit)
  • /config-audit:report (merged into analyze output)
  • /config-audit:watch (use /config-audit drift instead)

[2.0.0] - 2026-04-03 (v2.0 Complete)

Summary

Complete rewrite from LLM-only prototype to deterministic scanner-backed configuration intelligence. 7 development sessions (S1-S7), ~15,000 lines of code, 408+ tests.

Highlights

  • 8 deterministic scanners (CML, SET, HKV, RUL, MCP, IMP, CNF, GAP) + PLH standalone
  • Feature gap analysis with 25 dimensions across 4 tiers
  • Auto-fix engine with 9 fix types + backup/rollback
  • Drift detection with baseline comparison
  • Suppression engine (.config-audit-ignore)
  • Self-audit CLI
  • 17 commands, 6 agents, 4 hooks
  • 408+ tests (zero external dependencies)

Added (S7)

  • Example projects: examples/minimal-setup/ and examples/optimal-setup/
  • Demo script: examples/run-demo.sh
  • .config-audit-ignore for self-audit suppressions
  • V2-ANNOUNCEMENT.md
  • DEPRECATED.md for capability-auditor skill

Fixed (S7)

  • hooks.json: SessionStart and Stop timeout 5ms → 5000ms
  • self-audit.mjs: Suppression now enabled (was hardcoded to suppress: false)

Changed (S7)

  • README.md: Complete rewrite for public release
  • CLAUDE.md: Added Suppressions section
  • .gitignore: Added node_modules/ and S*-PROMPT.md

[1.6.0] - 2026-04-03 (v2.0 S6: Unified Reports + Self-Audit + Suppressions)

Added

  • Report generator scanners/lib/report-generator.mjs — unified markdown reports: generatePostureReport(), generateDriftReport(), generatePluginHealthReport(), generateFullReport()
  • Suppression engine scanners/lib/suppression.mjs.config-audit-ignore file support with exact IDs and glob patterns (CA-SET-*), audit trail via suppressed_findings in envelope
  • Self-audit CLI scanners/self-audit.mjs — runs all scanners + plugin health on this plugin: node self-audit.mjs [--json] [--fix], exit codes 0/1/2
  • PostToolUse hook post-edit-verify.mjs — verifies config files after Edit/Write, blocks if new critical/high findings introduced
  • New command: /config-audit:report — generate unified report (posture + optional drift/plugin-health)
  • Test fixture .config-audit-ignore in fixable-project
  • 54 new tests (total 408 across 25 test files)

Changed

  • scan-orchestrator.mjs: suppression integration — applies .config-audit-ignore after all scanners run, --no-suppress flag to disable
  • hooks.json: added PostToolUse event with post-edit-verify

[1.5.0] - 2026-04-03 (v2.0 S5: Drift + Watch + Plugin Health)

Added

  • Diff engine scanners/lib/diff-engine.mjs — diffEnvelopes() comparing baseline vs current, formatDiffReport() for terminal output
  • Baseline manager scanners/lib/baseline.mjs — save/load/list/delete named baselines in ~/.claude/config-audit/baselines/
  • Drift CLI scanners/drift-cli.mjs — standalone: node drift-cli.mjs <path> [--save] [--baseline name] [--json] [--list]
  • Plugin health scanner scanners/plugin-health-scanner.mjs (PLH) — validates plugin structure, frontmatter, cross-plugin conflicts (runs independently, not in scan-orchestrator)
  • 3 new commands:
    • /config-audit:drift — compare current config against saved baseline
    • /config-audit:watch — on-demand drift check with baseline monitoring
    • /config-audit:plugin-health — audit plugin structure and cross-plugin coherence
  • Test fixtures test-plugin/ (valid) and broken-plugin/ (invalid) for plugin health tests
  • 48 new tests (total 354 across 21 test files)

[1.4.0] - 2026-04-03 (v2.0 S4: Fix + Rollback Action Pillar)

Added

  • Fix engine scanners/fix-engine.mjs — deterministic auto-fix for 9 fix types:
    • json-key-add (missing $schema), json-key-remove (deprecated keys), json-key-type-fix (type mismatches, invalid effortLevel), json-restructure (hooks array→object, matcher object→string), frontmatter-rename (globs→paths), file-rename (non-.md→.md)
  • Rollback engine scanners/rollback-engine.mjs — listBackups(), restoreBackup(), deleteBackup() with checksum verification
  • Fix CLI scanners/fix-cli.mjs — standalone: node fix-cli.mjs <path> [--apply] [--json] [--global], dry-run by default
  • Backup lib scanners/lib/backup.mjs — shared backup module with checksums and manifests
  • 2 new commands:
    • /config-audit:fix — scan, plan, backup, apply, verify in one flow
    • /config-audit:rollback — list or restore from backups
  • PreToolUse hook auto-backup-config.mjs — auto-backup config files before Edit/Write
  • Test fixture fixable-project/ — fixture with all 9 fixable issue types
  • 38 new tests (total 306 across 17 test files)

Changed

  • file-discovery.mjs: walkRulesDir now discovers all files (not just .md) for non-.md validation
  • backup-before-change.mjs: refactored to use shared lib/backup.mjs (no logic duplication)
  • hooks.json: added PreToolUse event with auto-backup

[1.3.0] - 2026-04-03 (v2.0 S3: Posture + Feature Gap Commands)

Added

  • Scoring module scanners/lib/scoring.mjs — utilization, maturity (5 levels), segments, area scoring, scorecard generation
  • Posture CLI scanners/posture.mjs — standalone Node.js tool: node posture.mjs <path> [--json] [--global]
  • 2 new commands:
    • /config-audit:posture — quick scorecard with A-F grades, utilization%, maturity level
    • /config-audit:feature-gap — deep gap analysis with prioritized next-best-actions
  • feature-gap-agent — Opus agent for deep analysis, report generation (max 200 lines)
  • Knowledge file gap-closure-templates.md — 11 templates with effort/gain estimates
  • HTML report template templates/feature-gap-report.html — visual report with progress bars, grade badges
  • 64 new tests (total 268 across 14 test files)

Changed

  • Tier weighting: T1 gaps count 3x, T2 count 2x, T3/T4 count 1x in utilization score
  • Maturity is threshold-based: highest level where ALL requirements are met

[1.2.0] - 2026-04-03 (v2.0 S2: Advanced Scanners + Knowledge Base)

Added

  • 4 advanced scanners (zero external deps):
    • mcp-config-validator.mjs (MCP) — server types, trust levels, env vars, unknown fields
    • import-resolver.mjs (IMP) — broken @imports, circular refs, deep chains, tilde paths
    • conflict-detector.mjs (CNF) — settings conflicts, permission contradictions, hook duplicates
    • feature-gap-scanner.mjs (GAP) — 25 feature gaps across 4 tiers (Foundation/Depth/Advanced/Enterprise)
  • Knowledge base — 5 reference documents: capabilities, best practices, anti-patterns, hook events, feature evolution
  • New test fixtures.mcp.json files, @import chains, conflict-project/ fixture
  • 75 new tests (total 204 across 12 test files)

Changed

  • Scan orchestrator runs 8 scanners (was 4)
  • Analyzer agent cross-references scanner findings with knowledge base

[1.1.0] - 2026-04-03 (v2.0 S1: Scanner Foundation)

Added

  • Deterministic scanner infrastructure — 4 Node.js scanners (zero external deps):
    • claude-md-linter.mjs (CML) — CLAUDE.md structure, length, sections, @imports, duplicates
    • settings-validator.mjs (SET) — settings.json schema, unknown/deprecated keys, type checks
    • hook-validator.mjs (HKV) — hooks.json format, script existence, event validity, timeouts
    • rules-validator.mjs (RUL) — .claude/rules/ glob matching, orphan detection, deprecated fields
  • Scanner lib — 5 shared modules: severity, output, file-discovery, yaml-parser, string-utils
  • Scan orchestratorscan-orchestrator.mjs runs all scanners, outputs JSON envelope
  • Test infrastructure — 129 tests across 8 test files using node:test (zero deps)
  • Test fixtures — 4 fixture projects (healthy, broken, empty, minimal)
  • Finding ID format: CA-{SCANNER}-{NNN} (e.g. CA-CML-001)

Fixed

  • Agent model mismatches: scanner→haiku, analyzer→sonnet, planner→opus, implementer→sonnet, verifier→haiku

Changed

  • CLAUDE.md rewritten in English for public release readiness

[1.0.0] - 2026-02-11

Added

  • Cross-platform support (macOS, Linux, Windows)

Fixed

  • stop-session-reminder.mjs: Use path.basename/path.dirname instead of hardcoded / split
  • backup-before-change.mjs: Handle both / and \ path separators in safe filename generation

Removed

  • "Windows: hooks are 100% bash" from known gaps (was incorrect — all hooks are Node.js)

[0.7.0] - 2026-02-07

Note

Version reset from 1.2.0 to reflect actual maturity. Previous version was inflated — this plugin has never been externally tested.

What exists today

  • 6 specialized agents (scanner, analyzer, interviewer, planner, implementer, verifier)
  • Full machine-wide Claude Code configuration discovery
  • Scope selection (current project, repo, home, full machine)
  • Inheritance hierarchy mapping and conflict detection
  • Mandatory backups before any changes
  • Rollback support
  • Syntax validation for all configuration files
  • Quick audit-only mode
  • Full optimization workflow with HITL checkpoints

Known gaps

  • Testing: no automated tests
  • Onboarding: never verified that a new user can install and use from scratch
  • External verification: nobody else has ever used this