From 3131e39f64ea46bef22b4ec0ac735f90488ece03 Mon Sep 17 00:00:00 2001 From: autonomic-bot Date: Thu, 9 Jul 2026 08:10:56 +0000 Subject: [PATCH] =?UTF-8?q?review(redfix):=20wake=20#41=20=E2=80=94=20merg?= =?UTF-8?q?e=20target=20b5f2b10=20CONFIRMED=20behaviourally,=20not=20by=20?= =?UTF-8?q?sha?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Broke the pattern of re-probing B-redfix-8 (wakes #37-40, operator-only, adds nothing) and instead attacked the artefact the operator acts on: "merge b5f2b10, NOT 07fc6d4". Applied this phase's own lesson — verifying a fix's stated invariant beats verifying its shas, and mutation-testing is what makes a "+N tests" claim meaningful. - Lineage: b5f2b10 = 07fc6d4 + exactly the F-redfix-4 fix; fix symbols absent at 07fc6d4. - Reproduced the pre-fix defect cold (stdlib only; no pytest/awk/curl on orchestrator, no python3 on cc-ci): both canonical + live reconciler resolved to /keycloak/snapshot, no guard. - Invariants GREEN at b5f2b10. Mutation A (canonical_ns -> bare recipe) and Mutation B (guard -> no-op) each go RED on exactly the right invariant; mutation A leaves the non-provider invariant PASS, so the probe is specific, not globally fragile. - Guard is wired into both destructive paths, ahead of the destructive work. Drove the real F-redfix-4 scenario through the PUBLIC api: refused, live known-good intact. - Disbelieved then confirmed two Builder claims: "10 new tests" (exactly 10, coverage matches) and "no migration on cc-ci" (keycloak slot holds only last_good, no canon-* slot) — the latter is load-bearing because the fix ADDS a way for restore() to raise, and B-redfix-5 leaves the rollback restore() outside the try/except. Unreachable today; recorded as a merge precondition. - B-redfix-5 re-verified accurate at b5f2b10 (warm_reconcile.py:533-537). - "Zero blast radius": 8/8 sampled canonicals on cc-ci record domain=warm-, guard passes. Verdict: merge guidance CONFIRMED. No new finding. M1+M2 PASS stand, DONE stands, no VETO. B-redfix-8 unchanged (OPEN/HIGH/operator-rotation-only, not re-probed by design); B-redfix-5 deferred. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01SoraVF2hkEMExHswFX1g4M --- machine-docs/REVIEW-redfix.md | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) 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.