**U9, PR4.** Docs-only correction to a document already on `main`
(#2511). No changeset.
## I got #2511 wrong, and it matters
#2511's table claimed all six merge safeguards were verified. **Two of
them were not**, and the error is the same family this program exists to
stamp out: I reported the **absolute** failure count under mutation,
with **no baseline**.
`merge-error-recovery.test.ts` (10 failures) and `self-healing.test.ts`
(1) are **already red on clean `main`**. The "11 failed" I credited to
the row 1 mutation *was that pre-existing red*. The mutation added
nothing. I even flagged the identical `11 failed` on rows 1 and 3 as "a
red flag" in my own notes and then did not chase it.
## Re-measured as deltas
Baseline fail-SET vs mutated fail-SET on the identical selection,
reporting only NEW failures, each named:
| # | Safeguard | Baseline | Mutated | **NEW** | Verdict |
|---|---|---|---|---|---|
| 1 | user pause | 11 | 11 | **0** | **NOT COVERED** |
| 2 | `autoMerge:false` | 0 | 9 | **9** | covered |
| 3 | dependency gating | 0 | 5 | **5** | covered |
| 4 | capacity single-flight | 10 | 10 | **0** | **NOT COVERED** |
| 5a | merge-proof (pre-enqueue) | 0 | 1 | **1** | covered, thin |
| 5b | file-scope | 0 | 6 | **6** | covered |
| 6 | at-most-once | 0 | 3 | **3** | covered |
**Four hold. Two do not.**
- **Safeguard 1** is the pause invariant re-ratified in #2486. Removing
`task.paused || task.userPaused` from the merge admission provider
admits a **user-paused card into the merge pump** — and nothing fails.
- **Safeguard 4** is the single-flight guard that serializes merge.
Removing it permits concurrent `drainMergeQueue` entry — and nothing
fails.
Both guards **work correctly today**. What is missing is any test that
would notice if they stopped. That is exactly the state U9 must not
convert on top of — and #2511 said the opposite.
## Also corrected: nothing here is defended by blocking CI
The one gate-admitted file (`merger-merge-lifecycle.test.ts`) is not the
file that proves any surviving row. Rows 2/5a/6 rest on
`project-engine.test.ts`, row 3 on core's `task-merge.test.ts` — neither
is in the gate (core's gate is two PG tests via `test:pg-gate`).
## Three distinct ways the first pass was wrong
All recorded in the doc, because each produced a confident wrong answer:
1. **Absolute counts with no baseline** — rows 1 and 4. A mutation run
must diff fail-sets and report only new failures.
2. **Too-narrow selection** — an earlier pass measured rows 1 and 3 at
zero and I nearly filed two false gaps. Widening fixed row 3 but is also
how the pre-existing red crept in. Both directions need the baseline
diff.
3. **A harness that silently matched nothing** — the delta harness's
regex required a `|project|` segment in vitest's `FAIL` line.
`@fusion/core` does not emit one, so it parsed **zero** failures at both
baseline and mutation and printed "NOT COVERED" for row 3, which is
covered by 5 tests. A verification tool that reports success without
checking anything is worse than no tool; it must be tested against a
known-failing case first.
The harness now aborts on a no-op patch, asserts a clean restore, and I
validated its parser against a known-failing run before trusting it.
## Next
**PR5 writes the missing tests for safeguards 1 and 4**, then gate
admission. Neither should wait for U8 — they guard code that exists
today, and the conversion needs them in place first. That is the
reversible call I'm making rather than asking.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>