docs: record the bootstrap guard (CLAUDE.md + Phase 7 note)
v2-ci / build (push) Successful in 4m38s
v2-ci / unit-tests (push) Failing after 16m7s

The simultaneous-cold-start split-brain, carried as a Phase 7 "candidate follow-up,
not shipped", is now closed by the opt-in Cluster:BootstrapGuard (279d1d0f). Documents
the guard in CLAUDE.md's bootstrap section and marks the Phase 7 finding closed.

Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
Joseph Doherty
2026-07-24 08:35:16 -04:00
parent 279d1d0fb1
commit d1dac87f6f
2 changed files with 12 additions and 7 deletions
@@ -43,13 +43,16 @@ pair has exactly one Primary, which is what matters.
- **Graceful failover restarts the node in-place and it rejoins** (no split), because a single peer that
is already Up answers the restarting node's InitJoin. The **simultaneous cold-start** split (Phase 6
finding) only bites when BOTH pair nodes start from nothing at once — see the runbook mitigation.
- **Carried Phase-6 finding — simultaneous cold-start of both pair VMs.** On the real co-located VMs a
site's two VMs can power-cycle together; two self-first seeds can then each form a 1-node cluster
(split brain, two Primaries in one pair). The docker-dev rig serializes with a founder healthcheck +
`depends_on: service_healthy`. **Production mitigation (operational, not code):** stagger the two VMs'
service-manager start, or start the designated founder VM first. A product-level guard (e.g. a brief
reachability-gated join delay on the non-founder) is a candidate follow-up if operational staggering
proves fragile — tracked as a Phase 7 note, not shipped.
- **Carried Phase-6 finding — simultaneous cold-start of both pair VMs — NOW CLOSED by a product guard
(`279d1d0f`).** On the real co-located VMs a site's two VMs can power-cycle together; two self-first
seeds can then each form a 1-node cluster (split brain, two Primaries in one pair). Two mitigations now
exist: (1) **operational** stagger the two VMs' service-manager start / start the founder first (the
docker-dev `depends_on: service_healthy` serialization, still used by site-b); (2) **product** — the
opt-in `Cluster:BootstrapGuard` (default off), which makes the lower-address node the founder and the
higher node probe-then-join, arbitrating the race inside the join decision. Live-gated on the site-a
pair (guard on, serialization removed): simultaneous start → 250/240, no split; higher-node cold-start
-alone → self-forms → 250. See `docs/Redundancy.md` §"Bootstrap guard". The guard does NOT need the
compose `depends_on` that production hardware lacks — it is the production-faithful fix.
---