From 42e48514e40ad1a4518ef2dc2ea169d42e72faac Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Tue, 23 Jun 2026 18:30:09 +0200 Subject: [PATCH] =?UTF-8?q?release:=20v5.9.0=20=E2=80=94=20"Machine-wide?= =?UTF-8?q?=20token=20lens"=20(B1+B2+B3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cuts the v5.9.0 release bundling the three hardening gaps shipped since v5.8.0: B1 (agent-listing budget — new orchestrated scanner AGT, count 15→16), B2 (machine-wide always-loaded token roll-up in the campaign ledger), and B3 (cache-aware filtering + stale plugin-cache disk-cleanup finding). Version sync: plugin.json 5.8.0→5.9.0; README badges version 5.9.0 / scanners 15→16 / tests 1168→1215; README intro + Health prose + scanner table (new AGT row, TOK 7th pattern) + self-audit prose 15→16; new version-history row; CHANGELOG [5.9.0]; CLAUDE.md finding-ID example (+CA-AGT) and test count 1168→1215 / 67→68 files (scanner dir 35→36). Gate: `self-audit --json --check-readme` -> readmeCheck.passed:true, mismatches:[]. Full suite green (1215, 0 fail). Tag + catalog ref-bump follow. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude-plugin/plugin.json | 2 +- CHANGELOG.md | 56 ++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 4 +-- README.md | 16 ++++++----- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index e875504..132f2bd 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "config-audit", "description": "Multi-agent workflow for analyzing, reporting, and optimizing Claude Code configuration across your entire machine", - "version": "5.8.0", + "version": "5.9.0", "author": { "name": "Kjell Tore Guttormsen" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ba1820..ac95a4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,62 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [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 + (`readActiveConfig` → `buildManifest` → 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 diff --git a/CLAUDE.md b/CLAUDE.md index 647cdda..c86d5b3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -105,7 +105,7 @@ Default: auto-detects scope from git context. Override with `/config-audit full| ``` ### Finding ID Format -`CA-{SCANNER}-{NNN}` — e.g. `CA-CML-001`, `CA-SET-003`, `CA-HKV-002`, `CA-RUL-005`, `CA-TOK-005`, `CA-CPS-001`, `CA-DIS-001`, `CA-COL-001`, `CA-SKL-001`, `CA-OST-001`, `CA-OPT-001` +`CA-{SCANNER}-{NNN}` — e.g. `CA-CML-001`, `CA-SET-003`, `CA-HKV-002`, `CA-RUL-005`, `CA-TOK-005`, `CA-CPS-001`, `CA-DIS-001`, `CA-COL-001`, `CA-SKL-001`, `CA-OST-001`, `CA-OPT-001`, `CA-AGT-001` ## Conventions @@ -123,7 +123,7 @@ Coding style: scanners are zero-dependency Node ESM; new findings use the `CA-{S node --test 'tests/**/*.test.mjs' ``` -1168 tests across 67 test files (22 lib + 35 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`. +1215 tests across 68 test files (22 lib + 36 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`. Per-scanner and per-build-block implementation notes (design rationale, primary-source verification, byte-stability lessons) live in `docs/scanner-internals.md` → **Implementation notes**. diff --git a/README.md b/README.md index 11ee166..6aa108a 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,16 @@ *AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)* -![Version](https://img.shields.io/badge/version-5.8.0-blue) +![Version](https://img.shields.io/badge/version-5.9.0-blue) ![Platform](https://img.shields.io/badge/platform-Claude_Code_Plugin-purple) -![Scanners](https://img.shields.io/badge/scanners-15-cyan) +![Scanners](https://img.shields.io/badge/scanners-16-cyan) ![Commands](https://img.shields.io/badge/commands-21-green) ![Agents](https://img.shields.io/badge/agents-7-orange) ![Hooks](https://img.shields.io/badge/hooks-4-red) -![Tests](https://img.shields.io/badge/tests-1168+-brightgreen) +![Tests](https://img.shields.io/badge/tests-1215+-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. 15 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, cross-plugin collision, and output-style detection. Zero external dependencies. +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. 16 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, cross-plugin collision, output-style, and always-loaded agent-listing-budget detection. Zero external dependencies. --- @@ -76,7 +76,7 @@ Claude Code reads instructions from at least 7 different file types across multi This plugin provides three layers of configuration intelligence: -- **Health** — 15 deterministic scanners verify correctness across every configuration file, catching broken imports, deprecated settings, conflicting rules, format errors, permission contradictions, prompt-cache token waste, cache-prefix instability, dead tool grants, cross-plugin skill collisions, output styles that silently strip Claude Code's coding instructions, and procedures in CLAUDE.md that would fit better as a skill +- **Health** — 16 deterministic scanners verify correctness across every configuration file, catching broken imports, deprecated settings, conflicting rules, format errors, permission contradictions, prompt-cache token waste, cache-prefix instability, dead tool grants, cross-plugin skill collisions, output styles that silently strip Claude Code's coding instructions, an oversized always-loaded agent listing, and procedures in CLAUDE.md that would fit better as a skill - **Opportunities** — context-aware recommendations for Claude Code features that could benefit your specific project, backed by Anthropic's official guidance - **Action** — auto-fix with mandatory backups, syntax validation, rollback support, and a human-in-the-loop workflow for anything non-trivial @@ -322,13 +322,14 @@ By default, `/config-audit` auto-detects scope from your git context. Override w | `import-resolver.mjs` | IMP | Broken @imports, circular references, deep chains, tilde path issues | | `conflict-detector.mjs` | CNF | Settings contradictions across scopes, permission conflicts, hook duplicates | | `feature-gap-scanner.mjs` | GAP | 25 feature checks shown as opportunities, not grades — plus a conditional `disableBundledSkills` recommendation when the active skill listing is over budget | -| `token-hotspots.mjs` | TOK | Cache-breaking volatile content, redundant tool permissions, deep import chains, oversized cascades, bloated skill descriptions, MCP tool-schema budget | +| `token-hotspots.mjs` | TOK | Cache-breaking volatile content, redundant tool permissions, deep import chains, oversized cascades, bloated skill descriptions, MCP tool-schema budget, and stale `~/.claude/plugins/cache` versions (disk-cleanup, zero live-context impact) — cache-aware ranking excludes superseded plugin versions by default (`--no-exclude-cache` to include) | | `cache-prefix-scanner.mjs` | CPS | Volatile content in lines 31–150 of the CLAUDE.md cascade — beyond the cache-prefix window but still re-loaded every turn | | `disabled-in-schema-scanner.mjs` | DIS | Dead/ineffective permission entries: (1) tools in BOTH `permissions.deny` and `permissions.allow` — deny wins (incl. the `Tool(*)` deny-all glob, equivalent to a bare deny); (2) unanchored allow wildcards (`*`, `B*`, `mcp__*`) that Claude Code silently skips — valid only as `mcp____*`; (3) `Tool(param:value)` rules whose key is the tool's own canonicalizing field (`command`/`file_path`/`path`/`notebook_path`/`url`) — CC ignores these and emits a startup warning | | `collision-scanner.mjs` | COL | Cross-plugin skill name collisions; user-vs-plugin overlaps | | `skill-listing-scanner.mjs` | SKL | Skill-listing token budget: a single skill description over the ~1,536-char listing cap Claude Code truncates (`CA-SKL-001`), and the summed active-skill descriptions exceeding the ~2%-of-context listing budget (`CA-SKL-002`) | | `output-style-scanner.mjs` | OST | Output-style validation: a custom (user/project) style missing `keep-coding-instructions: true` that silently strips built-in software-engineering instructions (`CA-OST-001`), a plugin style with `force-for-plugin: true` overriding the user's selected `outputStyle` (`CA-OST-002`), and a settings `outputStyle` resolving to no built-in or custom style — dead config (`CA-OST-003`) | | `optimization-lens-scanner.mjs` | OPT | Optimization lens (mechanism-fit): a multi-step procedure in CLAUDE.md that would fit better as a skill (`CA-OPT-001`) — reads the machine-readable best-practices register, framed as an opportunity, not a failure. The deterministic half of the lens; prose-judgment cases (lifecycle→hook, unscoped path→rule, "never"→permission) are judged by the opus `optimization-lens-agent` via `/config-audit optimize` | +| `agent-listing-scanner.mjs` | AGT | Always-loaded agent-listing budget: a per-agent description over the soft bloat cap (`CA-AGT-001`, advisory) and the summed active-agent name+description listing — re-sent every turn — exceeding the listing budget (`CA-AGT-002`). Both LOW and explicitly **inferred / upper-bound**: the agent-listing mechanism is undocumented, so the evidence discloses the estimate and heuristic budget rather than overstating certainty | > **Cross-scanner remediation — diagnosis meets the fix.** SKL diagnoses an over-budget > skill listing (`CA-SKL-002`); GAP prescribes the remedy. When the active skill listing @@ -529,7 +530,7 @@ node scanners/posture.mjs examples/optimal-setup/ ### Self-Audit: Scanning the Scanner -The plugin runs all 15 scanners + the standalone plugin-health scanner on itself via `self-audit.mjs`. Test fixtures and example files are automatically excluded from scoring — a configuration plugin that ships deliberately broken examples shouldn't fail its own audit. Use `--check-readme` to verify badge counts are in sync with the filesystem. +The plugin runs all 16 scanners + the standalone plugin-health scanner on itself via `self-audit.mjs`. Test fixtures and example files are automatically excluded from scoring — a configuration plugin that ships deliberately broken examples shouldn't fail its own audit. Use `--check-readme` to verify badge counts are in sync with the filesystem. ```bash node scanners/self-audit.mjs @@ -661,6 +662,7 @@ This plugin is cautious by design — configuration files are important, and a b | Version | Date | Highlights | |---------|------|-----------| +| **5.9.0** | 2026-06-23 | "Machine-wide token lens" — the three highest-impact hardening gaps toward whole-machine token tuning. **B1 — agent-listing budget (new orchestrated scanner AGT, count 15→16):** the always-loaded agent listing (name+description re-sent every turn) is now measured — `CA-AGT-001` per-agent description bloat (advisory), `CA-AGT-002` aggregate listing over budget; both LOW and explicitly **inferred / upper-bound** (the mechanism is undocumented — the evidence discloses it rather than overstating). **B2 — machine-wide always-loaded token roll-up:** the campaign ledger now carries a token bill — `campaign refresh-tokens` does a live cross-repo sweep that counts the **shared global always-loaded layer once** + per-repo deltas, with a ranked "most expensive repos" table (the `whats-active` double-count, avoided by construction). **B3 — cache-aware filtering (folds in B0):** `~/.claude/plugins/cache` holds *both* active and stale plugin versions (installPaths point INTO it), so token-hotspots + CNF are now **version-aware** — `--exclude-cache` (default ON) keeps each plugin's active version and drops only stale ones (`installed_plugins.json`-driven), so stale versions stop polluting the hotspot ranking and inflating duplicate-hook conflicts; stale versions surface as a separate **Dead config** disk-cleanup finding (zero live-context impact). `--json`/`--raw` byte-stable; frozen v5.0.0 + SC-5 snapshots untouched. 1215 tests | | **5.8.0** | 2026-06-23 | "Campaign motor" — a durable, machine-wide audit **campaign** that sits ABOVE individual sessions (one repo = one session; a fleet of repos = a campaign). **Ledger:** `~/.claude/config-audit/campaign-ledger.json` (outside the plugin dir → survives uninstall/upgrade) tracks a repo list + per-repo lifecycle (pending→audited→planned→implemented) + a machine-wide roll-up by status & severity; pure transforms with injected `now`. **`/config-audit campaign` (commands 20→21):** read-only report (`campaign-cli`) + human-approved writes (`campaign-write-cli`: init / add / set-status) — reports first, mutates only on explicit approval, never hand-edits the ledger. **Cross-repo backlog:** one severity-weighted prioritized pick-list (`buildBacklog`, `critical:1000/high:100/medium:10/low:1`). **Plan export + execution-by-reuse:** `campaign-export-cli --write` drops a planned repo's plan verbatim into its own `docs/`; execution reuses the existing `/config-audit implement` + `rollback` (no new execution machinery). All campaign code is `-cli`/lib → scanner count stays **15**, agents **7**, byte-stable. Plus pre-release cleanup: `knowledge-refresh` wired into the router + help; CLAUDE.md trimmed 540→134 lines (impl notes → `docs/scanner-internals.md`, config grade B→A). 1168 tests | | **5.7.0** | 2026-06-21 | "Optimization lens" — first detector of the «optimally shaped?» axis (vs «correct?»), plus a living knowledge layer. **Register:** `knowledge/best-practices.json`, a provenance-stamped, schema-validated best-practices register (first runtime-consumed `knowledge/` file). **OPT scanner (count 14→15):** `CA-OPT-001` (LOW) a ≥6-step CLAUDE.md procedure that would fit better as a skill, citing register entry `BP-MECH-003`. **`/config-audit optimize` + `optimization-lens-agent` (opus, agents 6→7):** prose-judgment lens for lifecycle→hook (`BP-MECH-001`), unscoped path→rule (`BP-MECH-002`), "never"→permission (`BP-MECH-004`); pre-filter recall + opus precision gate. **`/config-audit knowledge-refresh` (commands 19→20):** deterministic stale-check (injected reference date, 90-day cadence) + web re-verify/poll, human-approved writes only. Last two are agent/web-driven (not byte-stable). 1091 tests | | **5.6.0** | 2026-06-20 | "Steering-model II" — the load-pattern / compaction-survival model lands end-to-end. **Foundation:** `active-config-reader` now enumerates rules, agents, and output styles (alongside CLAUDE.md/plugins/skills/hooks/MCP), each tagged `loadPattern` (always / on-demand / external) + `survivesCompaction` from the published loading model; the frontmatter parser also reads YAML block sequences (`paths:` lists). **B (load-pattern accounting):** `manifest` reports component-level sources (the double-counting plugin roll-up is gone), tags every source with the load-pattern triple, and leads with an **always-loaded subtotal** ("tokens that enter context every turn"); `token-hotspots` annotates each ranked hotspot with its load pattern. **C (output styles):** new orchestrated **OST** scanner (count 13→**14**) — `CA-OST-001` a custom style stripping built-in coding instructions (missing `keep-coding-instructions: true`, V10), `CA-OST-002` a plugin style with `force-for-plugin: true` overriding the user's `outputStyle` (V11), `CA-OST-003` a settings `outputStyle` resolving to no known style (dead config). Doc-verified; frozen v5.0.0 snapshots preserved via strip-helpers, SC-5 regenerated. 1023 tests |