config-audit/docs/delete-and-rebuild-brief.md
Kjell Tore Guttormsen e9921d3c9d feat(optimize): add --subtract, the subtraction axis, behind a deterministic floor
Every command so far asked an addition question — what to add, what to move,
what it costs. Nothing asked what is no longer earning its always-loaded rent.
This adds that axis as a fourth lensCheck on the existing hybrid motor rather
than a new scanner or a 22nd command: the measured payoff (~18% of one file)
justifies a mode, not machinery.

It is 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 deleted one
costs a wrong remote or a broken script — so the floor is decided in code
(lib/floor-exclusion.mjs) before the opus judge sees anything, never in prose.

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, and container-reasoning is the error the hand-built
ground truth exists to catch.

Verified against that ground truth (built 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 first run found five floor
violations the synthesized fixture missed; each got a structural rule and a
fixture shape so it cannot regress.

Three real bugs the dogfood run exposed, all now covered:
- JS \b is ASCII-only, so /\bunngå\b/ never matches — every Norwegian keyword
  ending in æ/ø/å was silently dead.
- A bare word/word is not a path; "pros/cons" vetoed the largest deletable
  block until PATH_RE was tightened to rooted paths and globs.
- "Mid-sentence" must key on a preceding lowercase letter; the loose version
  read **bold labels:** and quoted openers as entities, costing 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 —
no "80%", no ablation figure.

Suite 1365 -> 1382/0. Frozen v5.0.0 snapshots untouched; plain optimize output
byte-identical on identical input (--subtract adds keys only when passed).
knowledge-refresh-cli's reference date moved to 2026-08-01: its premise that
every seed entry was verified 2026-06-20 expired when BP-SUB-001 got a genuine
verification date, and backdating the entry to fit the test would have been a
lie about when its source was checked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RW2haJXbxZpKivKHseSXNh
2026-07-31 16:24:51 +02:00

400 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Brief — Delete-and-Rebuild (config subtraction)
**Status:** BRIEF, not a plan. No code, no chunk breakdown, no version number committed.
Written 2026-07-29 so a session starting Thursday evening has a durable starting point.
STATE.md is gitignored in this repo, so this file — not STATE — is the record.
---
## 1. Trigger and its provenance
The operator relayed a third-party YouTube summary (Hyper Automation Labs) of a talk
Boris Cherny reportedly gave at Y Combinator Startup School, one day after Opus 5
shipped. Claims attributed to him in that summary:
- Anthropic deleted ~80 % of Claude Code's own system prompt when Opus 5 landed.
- Advice to users: every six months, delete your CLAUDE.md, your skills, your hooks —
see what the model does.
- Rebuild method: delete everything, use it, add one line back only when the model
stumbles on the same thing repeatedly.
- The model measured *slightly more intelligent* with the built-in prompts stripped.
**Two levels of confidence here, and they must not be collapsed** (updated 2026-07-29
after the operator corrected the first draft):
- **Attribution — confirmed.** The operator watched the recording and identifies Boris
Cherny on stage. This is not a channel's claim about who spoke; it is direct
observation by the operator. The talk happened and it is him.
- **The verbatim figures — still summary-level.** "80 % of the system prompt", "the
model measured slightly more intelligent without the prompts", the Bun numbers: these
reach us through the channel's editing, not through a primary transcript. They are
plausible and consistent with §2, and they are not quoted as fact anywhere below.
So if this becomes a `BP-*` entry in the best-practices register, the source field reads
*"Boris Cherny, YC Startup School talk (attribution confirmed by operator); figures via
third-party summary, not primary-verified"* — not "unverified", and not a bare citation
either. Getting a primary transcript for the figures is a nice-to-have, never a
prerequisite: the feature is argued from this repo's own logic (§3), and this repo has
one scar from treating a plausible quote as load-bearing fact («Fable low ≈ Opus high»,
fabricated, rejected 2026-07-14).
**What the operator has affirmed as in scope:** delete CLAUDE.md, then rebuild by adding
back what the model needs help with — *starting with what it must have*. That last clause
is not a detail; it is the design constraint in §6.0.
## 2. Verified ground truth (checked against the local CLI, 2026-07-29)
These *are* facts, and they are what make an empirical variant buildable:
| Fact | How verified |
|---|---|
| `claude --bare` exists — "Minimal mode: skip hooks, LSP, plugin sync, attribution, auto-memory, background prefetches, keychain reads, and CLAUDE.md auto-discovery. Sets `CLAUDE_CODE_SIMPLE=1`." | `claude --help` |
| `claude --system-prompt <prompt>` — replaces the session system prompt | `claude --help` |
| `--append-system-prompt`, `--add-dir`, `--setting-sources <user,project,local>`, `--plugin-dir`, `--settings`, `--agents` — all present and all usable to compose an isolated config for a controlled run | `claude --help` |
So the `CLAUDE_CODE_SIMPLE=1` env var the video calls "undocumented" is, in this CLI
version, a documented flag (`--bare`). That matters: an A/B ablation harness would not
need an undocumented hook.
## 3. Why this fits this repo (the argument that does *not* depend on §1)
The plugin has three pillars — Health, Opportunities, Action. Every existing command
answers a question on the **addition** axis:
- `feature-gap` — what could you add?
- `optimize` — what would fit a better mechanism?
- `posture` / `tokens` / `manifest` — how good/expensive is what you have?
- `fix` / `implement` — apply changes.
**Nothing answers the subtraction question: what is no longer earning its rent?**
`feature-gap` has no inverse. That is a real hole, independent of who said what on
a stage.
Two further reasons this belongs *here* specifically:
1. **Nothing in the repo measures instruction age.** Verified by grep over `scanners/`:
`stale` appears only for knowledge-register entry age (`lib/knowledge-refresh.mjs`)
and for stale plugin-cache versions (`scan-orchestrator.mjs`, M-BUG-11). No scanner
touches `git blame`, `mtime`, or the vintage of a CLAUDE.md block. An instruction
written for Sonnet 3.5 and an instruction written last week are indistinguishable to
every current scanner.
2. **Deleting bravely is only sane if you can undo it.** That is already pillar three:
`lib/backup.mjs`, `rollback-engine.mjs`, `auto-backup-config.mjs` (PreToolUse), and
`drift`'s `saveBaseline` / `loadBaseline` / `diffEnvelopes`. The safety net exists;
the feature that would use it does not. This is arguably the strongest framing:
*config-audit is already the infrastructure that makes "delete it and see" a
measurement rather than a gamble.*
## 4. Reusable machinery (do not rebuild these)
| Need | Already exists |
|---|---|
| Snapshot config before deleting | `scanners/lib/baseline.mjs` (`saveBaseline`), used by `drift` |
| Diff before/after | `diffEnvelopes` in the same module |
| Backup + restore individual files w/ sha256 manifest | `scanners/lib/backup.mjs`, `scanners/rollback-engine.mjs` |
| Cost of each source, always-loaded subtotal | `scanners/manifest.mjs`, `token-hotspots.mjs` |
| Human-approved-writes command pattern | `knowledge-refresh`, `campaign` (both non-byte-stable by design) |
## 5. Candidate shapes (sketches — pick on Thursday, do not pre-commit)
All three inherit the floor constraint in §6.0: whatever the shape, load-bearing local
facts are never deletion candidates, and a rebuild restores them first. A shape that
cannot express that distinction is disqualified regardless of how cheap it is.
**A. Deterministic vintage scanner (`CA-VIN-*`).** Per instruction block in CLAUDE.md /
rules / skills / hooks: age from git history, plus a compensatory-phrasing signal
(blocks that exist to correct model behaviour — "ALWAYS", "never forget", "read the
whole file first", "don't guess"). Output: ranked deletion candidates with age + token
cost + why it looks compensatory. Cheapest, most testable, fits the existing scanner
architecture, and composes with `tokens`/`manifest` for the payoff figure.
> **🔴 The age half of this shape is DEAD — measured 2026-07-31 (økt #40), see §8's
> updated checklist.** Per-line `git blame` over four real instruction files gives
> single-date shares of 88 % / 55 % / 100 % / 58 %: instruction blocks trace back to bulk
> commits, so per-block age carries almost no information. Worse, `blame` reports
> *last touch*, not vintage — a reformatting commit (e.g. this repo's own `96e32df`,
> "trim project CLAUDE.md to invariants") makes old instructions look young, which biases
> the signal rather than merely thinning it. That also kills the `mtime` fallback.
> **§8 pre-registered this exact outcome and its consequence: shape A's primary signal
> collapses to the compensatory-phrasing heuristic alone, and §7.2 is answered — the
> classification needs the agent layer.** Do not resurrect age as a "weak secondary
> signal"; that is the drift the pre-registration existed to prevent. What survives of
> shape A is the *deterministic phrasing + token-cost pre-filter*, feeding a
> precision-gated judge (the `optimize` architecture).
**B. Protocol command (`/config-audit rebuild`).** The delete → live with it → earned
re-add ledger loop, spanning sessions: archive current config, record what was archived,
and maintain a ledger where a line only returns when the operator records that the model
actually stumbled on it. Highest fidelity to the source idea; needs cross-session state
(the `sessions/` machinery already exists) and is inherently not byte-stable.
**C. Measured ablation harness.** Use `--bare` + `--system-prompt`/`--add-dir` to run the
same prompt with and without a block and compare. Closest to a real verifier, most
expensive in tokens, weakest determinism. Probably a later `--experimental` sub-mode of
A or B rather than its own command.
Likely landing: **A first** (deterministic, testable, immediately useful), with B as the
workflow that consumes A's output. C stays a documented idea until A+B exist.
> **DECIDED 2026-07-31 (#40), superseding the sketch above — see §7 q2/q3.** Shape A does
> not ship as a standalone `CA-VIN-*` scanner: its age signal is dead, and what remains of
> it (a phrasing + token-cost pre-filter) is precisely the front half of `optimize`'s
> existing hybrid motor. **The landing is `/config-audit optimize --subtract`** — a fourth
> `lensCheck` class emitting `CA-OPT-*` findings, with a deterministic **floor-exclusion**
> step ahead of the judge so a load-bearing block is never a candidate. Shape B (the
> earned-re-add ledger) stays out of v1 precisely because it is what would demand
> cross-session state and therefore a command of its own. Shape C unchanged: documented,
> not built.
## 6. Hard constraints (carry these into any implementation)
### 6.0 The floor: compensatory vs load-bearing instructions
**This is the invariant all three shapes in §5 must respect, and it is what makes the
feature safe to ship at all.** Not every line in a CLAUDE.md is the same kind of thing:
| Class | What it is | Test | Disposition |
|---|---|---|---|
| **Compensatory** | An instruction correcting model *behaviour* — "read the whole file first", "don't guess", "ALWAYS verify", "think before you code" | A smarter model would do this unprompted | **Deletion candidate.** Returns only when earned. |
| **Load-bearing** | A *local fact* the model cannot derive at any capability level — "never GitHub, only Forgejo at git.fromaitochitta.com", "system bash is 3.2, no `declare -A`", "test with `node --test 'tests/**/*.test.mjs'`", "`~/.claude` is not git-tracked" | No amount of intelligence produces this from the codebase alone | **Floor. Never a deletion candidate.** |
Model capability erodes the first class and does nothing to the second. That is the whole
mechanism behind the source idea — and it means "delete your CLAUDE.md" is only correct
for one of the two classes. A tool that treats them alike would delete the operator's
Forgejo constraint because Opus 5 "is smart enough now", which is a category error: the
model isn't failing at intelligence there, it simply cannot know.
**Consequence for the rebuild ordering.** A rebuild is not one undifferentiated
add-back-on-stumble loop. It is three tiers:
1. **Floor — goes back immediately, no trial period.** Load-bearing facts. The config is
never in a state where these are absent; a "delete everything" that drops them is a
broken experiment, not a brave one.
2. **Earned — out, returns only on repeated observed stumbling.** Compensatory
instructions that turn out to still be needed by *this* model.
3. **Dead — out and never missed.** The payoff, measurable in tokens via `manifest`.
A third class sits deliberately outside the axis: **policy prohibitions** ("never commit
secrets"). These may well be things the model would honour unprompted, but their cost of
being wrong is asymmetric and they are cheap. They stay in the floor by decision, not by
classification. Do not let a "the model knows this now" argument reach them.
The hard part is tier 1 vs tier 2, and that classification — not the deletion mechanics —
is the real engineering problem in this brief. Precision is asymmetric: a missed dead line
costs a few tokens per turn; a deleted load-bearing line costs a wrong remote, a broken
bash script, or a lost afternoon.
### 6.1 Existing repo constraints
- **`~/.claude` IS git-tracked as of 2026-07-26 — but archive by `mv` into `_archive/`
anyway, never `rm`.** Premise corrected 2026-07-31 (økt #40); the rule it was used to
justify is unchanged. Ground truth: `/Users/ktg/.claude` is a git repo, 7 commits,
initial commit `13cd708` 2026-07-26, remote `backup
/Volumes/DharmaBackup/claude-config.git` (external volume — not Forgejo, not GitHub),
47 files tracked (`hooks/` 19, `commands/` 14, `scripts/` 9, `CLAUDE.md`,
`settings.json`, `learnings/`, `docs/`). The rule survives on different grounds: only
those 47 files are recoverable, the history is days old, and the remote lives on a
volume that may not be mounted. Two consequences for this feature — (a) `~/.claude`
age evidence is bounded by the repo's own birth date and is therefore worthless, and
(b) any untracked file there (`memory/`, `coord/`, session state) is still
unrecoverable after `rm`.
- **`settings.json` is pathguard-protected** — no Edit/Write. Use `jq` + temp file +
atomic `mv`, with operator OK ([[settings-json-pathguard-write]]).
- **New scanner ⇒ the 7-step byte-stability checklist** ([[adding-scanner-byte-stability]]):
scanner + orchestrator + scoring area-map + strip-added-scanner + humanizer count +
SC-5 + humanizer wiring (`SCANNER_TO_CATEGORY` entry and `TRANSLATIONS.static` per RAW
title — M-16/M-17). Frozen `tests/snapshots/v5.0.0/` must stay untouched.
- **TDD is inviolable** — red tests before implementation, including for .md contracts.
- **`feat:` commits require non-trivial diffs in both README.md and CLAUDE.md**
([[docs-gate-feat-requires-readme-claudemd]]).
## 7. Open questions for Thursday
> **STATUS 2026-07-31 (#40): q1, q2, q3 are CLOSED below — decided on measured evidence
> (dead age signal) plus the hand-built fasit (`docs/subtraction-fasit.local.md`), under
> the operator's standing delegation of technical/design calls. q4 was already closed
> 2026-07-29. Do not re-litigate without new evidence.**
>
> **The landing: `/config-audit optimize --subtract` — a fourth lens class in the existing
> hybrid motor, not a new command and not a new scanner.** Full rationale in q3.
1. Scope of the deletion candidate set: user-level `~/.claude` only, project only, or both?
(Age evidence is much stronger for project-level, per §5A.)
**CLOSED — both, and user-level is mandatory in v1.** The argument that pointed at
project-level was git-age evidence, and that evidence no longer exists (§5A box), so
scope now follows *payoff and testability* instead. Two things force user-level in:
§8's blocking floor test is defined over the operator's global CLAUDE.md, and that file
is where the always-loaded cost actually sits (~4 300 tok every turn, every repo,
every session — vs a project CLAUDE.md that loads only in its own repo). Project-level
comes along because the same motor reads it and because two of §8's four floor anchors
turned out to live there.
*Implementation note, not a reopening:* `optimize` today takes a repo `target`. Reading
`~/.claude/CLAUDE.md` is a target-resolution change, and per §6.1 the `~/.claude` write
rules apply — but this mode proposes, it never writes.
2. **The core question, given §6.0:** can compensatory-vs-load-bearing be classified
deterministically with acceptable precision, or does it need the agent layer (like
`optimize`'s precision-gated `optimization-lens-agent`, which stays silent when
unsure)? Note the two signals are independent — a load-bearing fact can be old, and a
compensatory instruction can be new — so age alone can never carry this call. Prior:
a deterministic pre-filter feeding a precision-gated judge, which is exactly the
`optimize` architecture already in the repo.
**Design the §8 fasit around the ambiguous middle, not the poles.** The four named
must-survive items are clear-cut and any mechanism will get them right; the gate is
really decided by blocks like "Conventional Commits: `type(scope): beskrivelse`"
(local convention, or a nag the model would follow anyway?), "commit ofte med
beskrivende meldinger" (pure behaviour correction?), or the model-routing rubric
(a table of local policy that reads like advice). Include 35 such blocks
deliberately. A fasit built only from obvious cases will pass a tool that fails on
real config.
**CLOSED — it cannot be done deterministically. Deterministic pre-filter → precision-
gated judge, which is the `optimize` architecture verbatim.** Two independent lines of
evidence, both gathered before any code:
- *The age signal is gone* (§5A box). It was the only deterministic input that was
going to do real discriminating work; phrasing heuristics alone are what remain.
- *The fasit shows phrasing alone is not enough.* The blocks that decide the gate all
require reading **content against container** — a load-bearing fact wearing generic
phrasing, or vice versa:
| Fasit block | The trap |
|---|---|
| B-27 (defensive shell-scripting) | Reads as universal advice ("quote your variables"), but each line records a *specific local incident*, and "keep test code ASCII-clean" is inseparable from bash 3.2 (B-26, a §8 floor anchor). **A phrasing regex deletes this and fails the gate.** |
| B-32b ("never work in another repo") | Sits inside a bullet list of compensatory anti-patterns, formatted identically to its neighbours, but encodes the polyrepo structure and names `coord-send`. Container says delete, content says floor. |
| B-05 vs B-06 | Adjacent preference bullets, near-identical form, opposite calls. |
| B-30a / B-30b / B-30c | Three consecutive bullets under one heading, three different calls (Conventional Commits is floor, "lesbarhet > cleverness" is not). |
No regex separates these; a judge reading them in context can.
- *Two failure modes the judge must be prompted against, both found in the fasit:*
**staleness is not a deletion signal** (B-29 pins Opus 4.8 while the session runs
Opus 5 — that is a `drift`/dead-reference finding about a **floor** block), and
**tier-2 is not tier-3** (B-22, premiss-verifisering, is compensatory by class yet
earned itself again during this very session).
**Design consequence — the floor is NOT the judge's call.** Load-bearing exclusion runs
as a deterministic pre-filter step *before* the judge sees anything, so a floor block is
never a candidate at all. §8's gate is blocking and precision is asymmetric; making it
depend on a probabilistic judge would be the wrong guarantee. The judge then decides
only compensatory-tier questions on the remainder, and stays silent when unsure exactly
as `optimization-lens-agent` already does.
3. Does this ship as its own command, or as a `--subtract` mode of `optimize`?
Both are defensible; command count is already 21.
**CLOSED — a `--subtract` mode of `optimize`.** The discriminator is whether v1 needs
cross-session state: it does not. v1 ranks deletion candidates and reports the payoff;
the earned-re-add **ledger** (shape B) is what would require `sessions/` state, and it
is deliberately not in v1. Without the ledger there is nothing a separate command buys.
What the mode inherits by not being new (verified against the code, 2026-07-31):
| Requirement | `optimize` already has it |
|---|---|
| Deterministic pre-filter → opus judge | `optimization-lens-scanner.mjs` (`lens-prefilter`) → `optimization-lens-agent` |
| Precision gate, silent when unsure | Stated in the agent's own prompt |
| "Not a mistake" framing | Every `optimize` finding is a *Missed opportunity* — exactly right for a line that works but no longer earns its rent |
| Register-backed provenance | `knowledge/best-practices.json`, CONFIRMED-only |
| Non-byte-stable by design | Already documented as such in CLAUDE.md |
| Finding IDs | `CA-OPT-*`**no new `CA-VIN-*` scanner** |
And what it avoids: the 7-step byte-stability checklist (§6.1), a scanners badge bump
16 → 17, snapshot risk against frozen `tests/snapshots/v5.0.0/`, and a 22nd command.
**Proportionality is the clinching argument.** The fasit puts the honest ceiling at
~8501 400 always-loaded tokens on a ~4 300-token file — real (~20 %), but nowhere near
the source anecdote's 80 %, and 26 of 34 blocks are floor. On a well-maintained config
the subtraction axis is mostly a no-op. That payoff justifies a mode on an existing
motor; it does not justify a new scanner plus a new command. ("Starte ambisiøse tiltak
når en konfig-justering holder" is a named anti-pattern.)
Registry shape: a 4th `lensCheck` class alongside the three in the agent's table, with
its own `BP-SUB-001` register rule. `--subtract` is the flag because the subtraction
axis should not fire on a plain `/config-audit optimize` run — it asks a different
question and needs the operator to have opted into it.
4. ~~Ordering against the existing queue.~~ **Decided 2026-07-29:** the operator
prioritized this work ahead of pipeline step 4 (`rollback`), to start Thursday
2026-07-30. Do not re-litigate. The open part is only what follows it — the
prior order stands underneath: step 4 `rollback`, then the M-11→M-20 batch
release, then the v5.13 plan.
## 8. Verifisering (testable criteria)
**Before building anything:**
- [x] **DONE 2026-07-31 (#40).** `grep -rniE 'blame|mtime|birthtime' scanners/` returns
zero hits → §3.1 confirmed still true at build time.
- [x] **DONE 2026-07-31 (#41).** `node scanners/drift-cli.mjs . --save --name pre-subtraction`
succeeds and `lib/baseline.mjs` round-trips → §4 reuse is real, not assumed.
Written envelope is `{meta, scanners[16], aggregate, _baseline}` with
`_baseline.target_path` = the repo (15 findings, score 55). Run with no flags
beyond `--save --name`, since M-BUG-21 makes an unknown flag's value silently
become the scan target.
- [x] **RESOLVED 2026-07-31 (#41) by not needing it.** `BP-SUB-001` was written
`confirmed`, and asserts **nothing** from §1 — no "80 %", no ablation figure, no
Cherny attribution. Its claim is grounded entirely in the Anthropic steering blog
already cited by BP-MECH-001004, re-verified the same day: *"Every line loads into
every session for every engineer working in the repo, whether it's relevant to their
task or not. This consumes tokens and dilutes adherence"*, *"Build commands,
directory layout, monorepo structure, coding conventions, and team norms all fit
naturally here"*, and *"Keep CLAUDE.md under 200 lines"*. The anecdote motivated the
feature; it is not a premise of the shipped rule.
- [x] **DONE 2026-07-31 (#40) — THE AGE SIGNAL DOES NOT EXIST. Collapse condition met.**
Per-line `git blame` (not `git log`; per-line is the question) over four real
instruction files:
| File | Lines | Largest single-date share |
|---|---|---|
| `~/.claude/CLAUDE.md` | 250 | **88 %** (2026-07-26 = repo init; max age 5 days) |
| `config-audit/CLAUDE.md` | 102 | **55 %** (2026-04-08) |
| `config-audit/.claude/rules/ux-rules.md` | 32 | **100 %** (one commit) |
| `llm-security/CLAUDE.md` | 110 | **58 %** (2026-04-08) |
Blocks trace back to bulk commits exactly as the pre-registration feared, and
`blame` measures last-touch rather than vintage (a reformat resets it), so the
signal is *biased*, not merely sparse. **Consequence, as pre-registered: shape A's
primary signal is gone and §7.2 is answered — deterministic phrasing/cost
pre-filter → precision-gated judge.** See the boxed note in §5A.
- [x] **DONE 2026-07-31 (#40): the §8 floor-test fasit is built, before any classifier
exists.** `docs/subtraction-fasit.local.md` (LOCAL-ONLY — it quotes the operator's
global CLAUDE.md verbatim and this repo's only remote is the public `open/` mirror).
34 blocks over `~/.claude/CLAUDE.md`, each labelled `FLOOR` / `POLICY-FLOOR` /
`DELETABLE`, with 13 marked ⚠ AMBIGUOUS per §7.2. Headline numbers: 26 of 34 blocks
are floor; the deletable set is ~1 400 always-loaded tokens, realistically ~850
after tier-2 earn-backs, against a ~4 300-token file (**~20 %, not 80 %**).
**Correction it forces:** two of §8's four named floor anchors are not in that file
at all — the test command is project-scope (`config-audit/CLAUDE.md`), and
"`~/.claude` is not git-tracked" is now false (§6.1). The floor gate must run over
a *set* of files, not one.
**When `optimize --subtract` is built** (was: "if shape A is built" — retitled 2026-07-31
per §7 q3; the two struck items below were premised on a new standalone scanner, which is
no longer the shape):
- [x] **DONE (#41).** Red tests written first against a synthesized fixture and confirmed
failing (module not found) before either module existed.
- [x] **DONE (#41).** Suite green at **1382/0** (was 1365).
- [x] **DONE (#41).** `git diff --stat tests/snapshots/v5.0.0/` empty, **and** a plain
`optimize-lens-cli.mjs` run diffed byte-identical against its pre-change output
(`--subtract` adds keys only when the flag is present; `LENS_DETECTORS` still
exposes exactly 3 detectors, asserted in the suite).
- [ ] ~~`node scanners/self-audit.mjs --check-readme` passes (badge counts updated:
scanners 16 → 17).~~ **No badge bump — no new scanner.** `--check-readme` must still
pass, and README/CLAUDE.md still need the docs-gate diffs for a `feat:` commit
([[docs-gate-feat-requires-readme-claudemd]]).
- [ ] Every new finding renders with a non-`Other` `userImpactCategory` and a
non-`_default` action language → humanizer wiring correct (M-16/M-17).
- [x] **PASSED 2026-07-31 (#41) — the blocking floor test.** Fasit built first (#40), tool
run after, and the comparison **machine-checked**: a script asserts the intersection
of the candidate list with every FLOOR / POLICY-FLOOR line range in the fasit is
empty. Result: **zero load-bearing blocks proposed for deletion.** Both §8 anchors
present in the subject file (B-25 "Aldri GitHub. Kun Forgejo", B-26 "System bash er
3.2") are excluded, as are B-09, B-13, B-17, B-23, B-27, B-29, B-32b and B-38b.
The first run found **five** violations the synthesized fixture missed; each was
fixed with a structural rule (list-stem merge, ordered-list-as-contract, security
terms, `unresolved-entity`, declarative guard) and a fixture shape added so it
cannot regress.
- [x] **MET (#41), with the number stated honestly.** 11 of 18 deletable groups surfaced,
≈756 always-loaded tokens ≈ **18 %** of the ~4 300-token file — inside the
pre-registered ~850 / ~20 % band. The 7 misses are the conservative default working
as intended (unresolvable entity names, code spans, and declarative/infinitive
phrasing carrying no imperative). Precision over recall held throughout: every fix
in this session traded recall away, never the gate.