Commit graph

333 commits

Author SHA1 Message Date
95629797f8 feat(raft-sim-conformance): optional TLC-consumable trace export, default-off (task 8.1) 2026-06-24 16:49:36 +00:00
ffeb7c2cf5 docs(raft-sim-conformance): reflect runtime fault-injection flag (vs compile-time macro) in design 2026-06-24 16:36:54 +00:00
90197f8595 test(raft-sim-conformance): negative test proving oracle teeth, both directions (task 7.2) 2026-06-24 16:30:20 +00:00
62523892d9 feat(raft-sim-conformance): runtime fault-injection switch (G1 term-stamp, G3 truncate) (task 7.1) 2026-06-24 16:15:13 +00:00
7087dc19d9 docs(raft-sim-conformance): mark task 6.2 done — conformance batch ASAN/UBSAN-clean 2026-06-24 16:04:29 +00:00
47f68032b7 test(raft-sim-conformance): randomized M-seed sweep + all scenarios, zero violations (task 6.1) 2026-06-24 15:59:22 +00:00
a2933c274d feat(raft-sim-conformance): stale-rejoin scenario, bug-851 G5 term adoption via AE (task 5.5) 2026-06-24 15:50:13 +00:00
b1f289d2cb feat(raft-sim-conformance): Figure-8 commit-safety scenario + restore dropped 5.3 impl (task 5.4)
Adds raft_sim_scenario_figure8: a previous-term entry on a quorum under a
new-term leader is NOT committed until a current-term entry commits (library-
gated by raft_advance_commit; verified by a mutation test). Also restores the
raft_sim_scenario_drop_dup_reorder implementation that commit e445404 (task 5.3)
silently dropped from raft_sim.c (a stray git checkout reverted the uncommitted
impl), which had left the lib build broken with an undefined reference.

Build green: test_sim_5_3_drop_dup_reorder + test_sim_5_4_figure8 both PASS.
2026-06-24 15:38:01 +00:00
e445404439 feat(raft-sim-conformance): drop/duplicate/reorder adversarial scenario (task 5.3) 2026-06-24 15:09:31 +00:00
6976392d77 feat(raft-sim-conformance): restart-churn adversarial scenario (task 5.2) 2026-06-24 14:47:25 +00:00
7d0964dd16 feat(raft-sim-conformance): partition/heal adversarial scenario (task 5.1) 2026-06-24 14:34:22 +00:00
b844e75f3b feat(raft-sim-conformance): raft_sim_run + deterministic replay (task 4.4) 2026-06-24 14:14:15 +00:00
5f67be4fa3 test(raft-sim-conformance): conformance Restart persistence test (task 4.3) 2026-06-24 14:05:19 +00:00
e15bbf8190 feat(raft-sim-conformance): seeded scheduler raft_sim_step + per-step invariant check (task 4.2)
Harness surfaced a real upstream raft.c split-brain bug (bug-1095/1096,
bug-851 family) in raft_on_vote_reply: counter-based vote tally double-counts
a duplicated vote-response and no stale-term drop -> two leaders same term.
Out of this spec's boundary (raft.c protocol); recorded as Implementation Note.
2026-06-24 13:37:03 +00:00
90e7e9c999 feat(raft-sim-conformance): action trace recorder + raft_sim_dump_trace (task 4.1) 2026-06-24 13:10:31 +00:00
f0be46fb32 feat(raft-sim-conformance): raft_sim_check_all first-failure composition (task 3.5) 2026-06-24 13:00:10 +00:00
a38d9a7271 feat(raft-sim-conformance): State Machine Safety invariant checker (task 3.4) 2026-06-24 12:51:58 +00:00
63a9740c85 feat(raft-sim-conformance): Leader Completeness invariant checker (task 3.3) 2026-06-24 12:44:18 +00:00
d9b5fd51e0 test(raft-sim-conformance): focused Log Matching checker test for R3.2 (task 3.2) 2026-06-24 12:35:36 +00:00
25b14a6dca feat(raft-sim-conformance): Election Safety invariant checker (task 3.1) 2026-06-24 12:28:16 +00:00
b450e60d13 feat(raft-sim-conformance): bag-only Drop/Duplicate/Reorder ops (task 2.2) 2026-06-24 12:19:15 +00:00
ae4495104a feat(raft-sim-conformance): bag push/deliver routes vote+AE RPCs through library handlers (task 2.1) 2026-06-24 12:10:11 +00:00
631854303c feat(raft-sim-conformance): seeded PRNG + in-memory adapter callbacks + conformance init (task 1.2) 2026-06-24 11:55:11 +00:00
35c012ddc2 feat(raft-sim-conformance): add conformance-harness data types to raft.h (task 1.1) 2026-06-24 11:40:38 +00:00
2b15dfb67a docs(raft-sim-conformance): fix mermaid pipe + semicolon syntax in design diagrams
Mermaid parse errors in design.md's diagrams:
- Literal `|` in flowchart node labels is read as an edge-label delimiter:
  system-flow `term | 0` -> `term | 0`; checker `|{i:...}|` ->
  `|{i:...}|` (entity renders as a literal `|`, meaning preserved).
- `;` is a mermaid statement separator and truncated the text it appeared in:
  sequence msgs `commit_index += ; apply()` -> `commit_index advanced, apply()`
  and `(R5); halt` -> `(R5), then halt`; flowchart label `responses;<br/>` ->
  `responses,<br/>`.

Valid edge-label pipes (-->|violation|) and all `;` in prose/tables outside the
mermaid fences are unchanged. All three diagrams now render.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MSxwb2Xt5F84ATgiAZnH8V
2026-06-24 10:43:48 +00:00
af099dca3e test(ha-convergence-recovery): close Task 8 — live /xce-test-ha 8-round PASS
Ran the live 3-node E2E (the last open task). Result:
  PASS: 8 rounds, 30 jobs submitted, zero job loss; all 3 nodes led
  (ctrl-1,ctrl-2,ctrl-3)  (exit 0)

Live-covered: kill-primary -> promoted standby owns all confirmed jobs
(zero loss across 8 failovers, jobs 3->30, expect-job ok each round);
restart ex-primary -> converged rejoin, 3-of-3 each round, no phantom jobs;
deterministic leadership coverage (ctrl-3 forced via RequestVote-freshness
fence). The soak also served as the live regression for bug-1077 (~40
failover client round-trips, no SEGV) after rebuilding the controller (the
deployed binaries predated that fix).

Not exercised live (remain unit-covered, tasks 2/4/5): withhold-ACK barrier
single-outcome (test_demotion_convergence) and isolate/heal quorum-WARN
(test_convergence_verification). All 8 tasks now [x]; spec closed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MSxwb2Xt5F84ATgiAZnH8V
2026-06-24 10:17:11 +00:00
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
4338a97d0e docs(steering): sync HA protocol with ha-term-safety — per-entry term, (term,lsn) reconcile, commit-source ownership, bug-828 truncate rule 2026-06-23 04:48:06 +00:00
51d8a4c981 docs(ha-term-safety): record bug-828 FIXED (premature pre-election truncate) + bug-854 residual 2026-06-23 04:08:18 +00:00
ff869e4fc0 docs(ha-term-safety): characterize remaining bug-828 slice (election watermark tangle) 2026-06-23 03:15:23 +00:00
14c41cb298 docs(ha-term-safety): record Option A resolution + newly-exposed bug-828 failover job-loss 2026-06-23 00:29:06 +00:00
df4bc4f86a test(ha-term-safety): live-harness fixes + record bug-991 (commit stall via catch-up + 2.4 ceiling)
Ran clean_failover_preservation.sh on a real fs ($HOME/tmp/xce-ha-deploy, ext4).
PAYLOAD run CONFIRMED bug-990 fixed live: standbys persist the leader's real
term (2) for the committed prefix; walcompare OK.

Harness fixes: robust failover detection (any post-kill CTRL_PRIMARY promotion,
45s settle) + a wait-for-commit-before-kill gate (jobsub returns on accept not
commit, so the prior harness raced replication vs kill).

That commit-wait gate exposed bug-991: on a STABLE cluster, jobs replicate to
all members (durable LSN 5) but committed_lsn pins at 1. Catch-up-delivered
entries don't advance the library commit_index, so task-2.4's ceiling
min(durable-majority, raft_commit_index) starves commit below the durable
majority. A real regression from the commit-source switch (2.4+3.3), invisible
to the steady-state-only unit tests.
2026-06-22 21:10:48 +00:00
5fa394f598 docs(ha-term-safety): record final verification state — build+unit gate green (175/0); 5.2/5.4/5.5 live runs env-blocked 2026-06-22 15:48:53 +00:00
efe621c124 feat(ha-term-safety): retire term-0 repl_broadcast live send; carry leader term on replication (task 3.4)
Closes bug-990: the live replicator repl_broadcast wire-encoded term=0
(entry.term was never set), so standbys persisted hdr.term=0 and took the
legacy direct-apply path instead of the library (term,lsn) intake. Now the
live broadcast and catch-up streamer carry the leader's real term + AE prev
tuple; ha_apply_wal_catchup_entry routes every term-carrying entry through
raft_on_append_entries_request -> on_persist_entry (leader term stamped),
retiring the term-0 direct-apply. Standbys now persist the leader's real term
and reconcile by (term,lsn); the live commit chain is end-to-end.

848/849/850 + persist-before-ACK + commit-barrier + bug-960 preserved.
Proven at integration level; the definitive live STRICT_TERM failover run is
env-blocked here (bug-924 overlayfs) and recommended on a real fs.
2026-06-22 15:45:14 +00:00
87242aa8e9 spec(ha-term-safety): add task 3.4 — retire term-0 repl_broadcast, drive live library send (closes bug-990) 2026-06-22 15:02:21 +00:00
fda15336d5 test(ha-term-safety): clean-failover live harness; mark 5.2/5.4/5.5 blocked on bug-990 (live-send retirement)
Task 5.2 harness deploys a real 3-node localhost cluster, kills the primary,
promotes a standby, restarts the old primary, and asserts convergence via
walcompare --committed + per-job expect-job. Runs GREEN in PAYLOAD mode
(guards 848/849/850 + no committed-job loss).

The term-identity half of R5.1 is BLOCKED (bug-990): live replication still
uses term-0 repl_broadcast; the library send loop (on_send_append_entries) has
no live caller, so standbys persist hdr.term=0. The live-send retirement was
deferred across tasks 3.2/3.3 into verification tasks 5.2/5.4 that cannot
contain the production cutover. Needs a dedicated implementation task.
2026-06-22 13:27:21 +00:00
a166ecb45b test(ha-term-safety): cross-member walcompare divergence tool (task 5.3)
Diffs member WALs by (lsn -> term, pcrc) over the committed prefix, exits non-zero
on any committed-prefix divergence with the diverging LSN + per-member entry
identities. Reuses the canonical waldump decoder via a new per-entry visitor seam
(no second WAL parser). Detects same-LSN/diff-term and same-LSN/diff-content
divergences (the bug-851 class) that an LSN-range check would mask.
2026-06-22 13:05:23 +00:00
d39500ba48 test(ha-term-safety): end-to-end adapter callback + marker-path integration test (task 5.1) 2026-06-22 12:03:20 +00:00
be4c1f0721 feat(ha-term-safety): out-of-band config bytes, hash-verify-before-activate, serve-fence (task 4.3)
Wires config_committed_marker to a production consumer (config_activate_committed):
verifies pushed bytes' sha256 against the committed marker before activation, fences
(loud, non-serving, stay-alive) on mismatch, and activates the latest committed marker
on recovery. Enforced via ha_dispatch_gate + promotion gate.

Fixes bug-981: a 2nd+ config upgrade could permanently fence a standby on a
push-before-commit ordering. Adds an off-iomgr delaymgr retry scheduled from the
commit-advance hook (atomic read + non-blocking enqueue only on the ACK thread) that
re-activates and clears the stale fence once the new marker commits and bytes verify.
2026-06-22 11:47:15 +00:00
c3201a2b1e feat(ha-term-safety): append config markers via unified log + derive committed config (task 4.2)
Wires do_upgrade_config_with_bytes to append WAL_CONFIG_MARKER through
raft_log_append (live term-stamped consensus record), demotes committed_config_tag
to a non-authoritative cache, and derives the committed config from the latest
committed marker. Preserves the derive across checkpoint compaction via an optional,
backward-compatible config-ref tail in the snapshot sentinel.
2026-06-22 10:34:10 +00:00
f447ad4183 feat(ha-term-safety): add WAL_CONFIG_MARKER entry type + reference payload codec (task 4.1) 2026-06-22 09:37:23 +00:00
f1783f7952 feat(ha-term-safety): drive ACK path through raft_on_append_entries_reply + advance_commit (task 3.3)
Includes fix for bug-960: endpoint-type-aware peer-index resolution
(promoted-vote relative vs heartbeat-standby absolute) with fail-loud
bounds check, preventing an out-of-bounds match_index[] write / wrong-peer
false-quorum on the replication ACK path.
2026-06-22 09:25:44 +00:00
754e586f8f feat(ha-term-safety): route AppendEntries intake through library + leaderCommit wire (task 3.2) 2026-06-22 08:42:35 +00:00
6a519a928b feat(ha-term-safety): drive raft_log_append on primary append path (task 3.1) 2026-06-22 08:09:47 +00:00
3dd8493fff feat(ha-term-safety): wire five callbacks + library init restore (task 2.5) 2026-06-22 07:47:30 +00:00
3470bdfb71 feat(ha-term-safety): on_commit_advanced barrier trigger + commit-gate ownership (task 2.4) 2026-06-22 07:33:04 +00:00
2c5d298cb5 feat(ha-term-safety): on_send_append_entries callback body (task 2.3)
Replaces the stub: builds a WalCatchupEntry AppendEntries to standbys[peer_idx].ep
carrying prev_log_index/prev_log_term; for n_entries==1 sources the entry at
prev_log_index+1 from the local WAL (term via persist_entry_term, type+payload via
ha_read_wal_entry_at); n_entries==0 emits a zero-entry heartbeat. Async send
(xce_wal_batch_req_raw), payload freed on all paths. 2 tests via a capture seam.

Review-applied: corrected the misleading repl.c comment — leader_commit is accepted
but NOT yet wire-encoded (deferred to task 3.2: WalCatchupEntry leaderCommit field +
standby intake). Reviewer APPROVED; per-group 0 failures.
2026-06-22 06:01:30 +00:00
ed2443533f feat(ha-term-safety): on_get_entry_term + on_truncate_from callback bodies (task 2.2)
on_get_entry_term → persist_entry_term (recorded term, 0=absent, legacy-0→1; caveat
documented per 1.3 review). on_truncate_from → persist_truncate_wal_above(p, index-1)
(delete LSN>=index), returns rc for library/leader retry; non-promotable fence intact;
replaces the committed-boundary heuristic on the library-driven path. New
truncate_term_index_locked keeps the in-memory per-LSN term index consistent on both
truncate paths (under lock, cutoff-aligned) → no stale-term read after truncation.
3 tests, reviewer APPROVED, per-group 0 failures.
2026-06-22 05:29:06 +00:00
8f3d3ce8f4 feat(ha-term-safety): on_persist_entry callback body (task 2.1)
Replaces the iter-1 stub: persist_set_term(p, term) stamps the leader/library term
into hdr.term, then role-routes the write — primary via persist_wal_async, standby
via persist_wal_write_replicated(p, index, ...). Persist failure returns non-zero
(no false ACK; durable_lsn unchanged); bug-850 contiguity guard + persist-before-ACK
preserved beneath. In-flight raft_entry_blob_t {type,payload_len} envelope added to
wal_types.h (never enters the 40-byte header). 3 tests, reviewer APPROVED.

Deferred (recorded in tasks.md): task 3.1 must retire the hand-rolled primary append
so current_lsn tracks the library index; task 3.2 must not call on_persist_entry on
heartbeats. Per-group 0 failures (pre-existing test_commit_barrier_reply leak aside).
2026-06-22 04:53:18 +00:00
d540ebd183 feat(ha-term-safety): WAL term seam + recovery read-back + legacy-0 rule (task 1.3)
persist_set_term sets the live election term so appends stamp hdr.term (fixes the
G1 term-0 stamping); replay_wal builds an in-memory per-LSN term index (freed in
persist_fini, no on-disk change); persist_entry_term reads it back (0=absent).
Legacy-0 -> WAL_LEGACY_TERM=1 at read-back only (raw 0 retained so present-legacy-0
is distinguishable from absent). 40-byte wal_entry_hdr_t preserved (value fix, R1.7).
5 new tests (test_term_safety_adapter.c); reviewer APPROVED. Feeds task 2.2.

All controller test groups report 0 failures; the meson full-suite timeout is the
bug-924 overlayfs /tmp fdatasync artifact, not this task.
2026-06-22 04:32:29 +00:00