feat(scanners): a redundancy claim that belongs to one model is scoped to it

Anthropic documents that Claude Opus 5 verifies its own work, and that telling
it to double-check or to delegate verification to a subagent causes
over-verification -- token cost with no quality gain. The general subtraction
detector (BP-SUB-001) already surfaces those blocks for every user, with no
model-awareness at all.

`optimize --subtract --for-model <name>` adds the missing half. It ANNOTATES a
subset of the candidates --subtract already produced; it is not a second
detector and can never widen the candidate set. A second SUBTRACT_DETECTORS
entry would have collided with BP-SUB-001 on de-dup, and a prose-only signal in
the agent prompt would have been untestable.

There is no auto-detection, by measurement rather than omission: a CLAUDE.md has
no frontmatter and no resolvable target model, and this operator's own `route`
skill deliberately runs a different model per session -- the same file is read
by whichever model comes next. So the model is named, and the citation is
reported as conditional everywhere a human sees it (agent report copy, and the
Step 7a listing that is the last surface before an approval file).

Precision comes from the TARGET, not the verb list. Measured across the
409-file corpus: 392 BP-SUB-001 candidates, 31 (7.9%) carry a verify verb, and
0 also carry a reflexive or delegated target. Two independent raw-text greps
found 0 as well, so the zero is the corpus rather than an over-narrow regex.
Those 31 verb-only blocks -- "sjekk relevante config-filer", "Type-sjekk:
pyright", "To verify plugin functionality" -- are exactly the false positives a
verb-only version would have produced, which is BP-JUDG-001's 7/7 failure
arriving one lens over. The numbers live in the register entry's note and are
pinned by a test, because a session that cannot see the measurement reads the
zero as a broken detector and loosens it.

`recognized` is reported separately from `matchedCount`: a typo'd model name and
a genuinely clean config both yield zero, and without the distinction the CLI
would report a silent no-op as good news. Dogfooded on the real machine --
`opus-5` gives recognized:true/matchedCount:0, `oppus5` gives recognized:false.

source.published is absent because the guide carries no visible publish date;
its absence is asserted so a later session does not invent one to match the
other entries' shape. Both quoted sentences were verified verbatim 2026-08-12.

The payload stays additive -- forModel and per-candidate modelScope appear only
under the flag, so a plain --subtract run is byte-identical to before (asserted
on the serialized bytes, since a key set to undefined passes a shallow check).

Suite 1724 -> 1752 (+28). The one remaining failure is the pre-existing
drift-cli --output-file crash, untouched by this work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BRuXt6tZyowi8QYNKLSHQm
This commit is contained in:
Kjell Tore Guttormsen 2026-08-12 23:16:23 +02:00
commit 7df8e0d65b
11 changed files with 650 additions and 10 deletions

View file

@ -266,6 +266,7 @@ Your team configuration changes over time. Track it:
| `/config-audit optimize` | Optimization lens (mechanism-fit): config that works but fits a better mechanism — procedure→skill, lifecycle→hook, unscoped path→rule, "never"→permission. Hybrid motor (deterministic pre-filter + opus precision gate), every finding cites a best-practices-register rule |
| `/config-audit optimize --subtract` | **Subtraction lens** — the inverse question no other command asks: what no longer earns its always-loaded rent? Ranks CLAUDE.md blocks that correct general model *behaviour* rather than stating a local fact, split into **dead** (never missed) and **earned** (returns if the model stumbles), with the token payoff (`BP-SUB-001`). **Load-bearing local facts are excluded deterministically before the judge sees anything** — remotes, versions, paths, filenames, policy invariants and unresolvable entity names are never candidates, and an ordered list is treated as a contract. Opt-in and proposes only; add `--apply` to execute the removals you pick. Pair with `--global` to reach the user-level CLAUDE.md, where the always-loaded cost actually sits |
| `/config-audit optimize --subtract --apply` | **Execute approved removals.** You pick which blocks go by number; nothing is inferred. Every removal is checked against the file as it reads *now* — an approval that no longer matches is refused rather than applied to whatever moved into those lines — and the floor is re-asserted at write time, so a load-bearing block cannot be removed even by a hand-built approval. A dry run always precedes the write, the backup's manifest is verified to cover the file being written before a byte changes, and `/config-audit rollback` restores it. A removal targeting your machine-wide `~/.claude/CLAUDE.md` is **refused until you approve that scope explicitly** — it costs, and saves, in every project on every turn |
| `/config-audit optimize --subtract --for-model <name>` | **Model-scoped subtraction.** Some instructions are dead weight only for a *particular* model: Anthropic documents that Claude Opus 5 verifies its own work and **over-verifies** when told to double-check or to delegate verification to a subagent, adding token cost with no quality gain (`BP-PROMPT-001`). This flag annotates the `--subtract` candidates that carry a reflexive or delegated verification target — "double-check your own work", "use a subagent to verify" — while leaving *external* verification ("check the CI status") untagged. It **never widens the candidate set**, and there is deliberately no auto-detection: a CLAUDE.md has no frontmatter and no resolvable target model, and the same file is read by whichever model the next session runs — so the citation is reported as **conditional**, and an unrecognized model name is reported as unrecognized rather than as a silent zero |
| `/config-audit fix` | Auto-fix deterministic issues with backup + verification |
| `/config-audit rollback` | Restore configuration from a previous backup |
| `/config-audit plan` | Generate prioritized action plan from audit findings |
@ -647,6 +648,30 @@ classification), or when it names a capitalized entity the mechanism cannot reso
dictionary. That last rule is a deliberate conservative default: it declines to decide and
keeps the block, paying in recall rather than risk.
### Model-scoped candidates (`--for-model`)
Some instructions are dead weight only for a *particular* model. Anthropic documents that
Claude Opus 5 verifies its own work, and that explicit instructions to double-check or to
delegate verification to a subagent cause **over-verification** — token cost with no gain in
quality. `--subtract --for-model opus-5` annotates the subtraction candidates that carry a
reflexive target ("double-check your own work", "før du svarer") or a delegated one ("verify
with a subagent"), while leaving *external* verification ("check the CI status") untagged.
It is an annotation, never a detector: it tags a subset of what `--subtract` already found and
can never widen the candidate set. There is deliberately **no auto-detection** — a CLAUDE.md
has no frontmatter and no resolvable target model, and the same file is read by whichever model
the next session happens to run. That is also why the citation is reported as *conditional*
rather than as a settled fact about the file, both in the report and in the approval listing
shown before anything is written.
Precision comes from requiring the target, not from a narrow verb list. Measured across 409
real CLAUDE.md files: 392 subtraction candidates, **31 carry a verify verb, and 0 also carry a
reflexive or delegated target** — a zero confirmed by two independent raw-text greps, so it is
the corpus rather than an over-narrow rule. Those 31 verb-only blocks are precisely the false
positives a looser version would have produced. A model name the register does not cover is
reported as **unrecognized** rather than as a bare zero, so a typo never reads as "your config
is already clean".
The write half (`--apply`) keeps the same asymmetry. It is not a `fix` action and not a
`plan`/`implement` step, and both exclusions are measurements rather than preferences: the
subtraction axis never enters the orchestrated envelope, so `fix`'s re-scan verification would