docs(bf-4nybh): confirm Mode A partitioning gaps a/b/c as genuine

Independent re-verification of the three candidate coverage gaps. All three
CONFIRMED:
(a) survivor-stability/minimal-reshuffling not pinned in test_mode_a_pod_reassignment
(b) no Mode A scale-up 3->4 (the p4_topology_chaos 3->4 test is a separate
    Rebalancer/Topology subsystem using router::score, a different hash
    construction than rendezvous_score, and never touches ModeACoordinator)
(c) no pinned shard->owner golden vector; owner_for_shard is never called by
    any test, only the boolean owns_shard is asserted

Genuine gaps = {a, b, c}.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-07-09 09:40:29 -04:00
parent a05055b8ad
commit fb5eeec7f9

121
notes/bf-4nybh.md Normal file
View file

@ -0,0 +1,121 @@
# bf-4nybh — Confirm-or-refute the Mode A partitioning test gaps (step 3)
Investigation step 3 of umbrella **bf-1zn8a**. Independent re-verification of the
three candidate coverage gaps (a/b/c) so later siblings attack only REAL holes.
## TL;DR — all three CONFIRMED (genuine gaps)
| # | Candidate gap | Verdict | Key evidence |
|---|---|---|---|
| (a) | survivor-stability / minimal-reshuffling not pinned | **CONFIRMED** | `anti_entropy.rs:1665` records only `pod3_owned_initial`; never asserts survivors keep their shards |
| (b) | no scale-UP (3→4); only scale-DOWN (3→2) | **CONFIRMED** | only Mode A resize is 3→2 (`anti_entropy.rs:1714`); the one 3→4 test is a *different subsystem* |
| (c) | no pinned shard→owner golden vector vs `top1_by_score(hash(s‖pid))` | **CONFIRMED** | `owner_for_shard` is never called by any test; only boolean `owns_shard` is asserted |
**GENUINE gaps = { (a), (b), (c) }** — all three. Nothing was refuted as
already-covered.
---
## Verdict (a) — CONFIRMED: survivor-stability / minimal-reshuffling
`test_mode_a_pod_reassignment` (`crates/miroir-core/src/anti_entropy.rs:1665`)
goes 3→2 (kill pod-3) and asserts two things about pod-3's *old* shards:
- each previously-pod-3 shard is now owned by exactly one of pod-1/pod-2 —
`owner_count == 1`, `anti_entropy.rs:1731-1737`
- all 64 shards are still owned — `total_owned == 64`, `anti_entropy.rs:1751-1755`
What it records initially is **only** `pod3_owned_initial`
(`anti_entropy.rs:1704-1711`). It never records pod-1's or pod-2's pre-removal
shards and never asserts they are unchanged afterward. A buggy implementation
that reshuffled **every** shard on each peer-set change — while still preserving
exactly-one-owner + full coverage — would pass this test.
The "minimal reshuffling" property is an explicit Mode A design invariant:
`mode_a_coordinator.rs:19` ("ownership with minimal reshuffling") and
`plan.md` §14.5 (cited at `mode_a_coordinator.rs:1`). Un-pinned for the
background-work ownership path. **CONFIRMED.**
## Verdict (b) — CONFIRMED: no Mode A scale-up
The only Mode A peer-set *mutation* anywhere is the 3→2 downscale
(`anti_entropy.rs:1714`, `peer_set_2pods`). A whole-crate grep for `"pod-4"` /
`peer_set_4pods` / `4pod` returns **nothing** — no Mode A test adds a 4th pod.
One apparent 3→4 scale-up test exists —
`p4_topology_chaos.rs:282` (`chaos_rebalance_optimal_movement`, adds `node-3`) —
but it does **not** cover Mode A and therefore does not refute the gap:
- It runs in the **Rebalancer / Topology** subsystem, which never references
`ModeACoordinator` / `owner_for_shard` / `rendezvous_score`
(`grep` of `rebalancer.rs` / `topology.rs` for those symbols → empty).
- It routes through `assign_shard_in_group` → router's `score()`
(`router.rs:25`, `router.rs:12`), which is a **different hash construction**
from Mode A's `rendezvous_score`:
- router `score(shard_id: u32, node_id: &str)`: `XxHash64::with_seed(0)`
`shard_id.hash(h)` (raw `u32` bytes) → `node_id.hash(h)` (std `str` Hash,
appends a `0xff` terminator). No separator.
- Mode A `rendezvous_score(shard_id: &str, peer_id: &str)`
(`mode_a_coordinator.rs:112`): `XxHash64::with_seed(0)`
`write(shard_id.as_bytes())` (decimal string, e.g. `"42"`) → `write(b"||")`
`write(peer_id.as_bytes())`. No `0xff` terminator.
Different input encoding, different separator, different terminator ⇒ different
scores, different code path. The p4 test validates the rebalancer's migration
accounting, not Mode A rendezvous partitioning. **Mode A scale-up is genuinely
untested. CONFIRMED.**
## Verdict (c) — CONFIRMED: no pinned shard→owner golden vector
`owner_for_shard` (`mode_a_coordinator.rs:123`) is the only API that returns a
*concrete* owner, yet it has exactly **one** call site — internal, inside
`owns_shard` (`mode_a_coordinator.rs:156`). No test calls it
(`grep -rn "owner_for_shard" --include=*.rs crates/ | grep -v fn` → only `:156`).
Every Mode A ownership test instead calls the boolean `owns_shard` and asserts
only *properties*:
- `test_owns_exactly_one_peer_per_item` (`mode_a_coordinator.rs:422`): exactly
one pod owns each item — never *which* pod.
- `test_mode_a_three_pods_each_shard_processed_once`
(`anti_entropy.rs:1541`): exactly-one-owner + full coverage + even spread.
- `test_rendezvous_score_*` (`mode_a_coordinator.rs:317/325/333`): determinism
+ peer-sensitivity + shard-sensitivity of the raw score — never a pinned
owner.
- `tests/hash_fixtures.rs` only `println!`s (no `assert!`) and covers the
router's key→shard *modulo* mapping, not Mode A ownership.
So no test pins a specific shard→owner mapping against the documented
`top1_by_score(hash(shard_id‖peer_id))` rule (`mode_a_coordinator.rs:112-118`).
A change to the hash construction that preserves those properties while silently
reassigning all ownership — e.g. swapping the `"||"` separator
(`mode_a_coordinator.rs:115`) to `"|"`, swapping the shard/peer write order, or
reseeding `XxHash64` (`:113`) — would pass every existing test yet break the
documented `hash(s‖pid)` contract. A golden-vector test (fixed `peer_set` +
hardcoded expected owner per shard) is the missing guard. **CONFIRMED.**
---
## Corroboration note
These verdicts independently reproduce the gap analysis already recorded in the
sibling note `notes/bf-1zn8a.md` (step 3, lines 153-208). This bead's added
value is the explicit, cited refutation-of-the-refutation for (b): the
`p4_topology_chaos` 3→4 test looks like it could cover scale-up, but it targets
the separate Rebalancer/Topology subsystem with router's `score()` — a different
hash construction — so it does **not** close the Mode A gap.
## Acceptance criteria (bf-4nybh)
- [x] Verdict for (a) minimal-reshuffling: **CONFIRMED**
`anti_entropy.rs:1665,1704-1711,1731-1737,1751-1755`; property at
`mode_a_coordinator.rs:19`.
- [x] Verdict for (b) scale-up: **CONFIRMED** — only Mode A resize is 3→2
(`anti_entropy.rs:1714`); no `pod-4` crate-wide; the p4 3→4 test
(`p4_topology_chaos.rs:282`) uses router `score()` (`router.rs:12`) ≠
`rendezvous_score` (`mode_a_coordinator.rs:112`) and never touches
`ModeACoordinator`.
- [x] Verdict for (c) pinned hash vector: **CONFIRMED**`owner_for_shard`
(`mode_a_coordinator.rs:123`) never called by tests; only boolean
`owns_shard` asserted; rule at `:112-118`.
- [x] Final GENUINE gaps = { (a), (b), (c) }.