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

53 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.)