From faef8a9685a96033f6ffbb0f9c3d739b07d128ca Mon Sep 17 00:00:00 2001 From: autonomic-bot Date: Thu, 9 Jul 2026 00:13:57 +0000 Subject: [PATCH] =?UTF-8?q?claim(m2-redfix):=20M2=20RE-CLAIMED=20=E2=80=94?= =?UTF-8?q?=20F-redfix-4=20remedied=20at=20redfix-m2-harness@b5f2b10=20(wa?= =?UTF-8?q?rm=20state=20keyed=20by=20stack=20namespace);=20clearing=20cond?= =?UTF-8?q?ition=20re-run=20green=20on=20cc-ci,=20unit=20suite=20315->325;?= =?UTF-8?q?=20awaiting=20Adversary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01FS8p1esg57UAC69riNvuBX --- machine-docs/STATUS-redfix.md | 103 ++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/machine-docs/STATUS-redfix.md b/machine-docs/STATUS-redfix.md index 736dc46..fbd72fb 100644 --- a/machine-docs/STATUS-redfix.md +++ b/machine-docs/STATUS-redfix.md @@ -7,20 +7,101 @@ gitea, keycloak) → isolate → root-cause → classify (flake vs genuine; reci warm-machinery vs load) → FIX each (recipe PR or harness improvement) → verify green. No standing exceptions. Nothing merged. -## NOT DONE — claim withdrawn 2026-07-09 (standing VETO: F-redfix-4, keycloak) +## Gate: M2 RE-CLAIMED (F-redfix-4 remedy), awaiting Adversary — 2026-07-09 -The Adversary raised **F-redfix-4** (REVIEW-redfix.md, re-confirmation #8 @2026-07-08T23:56Z): the keycloak -enrollment is collision-free at the *domain* layer but NOT in *warm state* — `warmsnap.app_dir()` keys the -slot by bare recipe, so the live-warm reconciler (`warm-keycloak…`, `last_good`) and the newly-enrolled -data-warm canonical (`warm-canon-keycloak…`, `canonical.json` + `snapshot/`) share one slot -`/var/lib/ci-warm/keycloak/`. I reproduced it independently (see below). The phase is **NOT done**; the -keycloak DoD item is reopened. Do **not** merge `redfix-m2-harness` until this clears. +Prior DONE was withdrawn under the standing VETO F-redfix-4 (REVIEW-redfix.md re-confirmation #8 +@2026-07-08T23:56Z). The remedy is in. Re-claiming M2 for the keycloak item only. -Scope: keycloak only. The M1 classifications and the discourse / mattermost-lts / gitea / bluesky-pds / -mumble fixes are unaffected and remain Adversary-verified (M1 PASS @2026-06-18T01:18Z, and the five -non-keycloak M2 fixes content-verified through re-confirmation #7). +**WHAT.** F-redfix-4 is fixed at `redfix-m2-harness` @ **`b5f2b10`** (parent `07fc6d4`, the sha the M2 PASS +was given against). Warm state is now keyed by a **stack namespace**, not a bare recipe: +`canonical.canonical_ns(r)` is the single source from which BOTH the canonical's domain and its warm-state +slot derive. A live-warm provider (`r in warm.WARM_DOMAINS`) gets ns `canon-` → domain +`warm-canon-keycloak.ci.commoninternet.net` (**unchanged**) + slot `/var/lib/ci-warm/canon-keycloak/`. +Every other recipe keeps ns `` → on-disk layout of the 15 existing canonicals unchanged. -Gate status: **M2 REOPENED** — will be re-claimed once the F-redfix-4 remedy is in and verified. +Addresses all four consequences: (1)+(2) slots disjoint, neither deployment can replace the other's +known-good; (3) the outage race is removed with the shared slot; (4) `prune_stale`'s reconciler-dir +invariant is now structural — `/` never gains a `canonical.json`. + +Also: `warmsnap._assert_slot_not_foreign()` refuses to snapshot/restore a slot recorded against a different +domain (defence in depth; fails before the destructive swap, not at the next restore). The two comments +asserting the deployments "can never touch each other" are corrected (`canonical.py`, +`tests/keycloak/recipe_meta.py`). `WARM_CANONICAL = True` is retained — keycloak stays enrolled, no +skip-guard, no silent de-enrollment. + +**MIGRATION: none required.** On cc-ci `/var/lib/ci-warm/keycloak/` contains only `last_good` (the +canonical was never seeded), so no existing file changes slot. Verify: `ls -A /var/lib/ci-warm/keycloak/` +→ `last_good` only. + +**WHERE.** cc-ci repo, branch `redfix-m2-harness` @ `b5f2b10`. Files: `runner/harness/warmsnap.py`, +`runner/harness/canonical.py`, `runner/warm_reconcile.py`, `runner/run_recipe_ci.py`, +`tests/keycloak/recipe_meta.py`, `tests/unit/test_warmsnap.py`, `tests/unit/test_canonical.py`. + +**HOW to verify (1) — unit suite, cold clone, no docker needed.** + + git clone -q --branch redfix-m2-harness /tmp/v && cd /tmp/v + /nix/store/x188l04r3gfkh18gy1dpf05fv3kkrgs7-python3-3.12.8-env/bin/python3 -m pytest tests/unit -q + +EXPECTED: `325 passed` (baseline at parent `07fc6d4` is `315 passed`; +10 F-redfix-4 regression tests). +The 10 include `test_live_and_canonical_slots_are_disjoint`, `test_slot_maps_1to1_to_its_stack`, +`test_registry_path_of_live_warm_provider_is_not_the_reconciler_dir`, +`test_prune_stale_deenrolled_provider_spares_reconciler_last_good`, +`test_snapshot_refuses_to_clobber_another_domains_slot`, `test_restore_refuses_a_foreign_slot`. + +**HOW to verify (2) — the VETO's clearing condition, on the real node.** Non-destructive: writes only to a +scratch `CCCI_WARM_ROOT` + one throwaway docker volume. Uses the real idle `warm-canon-keycloak` stack for +the canonical side and a throwaway `warm-fakelive_…_data` volume as the live-warm stand-in (the live +`warm-keycloak` stack cannot be undeployed to snapshot it). + + ssh cc-ci + docker volume create warm-fakelive_ci_commoninternet_net_data + MP=$(docker volume inspect -f '{{.Mountpoint}}' warm-fakelive_ci_commoninternet_net_data); echo x > $MP/live.txt + git clone -q --branch redfix-m2-harness /tmp/v && mkdir -p /tmp/vw/keycloak + echo '10.7.1+26.6.2' > /tmp/vw/keycloak/last_good + cd /tmp/v/runner && CCCI_WARM_ROOT=/tmp/vw /nix/store/jag2131a95gw6ng7grig9pj3dn2q8vrv-python3-3.12.8-env/bin/python3 - <<'PY' + import sys; sys.path.insert(0, ".") + from harness import warmsnap as ws, canonical as c + LIVE, CANON = ws.live_slot("keycloak"), c.canonical_slot("keycloak") + CANON_DOM, LIVE_DOM = c.canonical_domain("keycloak"), "warm-fakelive.ci.commoninternet.net" + print(ws.snap_dir(LIVE), ws.snap_dir(CANON), c.registry_path("keycloak"), sep="\n") + ws.snapshot(CANON, CANON_DOM, version="canon-known-good") + ws.snapshot(LIVE, LIVE_DOM, version="live-last-good") # used to clobber the canonical + print("restore(canon) ->", ws.restore(CANON, CANON_DOM)["volumes"]) + print("restore(live) ->", ws.restore(LIVE, LIVE_DOM )["volumes"]) + print("last_good:", open("/tmp/vw/keycloak/last_good").read().strip()) + try: ws.snapshot(CANON, LIVE_DOM); print("FAIL: foreign snapshot allowed") + except ws.SnapshotError as e: print("foreign REFUSED:", str(e)[:60]) + PY + docker volume rm warm-fakelive_ci_commoninternet_net_data; rm -rf /tmp/vw /tmp/v + +EXPECTED (observed 2026-07-09): + + /tmp/vw/keycloak/snapshot <- live slot + /tmp/vw/canon-keycloak/snapshot <- canonical slot: DISJOINT + /tmp/vw/canon-keycloak/canonical.json <- registry NOT in the reconciler's dir + restore(canon) -> ['warm-canon-keycloak_ci_commoninternet_net_mariadb', + 'warm-canon-keycloak_ci_commoninternet_net_providers'] + restore(live) -> ['warm-fakelive_ci_commoninternet_net_data'] + last_good: 10.7.1+26.6.2 <- survived the canonical seed + foreign REFUSED: warm slot 'canon-keycloak' holds the known-good of ... + +Each `restore()` returns its OWN stack's volumes (the clearing condition). Pre-fix, the same script prints +one shared slot `/tmp/vw/keycloak/snapshot` for both and `restore(canon)` raises `SnapshotError`. + +**Node integrity after my own run of the above (2026-07-09).** Real warm root untouched +(`/var/lib/ci-warm/keycloak/` = `last_good` only); `warm-canon-keycloak` mariadb digest byte-identical +across the restore round-trip (`1201440268 48846` before and after, 391 files / 2 files); live +`warm-keycloak…/realms/master` → **200** throughout; throwaway volume removed; scratch removed. + +**Scope.** keycloak only. M1 classifications and the discourse / mattermost-lts / gitea / bluesky-pds / +mumble fixes are untouched by this commit and remain Adversary-verified (M1 PASS @2026-06-18T01:18Z; the +five non-keycloak M2 fixes content-verified through re-confirmation #7). + +**Known residual, NOT in F-redfix-4's clearing condition** (filed as B-redfix-5 in BACKLOG-redfix.md, not +blocking): `warm_reconcile.py`'s rollback `warmsnap.restore()` still sits outside the `try/except` that +guards the upgrade, so any restore failure (e.g. a corrupt/absent snapshot) leaves live keycloak undeployed +after `abra.undeploy()`. The F-redfix-4 race that made this reachable is gone; the pre-existing robustness +gap is not. Recorded, not silently dropped. The prior DONE text is retained verbatim below as the historical record of what was claimed on 2026-06-18.