myxce/.kiro/specs/raft-sim-conformance/research.md
Rongsong Shen 05febbc9a7 docs(specs): gap re-validation 2026-06-24 (5 HA/raft specs)
/kiro-validate-gap gap-focused sweep. Per-spec gap analysis written to each
spec's research.md (3 new, 2 appended), grounded in file:line evidence:

- config-upgrade-snapshot (new): real open gap — transport + (term,lsn) marker
  + COW substrate exist, but the entity-bearing snapshot PAYLOAD does not
  (16-byte LSN sentinel). HARD-blocked on entity-bearing-snapshots. Closes
  bug-857. Effort L (XL incl. blocker) / Risk High.
- raft-commit-safety (new): CLOSED — G4 Figure-8 commit (raft.c:733) + G6 vote
  freshness (raft.c:157) done, full sim coverage; the "(0,0) stopgap / broken
  build" concern is stale (build green, real vote tuple at cluster.c:435).
- raft-sim-conformance (new): mostly open — raft.tla is reference-only; only
  1 of 4 invariants (Log Matching) checked in code. Missing PRNG/replay,
  message-fault bag, scheduler, 3 invariant checkers, fault switches,
  scenarios. Effort L / Risk Medium.
- ha-repl-hardening (append): RESCOPE/superseded — 31/33 EARS closed; Req 2.3
  + Req 6 obsolete (CTRL_PRIMARY_COMMITTING removed). Residual: docs only.
- ha-convergence-recovery (append): done bar Task 8 (live E2E); bug-1028
  post-failover availability resolved 2026-06-23.

Skipped ha-elect-ctx-lifecycle (no requirements.md).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MSxwb2Xt5F84ATgiAZnH8V
2026-06-24 10:04:06 +00:00

14 KiB
Raw Blame History

Gap Analysis — raft-sim-conformance (2026-06-24)

Analyzed per .claude/skills/kiro-validate-gap/rules/gap-analysis.md. Scope: a single feature — an in-process, deterministic, seed-driven harness that drives N src/lib/raft library instances through raft.tla-Next-equivalent transitions and asserts the four Raft safety invariants every step (the "use the verified spec as an oracle" payoff; acceptance bar = catch a bug-851-class divergence on a deliberately-broken build, pass on the correct build).

1. Current State Investigation

Authoritative spec

  • raft/raft.tla — Diego Ongaro's canonical Raft (abstract spec; ends raft.tla:471). All Next actions present and located: Restart (167), Timeout (178), RequestVote (190), AppendEntries (204), BecomeLeader (229), ClientRequest (246), AdvanceCommitIndex (259), Receive (421) with UpdateTerm (406) and DropStaleResponse (415), DuplicateMessage (443), DropMessage (448), Next disjunction (454). The .tla file carries NO embedded invariant THEOREM block; the four safety invariants (Election Safety, Log Matching, Leader Completeness, State Machine Safety) are tracked prose-side in src/controller/CLAUDE.md. No TLC config (*.cfg), no tla2tools/TLC tooling exists in-repo — the spec is a reference document, not a model-checked artifact today.

How the C library is checked against raft.tla today

  • It is not, automatically. Conformance is enforced by hand: src/controller/CLAUDE.md holds the TLA+ variable→code and action→code mapping plus a manual "Protocol Verification Checklist." There is no trace generation, no invariant checker that runs per-step, and no randomized simulation that asserts the four invariants.
  • src/controller/raft/ does not exist; the library lives at src/lib/raft/ (raft.c 33 KB, raft.h 30 KB, raft_sim.c 18 KB). Per src/controller/CLAUDE.md, the controller's raft.c was election-only; the full election+log+commit protocol now lives in the library src/lib/raft (deps raft-log-replication + raft-commit-safety landed — callbacks present and exercised in test_raft.c).

Existing harness (src/lib/raft/raft_sim.c, XCE_TESTING-gated)

Already present (the election + partial log layer it was grown to for the dependency specs), all consuming published library entry points verbatim:

  • raft_sim_init, raft_sim_deliver_pending (vote RPC routing via raft_on_vote_request/_reply), raft_sim_deliver_append_entries (AE routing via raft_on_append_entries_request/_reply), raft_sim_start_all_elections, raft_sim_fire_timeout, raft_sim_partition, raft_sim_advance_commit (raft_advance_commit), raft_sim_restart_node (models Restart, preserves persisted term/votedFor/log/commit_index), and one invariant checker: raft_sim_check_log_matching (raft_sim.c:424).
  • In-memory adapter callbacks already back the four log callbacks + on_commit_advanced into per-node raft_sim_node_t (log[], log_count, commit_index, commit_advanced_count, trace_buf). The library holds no entry bytes — the simulator owns storage, exactly as the dependency design anticipated.
  • raft.h XCE_TESTING section (raft.h:432+) already defines raft_sim_rpc_t, raft_sim_entry_t, raft_sim_ae_t, raft_sim_node_t, raft_sim_cluster_t, and bounds RAFT_SIM_MAX_NODES=8, RAFT_SIM_MAX_LOG=64, RAFT_SIM_ENTRY_LEN=16, RAFT_SIM_MAX_AE=64.
  • test_raft.c (7011 lines) already exercises election + AE + commit + a hand-built partition/catch-up convergence path and both directions of raft_sim_check_log_matching (pass-on-converged / fail-on-divergence).

All library entry points the harness needs already exist in raft.h

raft_log_append, raft_send_append_entries, raft_advance_commit, raft_on_append_entries_request/_reply, raft_on_vote_request/_reply, raft_init, raft_start_election, raft_on_election_timeout, raft_snapshot — all declared. Constraint: raft_snapshot_t (raft.h:69-76) exposes term/role/voted_for/votes only — NOT log or commit_index. So Election Safety can read from raft_snapshot, but Log Matching / Leader Completeness / State Machine Safety MUST read the harness-owned in-memory log[]/commit_index/applied[] (Req 3.6 "observable per-node state only" is satisfied by the in-memory mirror, not by reaching into raft.c).

Spec phase state

spec.json = tasks-generated, all 3 gates auto-approved (-y), ready_for_implementation. tasks.md has 8 task groups (1.1–8.1); every task box is unchecked [ ] — none of the conformance machinery is implemented. The brief/requirements/design are complete and internally consistent.

2. Requirement-to-Asset Map (gaps tagged Missing / Unknown / Constraint)

Req Need Existing asset Gap
1.1–1.6 Deterministic seeded driver Seeded PRNG (SplitMix64), (N,seed) byte-identical replay, message bag, action trace buffer, no threads/sockets/wall-clock raft_sim_init (no seed/PRNG); synchronous already Missing: PRNG, seed, raft_sim_conformance_init, bag (bag[]/bag_count), trace buffer, raft_sim_run replay. Constraint: must extend the existing raft_sim_cluster_t/_node_t, not rewrite.
2.1–2.6 Drive raft.tla Next actions via library entry points Restart/Timeout/RequestVote/BecomeLeader/ClientRequest/AppendEntries/AdvanceCommitIndex/Receive(UpdateTerm+DropStaleResponse)/Duplicate/Drop through published API Vote+AE delivery, restart, advance-commit, partition all wired verbatim to library handlers Missing: unified raft_sim_step scheduler selecting only enabled actions; ClientRequest via raft_log_append; bag-based Duplicate/Drop (current delivery drains a queue, no per-message drop/dup/reorder). UpdateTerm/DropStaleResponse are already inside the library handlers (consumed, not re-modeled).
3.1–3.6 Per-step assertion of all four invariants Election Safety, Log Matching, Leader Completeness, State Machine Safety; first-failure halt; observable-state-only Only raft_sim_check_log_matching exists Missing: raft_sim_check_election_safety, _leader_completeness, _state_machine_safety, _check_all (first-failure composer); applied[] per-node state machine view.
4.1–4.5 Targeted adversarial scenarios partition/heal, restart-churn, drop/dup/reorder, Figure-8, stale-rejoin (bug-851 G5) A hand-built partition/catch-up test exists in test_raft.c (not a reusable named scenario) Missing: 5 named raft_sim_scenario_* entry points asserting all four invariants throughout.
5.1–5.4 Failure-trace capture/replay seed+N+failing-invariant+ordered action trace dump; quiet-on-success; replay reproducibility per-node trace_buf (string events only, not structured actions) Missing: structured raft_sim_action_t trace + raft_sim_dump_trace; replay tie-in to raft_sim_run.
6.1–6.4 CI-runnable target M-seed sweep + all scenarios in src/lib/testsuite, ASAN/UBSAN-clean, bounded runtime test_raft.c already builds/runs under XCE_TESTING + sanitizers Missing: the sweep harness + bounds; mechanics (test entry point, sanitizer build) already exist. Low-risk.
7.1–7.4 Deliberately-broken-build negative test compile-time fault switches (RAFT_SIM_BREAK_TERM_STAMP, RAFT_SIM_BREAK_TRUNCATE) confined to adapter; both-directions assert none Missing. Constraint: switches must live in the adapter (raft_sim.c callbacks), never raft.c — the boundary forbids touching protocol code. This is the acceptance teeth.
8.1–8.3 Optional TLC-consumable trace export spec-named machine-readable record stream, off by default none Missing (optional [ ]*, deferred). Unknown/Research: no TLC tooling in-repo to actually consume it — export format can be written but cross-checking against raft.tla via TLC is unproven.

Cross-cutting findings

  • Currently CHECKED against raft.tla (in code): Log Matching only (one checker, raft_sim.c:424), and only on hand-driven clusters — not per-step, not over randomized runs. Election Safety / Leader Completeness / State Machine Safety are UNCHECKED in code (asserted only by prose checklist). Of the Next actions, Restart / Timeout / RequestVote / AppendEntries / AdvanceCommitIndex / Receive are driven by the existing sim; ClientRequest, per-message Duplicate/Drop/reorder, and a unified enabled-action scheduler are not yet driven.
  • raft.tla does NOT model durability (restores log intact on Restart, raft.tla:165-174) — so the harness, like the spec, cannot and should not assert the commit-durability invariant (that rests on ha-commit-durability tests). This correctly scopes the harness to the four safety invariants only. Confirmed against memory.

3. Implementation Approach Options

Option A — Extend raft_sim.c / test_raft.c in place (spec's chosen path)

Add PRNG, bag, action trace, three new checkers + composer, raft_sim_step/_run, five scenarios, fault switches — all inside the existing XCE_TESTING sim section and test file. Reuse raft_sim_node_t/_cluster_t (extend with bag[], applied[], seed, trace[]).

  • ✅ Matches the spec boundary exactly (owns raft_sim.c + raft.h XCE_TESTING + test_raft.c); reuses the verbatim-library-entry-point pattern already proven by the dependency specs; no protocol change; sanitizer/CI plumbing already in place.
  • ❌ raft_sim.c grows well past the existing 18 KB (toward the 800-line guideline ceiling); test_raft.c is already 7011 lines.

Option B — New raft_conformance.c / test_raft_conformance.c modules

Put the harness (PRNG, bag, scheduler, checkers, scenarios, fault switches) in dedicated files; keep raft_sim.c as the thin election/AE adapter.

  • ✅ Honors "many small files"; isolates the oracle from the dependency-era sim; easier to read/maintain; keeps test_raft.c from ballooning further.
  • ❌ Deviates from the spec's stated file boundary ("owns raft_sim.c … ONLY"); needs a meson build wiring change and a boundary-note update; minor duplication of the in-memory adapter.

Keep the in-memory adapter callbacks, bag, PRNG, and the four checkers in raft_sim.c/raft.h (they are genuinely "simulator" primitives and reuse existing node state). Lift the scheduler + five scenarios + M-seed sweep + negative test into a new test_raft_conformance.c (or a clearly-fenced section) so test_raft.c does not grow unbounded. Fault switches stay in raft_sim.c (adapter) per boundary.

  • ✅ Best file-size/cohesion balance; preserves the "harness drives only published entry points" boundary; checkers stay reusable; the heavy scenario/sweep code is isolated.
  • ❌ Splits ownership across two test files — needs a one-line boundary amendment in tasks.md/design.md (Allowed Dependencies already permit a new test file under src/lib/testsuite).

4. Effort & Risk

  • Effort: L (1–2 weeks). Eight task groups; the hard parts are the seeded scheduler with enabled-action selection (4.2), deterministic byte-identical replay (4.4/1.6), the three new invariant checkers (3.1/3.3/3.4) and the bug-851 Figure-8 / stale-rejoin scenarios (5.4/5.5). Substantial scaffolding already exists (adapter callbacks, partition, restart, Log Matching, AE/vote delivery), which pulls it down from XL.
  • Risk: Medium. Mitigants: all library entry points exist and are stable; deps landed; no protocol change; synchronous/no-I/O design removes concurrency risk; the negative test self-validates checker sensitivity. Residual risk: (a) determinism leaks (pointer values / iteration order in action selection) — must be audited (Req 1.2); (b) the Figure-8 and stale-rejoin scenarios must be precise enough to actually exercise the bug-851 mechanism (else the oracle's "teeth" are blunt); (c) keeping fixed-size bounds (RAFT_SIM_MAX_*) large enough for randomized runs without blowing the CI time/ASAN budget. The optional TLC export (Req 8) is Unknown — no in-repo TLC to validate it.

5. Recommendations for Design Phase

  • Preferred approach: Option C (hybrid) — checkers + bag + PRNG + fault switches in raft_sim.c/raft.h; scheduler + scenarios + sweep + negative test in a new src/lib/testsuite/test_raft_conformance.c. Amend the tasks.md/design.md file boundary with that one new test file (design "Allowed Dependencies" already permits it). If strict adherence to the stated single-file boundary is required, fall back to Option A and accept the file-size growth (flag a clang-format/size exception).
  • Key decisions to lock: (1) PRNG = SplitMix64 inlined, single cluster.seed source — forbid any wall-clock/address input (Req 1.2); (2) invariant checkers read ONLY harness-owned in-memory state (log[]/commit_index/applied[]) + raft_snapshot (role/term) — never raft.c internals (Req 3.6); (3) ClientRequest drives raft_log_append on a forced leader; Duplicate/Drop/reorder mutate ONLY the bag (Req 2.5); (4) fault switches confined to adapter callbacks, default-off, never set in the default CI run (Req 7.3).
  • Research items to carry forward: (a) TLC consumability (Req 8) — no tla2tools/.cfg exists in-repo; decide whether to add TLC tooling or keep Req 8 as a documented-format-only deferral. (b) confirm RAFT_SIM_MAX_LOG=64 / step bounds are large enough for randomized convergence yet inside the ASAN/UBSAN CI budget (Req 6.4). (c) verify the existing per-node applied[]/state-machine view needs adding (not present in current raft_sim_node_t) for State Machine Safety (Req 3.4).
  • Dependency watch: harness consumes raft-log-replication + raft-commit-safety callback signatures verbatim; per design "Revalidation Triggers," any change to on_persist_entry/on_get_entry_term/on_send_append_entries/on_truncate_from/ on_commit_advanced or the (lastLogTerm,lastLogIndex) freshness contract re-validates this spec.