New GAP finding CA-GAP-028: authored subagents exist and not one of them names `model:` or `effort:`, so every delegated task runs on the main conversation's model (`model` defaults to `inherit`). Cites BP-MODEL-001/002, landed in C1. `whats-active` and `manifest` now carry `model`/`effort` per agent. Shipped as a conditional LEVER rather than a 25th dimension, and the choice was made by measurement: as a t3 dimension the agent-less marketplace-medium fixture would count it vacuously-present, moving the denominators 41->42 and utilization 44->45 — which flips `segment` "Developing"->"Competent" in the frozen v5.0.0 posture baseline, a field strip-retired-gap.mjs does not mask. A lever never enters those denominators. The general rule is now an invariant in CLAUDE.md. One check across both axes, not one per axis: it fires only when neither is used anywhere, so a deliberate everything-on-one-model policy stays silent. Cost is recall, chosen for precision. Found by dogfooding, fixed red-first: `model: inherit` is the documented default spelled out, so it must not count as routing — otherwise a config opts out of the opportunity without changing anything real. Two pre-existing defects surfaced and closed on the way: - The humanizer guard asserted TRANSLATIONS.GAP.static EQUALS the dimension titles, which forbade humanizing any lever — all three existing levers fell through to the generic "feature opportunity" default, wrong for a budget lever. Guard now requires coverage of every emittable title, seen red against those three before the entries were written. - Two hand-written copies of the lever list (finding-codes guard, humanizer guard) merged into one exported LEVERS registry carrying code AND title. - suppression-validation pinned CA-GAP-028 as an unoccupied number; C4 claimed it. Fixed structurally with a derived first-free id, not by picking a new literal — same class as #60's "bump this again". Suite 1596/0. Frozen v5.0.0 snapshots untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pq3nye21RVYk4pZLeT8pGz
8.9 KiB
Config-Audit Plugin
Claude Code Configuration Intelligence — know if your config is correct, find what could improve it, fix it automatically. Three pillars: Health (deterministic scanners), Opportunities (context-aware recommendations), Action (auto-fix with backup/rollback).
Per-command flags, patterns, and feature lists live in README.md and /config-audit help. This file carries what's invariant for working on the plugin.
Positioning vs. built-in /doctor (measured 2026-08-03, binding): we are the deterministic/reproducible/all-scope/zero-quota side; /doctor is usage-weighted one-shot judgment. Never build a feature whose whole value is duplicating a /doctor check — see README «config-audit vs. the built-in /doctor» and docs/v5.13-model-routing-effort-deadref-plan.md §A.
Commands
Core (just run /config-audit to get started)
| Command | Description |
|---|---|
/config-audit |
Full audit with auto-scope detection |
/config-audit posture |
A-F health scorecard (10 quality areas) |
/config-audit tokens |
Prompt-cache-aware token hotspots, each tagged with its load pattern; cache-aware |
/config-audit manifest |
Ranked table of every token source + always-loaded subtotal |
/config-audit feature-gap |
Context-aware feature recommendations grouped by impact |
/config-audit optimize |
Mechanism-fit lens (procedure→skill, lifecycle→hook, path→rule, never→permission). Agent-driven, not byte-stable. --subtract adds the subtraction axis (what no longer earns its always-loaded rent, BP-SUB-001) — opt-in, proposes only |
/config-audit fix |
Auto-fix deterministic issues with backup + verification |
/config-audit rollback |
Restore configuration from backup |
/config-audit plan |
Create action plan from findings |
/config-audit implement |
Execute plan with backups + auto-verify |
/config-audit help |
Show all commands |
Additional
| Command | Description |
|---|---|
/config-audit drift |
Compare current config against saved baseline |
/config-audit plugin-health |
Audit plugin structure, frontmatter, cross-plugin coherence |
/config-audit whats-active |
Read-only inventory of active plugins/skills/agents/MCP/hooks/CLAUDE.md (with token estimates, and model/effort per agent) |
/config-audit knowledge-refresh |
Refresh the best-practices register (stale check + web poll). Human-approved writes; not byte-stable |
/config-audit campaign |
Machine-wide audit ledger + token bill across repos. Human-approved writes; not byte-stable |
/config-audit discover |
Run discovery phase only |
/config-audit analyze |
Run analysis phase only |
/config-audit interview |
Gather user preferences (opt-in) |
/config-audit status |
Show current session state |
/config-audit cleanup |
Clean up old sessions |
Agents
| Agent | Role | Model | Color | Tools |
|---|---|---|---|---|
| scanner-agent | Find config files | sonnet | cyan | Read, Glob, Grep, Write |
| analyzer-agent | Generate report | sonnet | blue | Read, Glob, Grep, Write |
| planner-agent | Create action plan | opus | yellow | Read, Glob, Write |
| implementer-agent | Execute changes | sonnet | magenta | Read, Write, Edit, Bash, Glob |
| verifier-agent | Verify results | sonnet | purple | Read, Glob, Grep |
| feature-gap-agent | Feature recommendations | opus | green | Read, Glob, Grep, Write |
| optimization-lens-agent | Mechanism-fit precision gate | opus | orange | Read, Glob, Grep, Write |
Hooks
| Event | Script | Purpose |
|---|---|---|
| PreToolUse | auto-backup-config.mjs |
Backup config files before Edit/Write |
| PostToolUse | post-edit-verify.mjs |
Verify after Edit/Write, block on new critical/high |
| SessionStart | session-start.mjs |
Check for active (unfinished) sessions |
| Stop | stop-session-reminder.mjs |
Remind about current session phase |
Reference docs (read on demand)
docs/scanner-internals.md— scanner inventory, lib modules, action engines, knowledge base, per-scanner/per-block implementation notes (design rationale, primary-source verification, byte-stability lessons)docs/humanizer.md— plain-language output (v5.1.0), humanizer vocabularies, output modes
Plain-Language Output (v5.1.0)
Default output of all commands routes through humanizeEnvelope (lib/humanizer.mjs), decorating each finding with userImpactCategory, userActionLanguage, and relevanceContext. --raw and --json bypass the humanizer for byte-stable v5.0.0 output. Full detail: docs/humanizer.md.
Suppressions
Create .config-audit-ignore at project root — one exact ID or glob per line (CA-SET-003, CA-GAP-*). Suppressed findings are tracked in the envelope's suppressed_findings for audit trail. Disable with --no-suppress.
Architecture
Workflow: /config-audit → discover + analyze (auto) → plan → implement → verify. Auto-detects scope from git context; override with full|repo|home|current; --delta for incremental. Session state lives under ~/.claude/config-audit/sessions/{id}/ (scope.yaml, discovery.json, state.yaml, findings/, analysis-report.md, action-plan.md, backups/, implementation-log.md).
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-SKL-001, CA-OST-001, CA-OPT-001, CA-AGT-001.
GAP dimensions vs. levers (invariant). GAP_CHECKS holds the 24 dimensions — always evaluated, always counted in the utilization denominators (TIER_COUNTS / TOTAL_DIMENSIONS in scoring.mjs, and TITLE_TO_ID there). A lever is a finding the scanner emits after the loop and only under a measured condition; it carries no tier, never enters those denominators, and is registered in the exported LEVERS object (code + title in one place, because the finding-code guard needs the code and the humanizer-coverage guard needs the title). Adding a dimension moves every user's utilization score and can flip the reported segment in a frozen baseline — adding a lever cannot. When a check is only meaningful for configs that already have some feature, it is a lever.
{NNN} names the CHECK, never the emission position (invariant). scanners/lib/finding-codes.mjs is the single authority: every finding() call passes a code, and an undeclared or missing one throws — there is no counter fallback, because a fallback lets a half-converted scanner ship IDs that look valid. Adding a check takes the next free number for that scanner, never the next source-order position; removing one moves its key to RETIRED_CODES and its number is never reissued. IDs are therefore not unique per finding — one check failing in three files emits three findings sharing an ID, and (id, file, line) is the instance key that fix-engine verification uses. Frozen v5.0.0 baselines mask IDs (tests/helpers/mask-finding-ids.mjs) instead of re-deriving them; the check→number pairs are pinned exhaustively in tests/lib/finding-codes.test.mjs.
Conventions
Enforced conventions live in .claude/rules/ (auto-loaded as project instructions):
ux-rules.md— output/narration/formatting for all commands (never dump raw JSON, narrate before each step, space-separated command suggestions)command-development.md— required command frontmatter +plugin:actionnamingagent-development.md— agent frontmatter + "when to use" conventionsstate-management.md— updatestate.yamlafter every workflow phase
Coding style: scanners are zero-dependency Node ESM; new findings use the CA-{SCANNER}-{NNN} ID format; byte-stable CLIs are verified against frozen tests/snapshots/v5.0.0/ baselines.
Subtraction floor (invariant). optimize --subtract is the only lens that proposes removing config, so scanners/lib/floor-exclusion.mjs runs as a deterministic pre-step before the judge — a load-bearing block is never a candidate, and that guarantee must not be moved into the agent prompt. Two rules follow from it: (1) staleness is not a deletion signal — an outdated version pin inside a floor block is a drift/CA-CML dead-reference concern; (2) tier 2 ≠ tier 3 — a compensatory block that keeps earning its place returns, and reporting it as dead weight is wrong even when the label matches. Norwegian keywords need the Unicode boundaries in subtraction-prefilter.mjs; JS \b is ASCII-only, so /\bunngå\b/ silently never matches.
Testing
node --test 'tests/**/*.test.mjs'
Test fixtures in tests/fixtures/. Per-scanner and per-build-block implementation notes (design rationale, primary-source verification, byte-stability lessons) live in docs/scanner-internals.md → Implementation notes.
Gotchas
- Session directories accumulate — use
/config-audit cleanupto manage - Scanners run on Node.js ≥ 18 (uses node:test, node:fs/promises)
- Plugin CLAUDE.md files in node_modules should be excluded via scope