journal(redfix): wake #8 — VETO F-redfix-4 reproduced independently, root-caused, fixed at b5f2b10; decision recorded (warm state keyed by stack namespace); B-redfix-5 residual filed
Some checks failed
continuous-integration/drone/push Build is failing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FS8p1esg57UAC69riNvuBX
This commit is contained in:
2026-07-09 00:13:47 +00:00
parent cfea875b2b
commit 397c303188
3 changed files with 127 additions and 0 deletions

View File

@ -1609,3 +1609,29 @@ trim job observed → adequate; revisit only if a cap is ever needed.
`dashboard/dashboard.py` + `tests/unit/test_dashboard.py` would be reformatted — confirmed pre-existing
at HEAD f68f1c5, outside the settings diff. Flagged for the dashboard owner / orchestrator; not fixed
here (narrow scope).
## Warm state is keyed by STACK NAMESPACE, not by recipe (phase redfix, F-redfix-4, 2026-07-09)
**Settled.** `/var/lib/ci-warm/<ns>/` is a slot owned by exactly ONE deployed stack. `ns` comes from
`canonical.canonical_ns(recipe)` for the data-warm canonical and `warmsnap.live_slot(recipe)` for the
live-warm reconciler. A recipe that is both (only `keycloak` today, via `warm.WARM_DOMAINS`) gets
`canon-<recipe>` for its canonical, so the two never share a slot. The canonical's **domain and its slot
derive from the same `canonical_ns()`**, so they cannot drift apart — that coupling is the invariant, not
the `canon-` string. Every slot `<ns>` maps 1:1 to the stack at `warm.stable_domain(<ns>)`.
Rejected alternatives:
- **Skip `seed_canonical` for `WARM_DOMAINS` recipes.** Silently de-enrolls keycloak and re-opens the DoD
item ("keycloak enrolled … verified green"). The Adversary named this as not acceptable; agreed.
- **Key every slot by the full domain / stack name** (the "purest" model). Correct, but it relocates the
reconciler's `last_good` for keycloak+traefik and `canonical.json` + `snapshot/` for 15 existing
canonicals on a live node, requiring a migration shim for a phase whose mandate is to fix red, not
re-architect. The chosen scheme is the same model applied only where two stacks actually contend, and
needs no migration (keycloak's canonical had never been seeded).
- **Guard-only (`_assert_slot_not_foreign` alone).** Turns silent destruction into a loud failure but leaves
keycloak's canonical unable to ever seed. Kept as defence in depth BEHIND the slot separation, not as the
fix.
Consequence for future enrollments: adding a recipe to `warm.WARM_DOMAINS` automatically namespaces its
canonical on both axes. `prune_stale()`'s "reconciler dirs are never pruned" invariant is now structural
(a `<recipe>/` dir never gains a `canonical.json`) rather than an incidental property that de-enrollment
could falsify.