/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
14 KiB
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; endsraft.tla:471). AllNextactions present and located:Restart(167),Timeout(178),RequestVote(190),AppendEntries(204),BecomeLeader(229),ClientRequest(246),AdvanceCommitIndex(259),Receive(421) withUpdateTerm(406) andDropStaleResponse(415),DuplicateMessage(443),DropMessage(448),Nextdisjunction (454). The.tlafile carries NO embedded invariantTHEOREMblock; the four safety invariants (Election Safety, Log Matching, Leader Completeness, State Machine Safety) are tracked prose-side insrc/controller/CLAUDE.md. No TLC config (*.cfg), notla2tools/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.mdholds 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 atsrc/lib/raft/(raft.c33 KB,raft.h30 KB,raft_sim.c18 KB). Persrc/controller/CLAUDE.md, the controller'sraft.cwas election-only; the full election+log+commit protocol now lives in the librarysrc/lib/raft(depsraft-log-replication+raft-commit-safetylanded — callbacks present and exercised intest_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 viaraft_on_vote_request/_reply),raft_sim_deliver_append_entries(AE routing viaraft_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_advancedinto per-noderaft_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.hXCE_TESTINGsection (raft.h:432+) already definesraft_sim_rpc_t,raft_sim_entry_t,raft_sim_ae_t,raft_sim_node_t,raft_sim_cluster_t, and boundsRAFT_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 ofraft_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 theNextactions, Restart / Timeout / RequestVote / AppendEntries / AdvanceCommitIndex / Receive are driven by the existing sim;ClientRequest, per-messageDuplicate/Drop/reorder, and a unified enabled-action scheduler are not yet driven. - raft.tla does NOT model durability (restores
logintact on Restart,raft.tla:165-174) — so the harness, like the spec, cannot and should not assert the commit-durability invariant (that rests onha-commit-durabilitytests). 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.hXCE_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.cgrows well past the existing 18 KB (toward the 800-line guideline ceiling);test_raft.cis 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.cfrom 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.
Option C — Hybrid (recommended)
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 undersrc/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 newsrc/lib/testsuite/test_raft_conformance.c. Amend thetasks.md/design.mdfile 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 aclang-format/size exception). - Key decisions to lock: (1) PRNG = SplitMix64 inlined, single
cluster.seedsource — 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) — neverraft.cinternals (Req 3.6); (3)ClientRequestdrivesraft_log_appendon 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/.cfgexists in-repo; decide whether to add TLC tooling or keep Req 8 as a documented-format-only deferral. (b) confirmRAFT_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-nodeapplied[]/state-machine view needs adding (not present in currentraft_sim_node_t) for State Machine Safety (Req 3.4). - Dependency watch: harness consumes
raft-log-replication+raft-commit-safetycallback signatures verbatim; per design "Revalidation Triggers," any change toon_persist_entry/on_get_entry_term/on_send_append_entries/on_truncate_from/on_commit_advancedor the(lastLogTerm,lastLogIndex)freshness contract re-validates this spec.