/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
6.6 KiB
Gap Analysis — raft-commit-safety (2026-06-24)
Scope: the commit-safety (G4 Figure-8 commit advance) + election-freshness (G6
(lastLogTerm,lastLogIndex)vote/PreVote) half ofraft/raft.tla, insrc/lib/raftonly. Boundary owns the library entry points + callbacks; the controller barrier/durability, config markers/snapshot, and conformance harness are owned elsewhere (ha-term-safety,raft-sim-conformance). HARD dep:raft-log-replication.
Executive Summary
This spec is DONE. All tasks 1.1–8.1 are [x] in tasks.md, and the actual code confirms it: the library half (G4 commit-advance + G6 vote/PreVote freshness) is implemented in src/lib/raft/{raft.h,raft.c,raft_sim.c}, fully tested in src/lib/testsuite/test_raft.c, and already consumed by the controller via the ha-term-safety adapter. The historical "(0,0) stopgap disables G6 / build broken" state recorded in project memory is stale — it has been superseded: the controller build is green (later commits bug-1060/bug-1067 build on top of it), the wire schema carries last_log_term/last_log_index, and cluster.c feeds the real candidate tuple into raft_on_vote_request. No unsafe stopgap remains in this spec's surface.
Requirement-to-Asset Map
| Req | Asset (file:line) | Status |
|---|---|---|
R1 Library-owned commit_index + current-term advance (G4) |
raft.h:62 (commit_index), raft.h:199 (initial_commit_index), raft.c:733 raft_advance_commit — leader-only -EPERM (raft.c:738), Max(agreeIndexes) scan (raft.c:740), current-term gate before advance, monotonic |
DONE |
R2 on_commit_advanced callback triggers barrier (no durability in lib) |
raft.h:169 decl (REQUIRED), raft.c:58 NULL-check -EINVAL at init, raft.c:780 fired once on strict advance; AE-reply does not advance (test_ae_reply_does_not_advance_commit) |
DONE |
R3 (lastLogTerm,lastLogIndex) binding-vote freshness (G6) |
raft.c:157 raft_log_ok, applied in raft_on_vote_request (raft.c:234) BEFORE on_vote_eligible; extended sig raft.h:267 |
DONE |
| R4 Same freshness on PreVote, side-effect-free | raft_on_pre_vote_request (raft.c:291 raft_log_ok), no state mutation / no on_persist; sig raft.h:329 |
DONE |
| R5 Preserve sync commit barrier + durability as policy | Library returns status codes only; barrier remains in controller commit.c/ha.c; on_commit_advanced is trigger-only |
DONE |
| R6 Deterministic Figure-8 + Leader Completeness + freshness sim | test_raft.c: test_advance_commit_figure8_hold_previous_term (2765), test_sim_7_1_figure8_commit_refusal (4095), test_sim_7_2_leader_completeness_across_changes (4161), test_sim_7_3_freshness_gate_lexicographic (4245), test_sim_7_4_on_commit_advanced_trigger_semantics (4345); all RUN_TEST-registered |
DONE |
No gaps tagged Missing/Unknown within the spec boundary.
Downstream Consumption (out-of-scope here, but verified present)
The controller integration that routes the WAL/commit barrier through the library is owned by ha-term-safety, and it is wired:
- Wire schema:
src/schema/controller.capnp:833-834addslastLogTerm @4/lastLogIndex @5to the RequestVote message. - Vote path:
src/controller/cluster.c:395-396,435-436reads the candidate'slast_log_term/last_log_indexfrom the wire and passes the real tuple intoraft_on_vote_request(...). No(0,0)stopgap — the application freshness gate (config_tag/durable_floor) is layered IN ADDITION to the librarylogOk, not in place of it. - PreVote path:
cluster.c:489,533plumbs the same tuple intoraft_on_pre_vote_request. - Commit source:
src/controller/ha.c:1686ha_cb_commit_advanced_stub(name is legacy — it is a real, wired callback) callscommit_set_raft_commit_index(...), making the library's current-term-gatedcommit_indexthe steady-state commit SOURCE; the term-blindha_compute_safe_committed_lsnis no longer the steady-state driver. Callback table atha.c:1741. - Build state: green. Later commits
61e1391 fix bug-1060 bug-1067and theha-term-safetyseries build on top of the extended vote signature, confirming the earlier "vote-signature extension BROKE cluster.c" condition is resolved.
Options (retrospective — for any follow-up only)
- Option A — Extend existing (CHOSEN, complete). The library log/election core already existed (
raft-library+raft-log-replication); this spec extendedraft.{c,h}with one new action, one callback, and two freshness args. Trade-off accepted: keeps everything in the pure library, no new TU. ✅ minimal surface, leverages existing callback contract. - Option B — Library owns
logOk(CHOSEN for freshness plumbing). Vote/PreVote signatures gainedcand_last_log_term/cand_last_log_indexand the library now owns thelogOkdecision (formerly the caller/dispatcher's). This is the "freshness-decision locus move" called out in R5.4. ✅ single authoritative predicate (raft_log_ok) shared by both paths. - Option C — Hybrid / new component. Not needed; rejected implicitly. A separate commit module would have split the
commit_indexinvariant away from the log model it depends on.
Effort & Risk
- Effort: S (already delivered). Remaining work for this spec = none. Verification re-run only.
- Risk: Low. Pure, synchronous, deterministic library code with full simulator coverage (Figure-8 hold/release, Leader Completeness across forced leader changes, lexicographic freshness, trigger-fire-count). No I/O, no threads; controller integration validated downstream by
ha-term-safetylive soak.
Recommendations for design phase
- No further design needed for raft-commit-safety. Treat as closed; G4/G6 of bug-851 are addressed at the library layer.
- Carry-forward (owned elsewhere, not this spec):
ha-term-safetyowns the controller wiring already in place — keep the bug-828/854/991/1060/1067 follow-ups there.raft-sim-conformanceowns the full TLC trace-check harness (Leader Completeness oracle) — this spec only seeds the deterministic sim assertions it will consume.
- Stale-memory correction (research item closed): the project-memory note "vote-signature extension BROKE the controller build (cluster.c); (0,0) stopgap unsafe—disables G6" is no longer current. Verified 2026-06-24: build green, real tuple wired, no stopgap. Update the memory entry accordingly.
Research-Needed
- None within this spec's boundary. (Open HA items — bug-828 residual / bug-854 client discovery — are tracked under
ha-term-safety, not here.)