myxce/.kiro/specs/raft-commit-safety/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

6.6 KiB
Raw Blame History

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 of raft/raft.tla, in src/lib/raft only. 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-834 adds lastLogTerm @4 / lastLogIndex @5 to the RequestVote message.
  • Vote path: src/controller/cluster.c:395-396,435-436 reads the candidate's last_log_term/last_log_index from the wire and passes the real tuple into raft_on_vote_request(...). No (0,0) stopgap — the application freshness gate (config_tag/durable_floor) is layered IN ADDITION to the library logOk, not in place of it.
  • PreVote path: cluster.c:489,533 plumbs the same tuple into raft_on_pre_vote_request.
  • Commit source: src/controller/ha.c:1686 ha_cb_commit_advanced_stub (name is legacy — it is a real, wired callback) calls commit_set_raft_commit_index(...), making the library's current-term-gated commit_index the steady-state commit SOURCE; the term-blind ha_compute_safe_committed_lsn is no longer the steady-state driver. Callback table at ha.c:1741.
  • Build state: green. Later commits 61e1391 fix bug-1060 bug-1067 and the ha-term-safety series 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 extended raft.{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 gained cand_last_log_term/cand_last_log_index and the library now owns the logOk decision (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_index invariant 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-safety live 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-safety owns the controller wiring already in place — keep the bug-828/854/991/1060/1067 follow-ups there.
    • raft-sim-conformance owns 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.)