chore(release): v5.13.0 — pipeline hardening batch (M-BUG-11..25 + --subtract)
Batch release of everything since v5.12.5: one new lens mode and 14 real bugs,
all of them dogfooding finds — either from running the plugin against the
maintainer's real machine, or from walking analyze -> plan -> implement ->
rollback end-to-end on a throwaway repo copy.
Minor, not patch. STATE recorded this batch as "fix: only"; the log says
otherwise — e9921d3 ships `optimize --subtract`, a user-facing opt-in flag, so
semver requires a minor. Verified by reading `git log v5.12.5..HEAD` rather than
trusting the note: 11 fix, 1 feat, 8 docs.
Consequence: the planned v5.13 work (model routing, effort awareness, dead
references) now targets v5.14. docs/v5.13-model-routing-effort-deadref-plan.md
keeps its filename so existing references resolve, and says so at the top —
leaving a doc named for a version that shipped something else is exactly the
dead-reference class that plan is about.
Gates, all re-run against ground truth before writing anything:
- node --test 'tests/**/*.test.mjs' -> 1398 pass / 0 fail
- scanners/self-audit.mjs --check-readme -> passed (tests badge 1344 -> 1398;
the path in STATE said scripts/, which does not exist)
- catalog check-versions.mjs -> 0 ERROR
Counts unchanged: scanners 16, agents 7, commands 21, hooks 4.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AA5LT1UaDzctNkMi414qzA
This commit is contained in:
parent
8f149891c9
commit
c60f849d2e
4 changed files with 153 additions and 3 deletions
|
|
@ -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.12.5",
|
||||
"version": "5.13.0",
|
||||
"author": {
|
||||
"name": "Kjell Tore Guttormsen"
|
||||
},
|
||||
|
|
|
|||
145
CHANGELOG.md
145
CHANGELOG.md
|
|
@ -5,6 +5,151 @@ 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.13.0] - 2026-07-31
|
||||
|
||||
### Summary
|
||||
"Pipeline hardening" — the batch release of everything found by dogfooding the plugin against the
|
||||
maintainer's real machine and by walking the `analyze → plan → implement → rollback` pipeline
|
||||
end-to-end on a throwaway repo copy: **one new lens mode** (`optimize --subtract`) and **14 bugs**
|
||||
(`M-BUG-11`…`M-BUG-20`, `M-BUG-22`…`M-BUG-25`), every one of them a real defect a user could hit.
|
||||
|
||||
The minor bump is carried by `--subtract` alone; the other 14 are fixes. Two themes run through them:
|
||||
**agent-facing commands were scanning config the user cannot act on** (plugin-bundled and vendored
|
||||
copies masking real findings), and **new finding types kept shipping without their matching humanizer
|
||||
entry**, so plain-language output contradicted the finding's own evidence. The rollback chunk found the
|
||||
worst class in the repo: a `restoreBackup` that returned `{restored: [], failed: []}` — a *success-shaped
|
||||
no-op* — because nothing agreed on where a backup lives or what its manifest looks like.
|
||||
|
||||
No count change (scanners **16**, agents **7**, commands **21**, hooks **4**). Frozen `v5.0.0` snapshots
|
||||
untouched throughout; the SC-5 default-output snapshot was regenerated once, for two humanized titles
|
||||
only (`M-BUG-15`). **1398** tests (+54).
|
||||
|
||||
**Known and deliberately not fixed in this release:** `rollback` still cannot delete files that
|
||||
`implement` *created* — a backup cannot hold a file that never existed. It no longer fails silently
|
||||
(manifests carry a `created:` list, `restoreBackup` returns `createdNotRemoved`, and `rollback.md`
|
||||
requires the report), but automatic deletion of user files is destructive and gets its own design.
|
||||
`drift-cli.mjs` still lacks `--output-file` (`M-BUG-21`).
|
||||
|
||||
### Added
|
||||
- **`optimize --subtract` — the subtraction axis (`BP-SUB-001`).** Every command so far asked an
|
||||
addition question: what to add, what to move, what it costs. Nothing asked what no longer earns its
|
||||
always-loaded rent. `--subtract` adds that as a fourth `lensCheck` on the existing hybrid motor — a
|
||||
mode, not a 22nd command or a 17th scanner, because the measured payoff (~18% of one file) justifies
|
||||
a mode and no more. It is **opt-in and proposes only**.
|
||||
It is also the only lens that proposes *removing* config, so it carries a guarantee the others don't
|
||||
need: **a load-bearing block is never a candidate.** Precision is asymmetric — a missed dead line
|
||||
costs a few tokens per turn, a wrongly deleted one costs a broken script or a wrong remote — so the
|
||||
floor is decided in code (`scanners/lib/floor-exclusion.mjs`) *before* the opus judge sees anything,
|
||||
never in prose. That ordering is an invariant, not an implementation detail.
|
||||
Granularity is the leaf block, with two structural exceptions: a paragraph ending in `:` merges with
|
||||
the list it introduces, and an ordered list is a contract whose steps inherit floor from any sibling.
|
||||
Unordered lists deliberately do **not** inherit — a load-bearing bullet and a disposable one routinely
|
||||
share a list.
|
||||
Verified against a hand-built ground truth written *before* any classifier existed, with the
|
||||
comparison machine-checked rather than read by eye: **zero load-bearing blocks proposed**, 11/18
|
||||
deletable groups surfaced, ~756 tok ≈ 18% of a ~4300-token file — inside the pre-registered band. The
|
||||
gate is re-runnable via `scripts/dogfood-subtraction-gate.local.mjs`. Three bugs the dogfood run
|
||||
exposed are now covered by fixtures: JS `\b` is ASCII-only so `/\bunngå\b/` never matched (every
|
||||
Norwegian keyword ending in `æ/ø/å` was silently dead); a bare `word/word` is not a path
|
||||
("pros/cons" vetoed the largest deletable block); "mid-sentence" must key on a preceding lowercase
|
||||
letter, or `**bold labels:**` read as entities and cost 4 of 11 groups.
|
||||
`BP-SUB-001` is grounded entirely in the Anthropic steering blog already cited by
|
||||
`BP-MECH-001..004` and asserts nothing from the talk that motivated the feature.
|
||||
|
||||
### Fixed
|
||||
- **`rollback` — the backup path contract the engine and the commands disagreed on
|
||||
(`M-BUG-22`/`M-BUG-23`/`M-BUG-24`/`M-BUG-25`).** Pipeline step 4 dogfood: `/config-audit rollback`
|
||||
could not see a single one of the four real backups on this machine, and reported "Backup not found"
|
||||
for one sitting right there. Four defects, one root — nothing agreed on where a backup lives or what
|
||||
its manifest looks like.
|
||||
**`M-BUG-22` (high):** `lib/backup.mjs` resolved `~/.config-audit/backups` (pre-v2.2.0) while every
|
||||
command, agent and doc uses `~/.claude/config-audit/backups`. The auto-backup hook and `fix-cli` wrote
|
||||
to the first, `implement` to the second, `rollback` read only the first — so `listBackups()` returned
|
||||
**9 phantom backups from the test suite** and **0 of the 4 real ones**. The canonical root is now
|
||||
`~/.claude/config-audit/backups`, with the legacy root kept **readable** (`legacy: true`) so older
|
||||
backups stay listable and restorable.
|
||||
**`M-BUG-25` (high, the worst failure mode in the file):** `parseManifest` understood only the
|
||||
engine's quoted `original_path:` spelling, but `implement` hand-builds its manifest with
|
||||
`- backup:`/`original:`/`sha256:`. Every implement-made backup parsed to zero files and
|
||||
`restoreBackup` returned `{restored: [], failed: []}` — success-shaped, and silent. Both formats parse
|
||||
now, and a manifest with unparseable entries **throws** instead of pretending to succeed.
|
||||
**`M-BUG-23`:** both session hooks watched `~/.config-audit/sessions`, which does not exist — "check
|
||||
for active sessions" had never fired once. It fires now.
|
||||
**`M-BUG-24`:** the suite called `createBackup()` against the developer's real home, leaving nine stray
|
||||
backups there while `cleanupOldBackups()` deletes past ten. The root is overridable via
|
||||
`CONFIG_AUDIT_BACKUP_ROOT` / `CONFIG_AUDIT_LEGACY_BACKUP_ROOT`, and both test files use it — any new
|
||||
test touching `createBackup()` must too.
|
||||
Verified against backup `20260717_032636` on a throwaway copy, through the previously broken engine
|
||||
path: 3/3 files restored **byte-exact** (sha256 match), zero writes outside the copy, backup dir
|
||||
unmodified.
|
||||
- **`rules-validator` — `globToRegex` corrupted mid-pattern `/**/` globs (`M-BUG-19`).** The
|
||||
`?` → `[^/]` replacement ran *after* the `{{GLOBSTAR_SLASH}}` placeholder was restored to `(?:/.+/|/)`,
|
||||
corrupting the group opener `(?:` into `([^/]:`. Every rule pattern containing a mid-pattern `/**/`
|
||||
silently matched only the zero-dir branch, so live rules were flagged "matches no files" (`CA-RUL`).
|
||||
Found by dogfooding `/config-audit implement` on a throwaway repo copy: the implementer agent's
|
||||
correct `posts/**/post.md` rule was flagged dead. Fixture outcomes byte-identical.
|
||||
- **`analyze` persists the agent-returned report (`M-BUG-18`).** The Claude Code subagent harness
|
||||
instructs spawned agents *not* to write report/summary/findings/analysis `.md` files — the parent
|
||||
reads the final text message. Verified live: `analyzer-agent` skipped `Write` entirely, so
|
||||
`analysis-report.md` never landed on disk and the plan/interview/status phases found nothing to read.
|
||||
New orchestrator-writes contract: the agent returns the complete report as its final message and the
|
||||
`analyze` command saves it verbatim before presenting the summary. The harness note is **file-type
|
||||
specific** — `plan` was dogfooded afterwards and writes `action-plan.md` without friction, so the same
|
||||
fix is *not* needed there.
|
||||
- **`implement` pins `>>` append discipline on the shared log (`M-BUG-20`).** `implement.md` spawns
|
||||
implementer agents in parallel batches, all appending to the same `implementation-log.md`. Dogfooding
|
||||
showed agents satisfying "append result to:" with a full-file `Write` — the last writer clobbered 4 of
|
||||
6 entries. Both contracts now pin the mechanism: append with a Bash `>>` heredoc, never the Write/Edit
|
||||
tool on a shared log.
|
||||
- **`optimize` lens scopes out plugin-bundled CLAUDE.md + keys candidates by absolute path
|
||||
(`M-BUG-11`).** The lens CLI fed its precision-gate agent every CLAUDE.md discovery returned, including
|
||||
the 256 files under `~/.claude/plugins/` — vendored copies across every cached version plus their
|
||||
fixtures and examples. `optimize --global` produced 454 candidates across 92 "files", ~250 of them from
|
||||
plugin-internal files a user cannot act on (the plugin overwrites them on update). Second defect:
|
||||
candidates were keyed by `relPath || absPath`, and `relPath` collides across scopes — a repo-root
|
||||
`CLAUDE.md` and `~/.claude/CLAUDE.md` both key to `CLAUDE.md`, so the two files that actually matter
|
||||
merged into one indistinguishable bucket and the agent's `Read(file)` would resolve the wrong one.
|
||||
Dogfood: candidates **454→45**, distinct files **92→11**, repo vs user-global now distinct.
|
||||
- **`feature-gap` scopes presence checks to authored config and reads the settings cascade
|
||||
(`M-BUG-13`).** The GAP scanner's 25 presence checks ran over the full `includeGlobal` discovery, so
|
||||
this plugin's own `examples/optimal-setup` (vendored across plugin-cache versions) satisfied every
|
||||
tier-3 check — masking real feature gaps to **GAP=0 on any target**. And the real
|
||||
`~/.claude/settings.json` was invisible to the settings-key checks (the `includeGlobal` gotcha plus the
|
||||
`maxFiles` cap), which would have flipped `statusLine`/`autoMode` into false positives the moment the
|
||||
maskers were removed. Both halves are fixed together: `isAuthoredConfig` excludes plugin-bundled and
|
||||
nested `examples/`/`tests/fixtures/` config, and `readSettingsCascade` reads user→project→local
|
||||
directly. Empty target: ~0 (masked) → **18** humanized opportunities.
|
||||
- **`posture --output-file` humanizes findings in default mode (`M-BUG-12`).** `feature-gap.md` and
|
||||
`posture.md` both read findings from `posture.mjs --output-file` and group on the humanizer fields,
|
||||
but `posture.mjs` only humanized the stderr scorecard — its `--output-file` JSON wrote the raw
|
||||
v5.0.0-shape result, so every finding's humanizer fields were `undefined` and both commands silently
|
||||
degraded to the raw tier-fallback. v5.1.0 plain-language output was dead for `feature-gap` and for
|
||||
`posture`'s finding-level grouping. The payload is now humanized in default mode (applied to
|
||||
`result.scannerEnvelope`, which is where posture nests it); `--json`/`--raw` stay raw.
|
||||
- **AGT findings humanize to "Wasted tokens", not "Other" (`M-BUG-17`).** The agent-listing scanner emits
|
||||
an always-loaded per-turn token cost — "the dominant single always-loaded source" — but
|
||||
`SCANNER_TO_CATEGORY` had no AGT entry, so its findings fell through to the `Other` fallback, a bucket
|
||||
that isn't even in the analyzer-agent's category list. All 16 orchestrator scanner prefixes are now
|
||||
covered by the category map, closing the class.
|
||||
- **On-demand copy for the oversized skill-body finding (`M-BUG-16`).** The v5.11 B7 finding measures a
|
||||
skill *body*, which loads only when the skill is invoked — but with no `SKL.static` entry it fell
|
||||
through to `SKL._default` ("using more of the listing budget than it should"), so the humanized title
|
||||
claimed a listing-budget cost and directly contradicted its own humanized evidence ("loads on demand
|
||||
only … NOT every turn").
|
||||
- **Honest absence-state copy for two GAP enhancement findings (`M-BUG-15`).** The "No path-scoped rules"
|
||||
and "No subagent isolation" checks fire on an *empty* collection too, but the humanized titles
|
||||
presupposed the feature exists — "Your subagents share Claude's main work folder" appeared in the same
|
||||
report as "You haven't set up any specialized helper agents yet". A user cannot simultaneously have no
|
||||
subagents and have subagents that lack isolation. Both titles now use the house "You haven't set up X
|
||||
yet" framing, which is honest for the zero-state *and* the has-but-unconfigured state. Fixed in the
|
||||
humanizer, not by gating the scanner — a presence gate would have moved the frozen v5.0.0
|
||||
marketplace-medium baseline.
|
||||
- **Size-neutral copy for "CLAUDE.md not modular" (`M-BUG-14`).** The check is a pure presence check with
|
||||
no length gate, but the copy claimed the file is "one big block" and that splitting makes it "easier on
|
||||
the loading time" — an unconditional size overclaim that simply lies for a ~625-token CLAUDE.md. Copy
|
||||
softened to the honest structural framing; no length gate added, which would have made it the only
|
||||
size-gated check among its siblings.
|
||||
|
||||
## [5.12.5] - 2026-06-26
|
||||
|
||||
### Summary
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
*AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)*
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
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.
|
||||
|
|
@ -689,6 +689,7 @@ This plugin is cautious by design — configuration files are important, and a b
|
|||
|
||||
| Version | Date | Highlights |
|
||||
|---------|------|-----------|
|
||||
| **5.13.0** | 2026-07-31 | "Pipeline hardening" — the batch release of everything found by dogfooding the plugin against the maintainer's real machine and by walking the `analyze → plan → implement → rollback` pipeline end-to-end on a throwaway repo copy: one new lens mode plus **14 bugs** (`M-BUG-11`…`M-BUG-20`, `M-BUG-22`…`M-BUG-25`). **Added — `optimize --subtract` (`BP-SUB-001`):** the subtraction axis, asking what no longer earns its always-loaded rent. Opt-in, proposes only, and the only lens that removes config — so a **load-bearing block is never a candidate**, decided in code (`scanners/lib/floor-exclusion.mjs`) *before* the judge runs, never in prose. Verified against a hand-built ground truth written before any classifier existed: **zero load-bearing blocks proposed**, 11/18 groups, ~756 tok ≈ 18% of a ~4300-token file. **Fixed — `rollback` (`M-BUG-22/23/24/25`):** nothing agreed on where a backup lives; `listBackups()` returned 9 phantom test backups and 0 of 4 real ones, and `restoreBackup` returned `{restored:[],failed:[]}` — a **success-shaped no-op** — because `parseManifest` knew only one of the two manifest spellings in use. Canonical root now, legacy kept readable, unparseable manifests **throw**. **`M-BUG-19`:** `globToRegex` corrupted mid-pattern `/**/`, flagging live rules dead. **`M-BUG-18`/`M-BUG-20`:** the subagent harness won't write report-shaped `.md` (analyze now persists the returned report), and parallel agents clobbered the shared log with `Write` (pinned to Bash `>>`). **`M-BUG-11`/`M-BUG-13`:** `optimize` and `feature-gap` scanned vendored plugin config a user cannot act on — `optimize` candidates **454→45**, `feature-gap` **~0 (masked) → 18** opportunities. **`M-BUG-12`/`M-BUG-14`/`M-BUG-15`/`M-BUG-16`/`M-BUG-17`:** plain-language output that contradicted its own evidence (posture's `--output-file` never humanized; four finding types with no humanizer entry). Known, deliberately unfixed: `rollback` cannot delete files `implement` *created* — it now reports them (`createdNotRemoved`) instead of failing silently; automatic deletion of user files gets its own design. No count change (scanners **16**, agents **7**, commands **21**, hooks **4**); frozen v5.0.0 untouched, SC-5 regenerated once for two humanized titles. **1398** tests (+54). |
|
||||
| **5.12.5** | 2026-06-26 | "Dogfood denoise" — a samle-release of the Fase-3 scanner false-positive batch (`M-BUG-2/6/7/8/10`, all dogfooding finds on the maintainer's real machine). Five scanners stop counting non-user / non-live config as the user's: **CNF** (`M-BUG-2`) excludes files under `.claude/plugins/` from conflict analysis (`isPluginBundled`) — installed plugins' bundled settings/hooks/fixtures are not a user-resolvable cascade (dogfood **339→0**, Conflicts was an F on pure plugin noise). **file-discovery** (`M-BUG-8`) adds `backups` to `SKIP_DIRS` — a `backups/` tree holds frozen copies, never live config (dogfood files-under-`/backups/` **36→0**, 717 live retained). **token estimator** (`M-BUG-6`) strips block-level `<!-- -->` HTML comments from CLAUDE.md sizing — CC strips them before injection, so they were never always-loaded tokens (dogfood ~3386→3301, ~85 tok). **CPS** (`M-BUG-7`) skips fenced/inline code and whitelists CC-stable path vars (`${CLAUDE_PLUGIN_ROOT}`/`${CLAUDE_PROJECT_DIR}`) before cache-buster matching (dogfood **5→2**). **SET** (`M-BUG-10`) typo-gates the unknown-settings-key finding — the CC schema is passthrough (verified against the 2.1.193 binary), so an unknown key is forward-compatible, not an error; it now flags only a near-miss of a known key (levenshtein ≤2), severity medium→low, +6 binary-verified `KNOWN_KEYS` (dogfood **6→0**). No count change (scanners **16**, agents **7**, commands **21**, hooks **4**); all five are byte-stable — frozen v5.0.0 + SC-5 + default-output snapshots untouched, no re-seed (each fixture's findings are genuinely unchanged). **1344** tests (+37). |
|
||||
| **5.12.4** | 2026-06-26 | "Rooted rules" — fixes `M-BUG-9` (dogfooding find) in `scanners/rules-validator.mjs`: the RUL "Rule path pattern matches no files" check now resolves a rule's `paths:`/`globs:` pattern against the rule's **own project root** (the dir containing its `.claude/`), not the outer scan root. Previously `countGlobMatches` globbed against the scan target and `collectProjectFiles`' `depth>4` cutoff never reached deep matching files, so a live rule in a **nested repo** (e.g. a marketplace checkout under `~/.claude`) was wrongly flagged "never activates" (high) — a false F-grade for anyone with rules in a nested repo. The fix derives each rule's project root, collects+globs per root (cached), and skips the check for user-global rules (`root === $HOME`), which scope against the active project at runtime. Same scope-conflation family as `M-BUG-1/2`. No count change (scanners **16**, agents **7**, commands **21**); the fix is a no-op when `projectRoot === targetPath` (the common single-repo scan), so frozen v5.0.0 + default-output snapshots stay byte-stable. **1307** tests (+2 TDD: nested-repo false-positive + HOME guard). |
|
||||
| **5.12.3** | 2026-06-26 | "Phantom agents" — fixes `M-BUG-3/4/5` (dogfooding finds) in `scanners/lib/active-config-reader.mjs`: `enumerateAgents` now counts only the agents Claude Code actually **registers**. Per the official subagents doc, an agent needs valid `name`+`description` frontmatter, and CC scans recursively and silently skips frontmatter-less files. The reader previously (`M-BUG-5`) counted every `.md` regardless of frontmatter, (`M-BUG-3`) never recursed into agent subdirs, and (`M-BUG-4`) double-counted when the project dir equals the user dir (scanning `$HOME` — root cause, also affecting rules/output-styles). Real-machine verify: user-agent count **13→0** (all 12 user agents + `REMEMBER.md` are frontmatter-less → CC registers none), HOME `project`-dup **13→0**; corrected always-loaded baseline ≈ **53** (was 66). Agent enumeration is machine-dependent and 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. |
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
# v5.13 Plan — Model Routing, Effort Awareness, Dead References
|
||||
|
||||
> **Target version is now v5.14.** The `5.13.0` slot was consumed by the pipeline-hardening batch
|
||||
> (`optimize --subtract` is a feature, so that release could not be a patch). The filename is kept so
|
||||
> existing references resolve; only the target moved.
|
||||
|
||||
Derived from an external video analysis ("The Model Isn't the Moat", 2026-07) cross-checked
|
||||
against primary sources and against what config-audit already encodes. Every claim acted on
|
||||
here was verified against Anthropic's own docs; video-only claims are explicitly rejected below.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue