Found by review after the SUB-WRITE commit, and both defects were in the
template rather than the engine every prediction in the fasit was about.
`--repo` is what a write target is classified AGAINST. The template passed the
SCAN target, and under `--global` that target IS ~/.claude -- so
~/.claude/CLAUDE.md matched `in-repo` and the gate went `silent`. Measured
against the real config: gate silent, scopeClass in-repo, 29 removals applied
with no approval asked. That is the same silent downgrade #62 measured for a
naive .git-upward walk, arriving through a different door, on the one target
this chunk was sequenced behind M-BUG-41 to protect. Every other gated template
already passed `--repo "$PWD"`; this one was the only outlier.
The dry run also could not validate the machine-wide case -- the case that is
mandatory in v1. The gate returned before any file was read, so a dry run there
reported 29 scope-gate refusals and zero checked spans, and the first run able
to find a stale approval would have been the one that writes. A gate guards a
WRITE, and a dry run is not one: `requiresApproval` and the disclosures are
still reported, so the operator is still asked.
The new caller-arm guard was itself red against the corrected template, matching
prose that merely NAMES the CLI. Narrowed to lines that invoke it.
Guards seen red against the original defects: `--repo "<target-path>"` red,
`--repo` omitted red, gate-blocks-dry-run red. Re-dogfooded as the template now
calls it: require-ok / user-scope / 29 spans validated / 0 files written.
Suite 1659 -> 1662/0. Frozen baselines untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017A6vrtPKsVuM4DJ27p7jzw
`optimize --subtract` has only ever proposed. `--apply` executes the blocks the
operator picks, behind a backup whose coverage is verified and a scope gate the
engine enforces rather than describes.
The open design decision from plan §C6 was settled by two measurements, not by
taste. It is NOT a fix-engine action: the subtraction axis appears nowhere in
scan-orchestrator or optimization-lens-scanner, so verifyFixes' re-scan would
mark every removal `verified` whether or not it happened -- a success-shaped
no-op, the same shape that made restoreBackup silently do nothing. It is NOT a
plan/implement step either: that pipeline needs a finding code, and OPT declares
exactly one, for the deterministic check.
The approval artifact is written by main context, not by the lens agent. That is
where the operator's decision actually happens, and it keeps the feature off the
still-unmeasured agent write surface (M-BUG-18 lists optimize as open).
Three properties are load-bearing, and each was seen red against its own defect:
removals validate against the ORIGINAL content and apply in descending line
order; the range check is not redundant with the text check (`line: 0` makes
`slice(-1, 0)` empty, so an empty text MATCHES and `splice(-1, 1)` deletes the
file's last line); and createBackup skips a nonexistent path while still
returning an id, so manifest coverage is asserted before a byte changes.
Two guards were green on their own defect and were fixed after measuring:
`/\b80\s*%\b/` never matches "80% of the file" -- `%` is a non-word character, so
the trailing `\b` demands a word character next. And the caller-arm sweep passed
vacuously against HEAD, iterating an empty list; only the added non-emptiness
assertion caught it.
The floor is repeated, not moved: floor-exclusion still vetoes before anything is
proposed, and the engine refuses a load-bearing block again so a hand-built
approval cannot route around it. `mv` to `_archive/` is a file-level rule and
does not apply to a block excision -- the timestamped backup is the recovery
artifact, and a second copy with no restorer would be worse than none.
strongestGate moves into write-scope.mjs so the gate ordering has one owner.
Dogfooded DRY-RUN against the real ~/.claude/CLAUDE.md: 29 candidates, gate
refused all 29 with exit 0 until the scope was approved, then 29/29 spans
validated with nothing written. ~789 tokens, ~18% of the file -- corroborating
the #40 fasit's ~850, and well short of what a deletion feature is tempted to
promise.
Suite 1625 -> 1659/0. Frozen v5.0.0 and default-output baselines untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017A6vrtPKsVuM4DJ27p7jzw