diff --git a/machine-docs/REVIEW-redfix.md b/machine-docs/REVIEW-redfix.md index f0309d2..a73a9fa 100644 --- a/machine-docs/REVIEW-redfix.md +++ b/machine-docs/REVIEW-redfix.md @@ -1206,3 +1206,66 @@ declares a still-live public credential inert on a broken probe. **No new findin M1 + M2 PASS stand; `## DONE` stands; no VETO. B-redfix-8 remains **OPEN, HIGH, operator-rotation-only** — sole close path is a correctly-computed sentinel that differs from `3fcea78925015fc9`, which only operator rotation produces. I cannot rotate a Class-A1 input (§4.4) nor rewrite published history (`--force` forbidden). + +--- + +## Wake #41 — 2026-07-09T08:10Z — merge-target guidance verified BEHAVIOURALLY (mutation + e2e). No new finding. + +Deliberately did **not** re-probe B-redfix-8 for a fifth time (wakes #37–#40 all did; it is +operator-rotation-only and re-confirming adds nothing). Instead attacked the one artefact the operator +will actually **act on**: the instruction *"merge `redfix-m2-harness`@`b5f2b10`, NOT `07fc6d4`"*. If that +is backwards, the operator merges the F-redfix-4 defect. Prior wakes only ever checked that claimed +artifacts **existed and matched shas** — which is precisely the weak check that let F-redfix-4 hide for +seven re-confirmations. So this wake tested the fix's **stated invariant**, and mutation-tested the test. + +**Lineage.** `b5f2b10` = `07fc6d4` + exactly one commit (`fix(keycloak): key warm state by stack +namespace…`). `canonical_ns` / `_assert_slot_not_foreign` present at `b5f2b10`, ABSENT at `07fc6d4`. +`b5f2b10` is *not* an ancestor of `main` — correct and expected: nothing is merged; the operator merges. + +**Pre-fix defect reproduced from cold** (worktree @ `07fc6d4`, stdlib only — no pytest anywhere; `awk`, +`curl`, `which` absent on the orchestrator, `python3` absent on cc-ci): +domains already differed (`warm-keycloak…` vs `warm-canon-keycloak…`) yet `registry_path` → `app_dir(recipe)` +and `seed_canonical` → `warmsnap.snapshot(recipe, …)`, so **both resolved to `/keycloak/snapshot`**, +with no guard. Same probe @ `b5f2b10` → no collision. Caveat, stated plainly: I demonstrated the **slot +collision + absence of guard**, not an executed clobber — `snapshot()` needs docker past the guard. + +**Invariants @ `b5f2b10` (all GREEN):** slot/domain/app_dir disjoint for every `WARM_DOMAINS` recipe; +non-providers keep the bare `` ns (gitea/discourse/mumble/bluesky-pds/mattermost-lts); guard raises +`SnapshotError` on a foreign slot, permits its own, permits an unclaimed slot. + +**Mutation test — the probe has teeth.** Mutant A (`canonical_ns` → `return recipe`) → RED on the 3 +disjointness invariants, invariant 2 still PASS (specific, not globally fragile). Mutant B +(`_assert_slot_not_foreign` → no-op) → RED on exactly the foreign-slot invariant. Both killed; tree restored. + +**Guard is wired, not dead code.** Called at `warmsnap.py:158` in `snapshot()` (BEFORE `_assert_undeployed` +and before the staging teardown) and `:209` in `restore()` (before the volume clear). Drove the real +F-redfix-4 scenario through the **public** API — `snapshot(live_slot('keycloak'), canonical_domain('keycloak'))` +→ refused with the F-redfix-4 message, live known-good intact. Structurally blocked *and* behaviourally blocked. + +**Two Builder claims disbelieved, then confirmed:** +1. *"10 new tests"* → exactly 10 `def test_` added, names match the claimed coverage incl. foreign-slot + refusal in **both** `snapshot` and `restore`. (Could not execute them: no pytest on either host.) +2. *"no migration on cc-ci — keycloak's dir holds only `last_good`"* → cold `ssh cc-ci`: + `/var/lib/ci-warm/keycloak/` contains **only `last_good`** (no `snapshot/`), and **no `canon-*` slot exists**. + +This mattered: the fix **adds a new way for `restore()` to raise** (the guard), and B-redfix-5 leaves the +reconciler's rollback `restore()` outside the try/except. Composed, a foreign-slot raise in rollback would +leave live keycloak undeployed after `abra.undeploy()`. That composition is **unreachable on cc-ci today** +precisely because the keycloak slot carries no snapshot meta. Recorded as a merge precondition, not a defect. + +**B-redfix-5 re-verified accurate at the merge target** (`warm_reconcile.py:533-537`): `abra.undeploy` → +`wait_undeployed` → `warmsnap.restore` sit outside the try/except that guards only the upgrade (520-525); +`deploy_version` at 537 is likewise unreached if `restore()` raises. DEFERRED.md's description still holds. + +**"Zero blast radius on the 15 existing canonicals"** spot-checked on cc-ci: 8/8 sampled +(gitea, ghost, drone, mumble, plausible, immich, hedgedoc, n8n) record `domain=warm-…` in +`snapshot/meta.json`, which is exactly what post-fix `canonical_domain()` returns for a non-provider → +guard passes, no migration. + +**Verdict: merge guidance CONFIRMED — `b5f2b10` is the correct merge target; `07fc6d4` carries the defect.** +No new finding. M1 + M2 PASS stand; `## DONE` stands; **no VETO**. B-redfix-8 remains OPEN/HIGH/ +operator-rotation-only (unchanged, not re-probed this wake by design). B-redfix-5 remains deferred/open. +Merge precondition for the operator: `/var/lib/ci-warm/keycloak/` must still hold no `snapshot/` at merge +time (it doesn't today) — otherwise B-redfix-5 × the new guard can wedge the live OIDC provider on rollback. +Did not read JOURNAL-redfix.md before forming this verdict; read the fix's commit message only as git history +(code), and independently re-derived every claim I took from it.