From 5a2fae1d37b164b27edc9bc3824d79415d724fc8 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Sat, 13 Jun 2026 03:46:29 +0000 Subject: [PATCH] feat(ha-commit-barrier-rollout): config barrier with production ACK ingestion (task 5.2 + r1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit configAck branch: per-push majority (acked_config_lsn >= push_tag, 1+count > n_peers/2 over r->ce->n_peers, n_peers<=1 immediate, ce==NULL -EINVAL); legacy >0 counting and n_active==0 shortcut dead. push_config_to_standbys returns the barrier result; upgrade failure -> send_upgrade_error (rc forwarded), no success ACK, no restart, committed_config_tag success-only. Remediation r1 (two CRITICAL review findings): - F1: wire production config-ACK ingestion — replconfigpushreplymsg decode branch in repl_ack_decode_and_dispatch -> repl_on_config_ack on the promoted-vote ep; + defensive cluster.c OPCode_reply case. Closes the unlanded half of ha-config-bootstrap 8.1; without this the barrier would brick every multi-member config upgrade. No schema change. - F2: keep the barrier off the iomgr thread — export push_config_transport_only (non-blocking) for the 5 ha.c + join.c streaming sites; barrier path now vtask-only (do_upgrade_config). Wire-path test drives real capnp ACK bytes through the production decode branch. 30/30 rollout, 6/6 repl_majority, 46/46 config_repl; census 0 FAIL; pre-existing fixture leak only (less than baseline). --- .../specs/ha-commit-barrier-rollout/tasks.md | 3 +- .wolf/buglog.json | 863 +++++++++++------- .wolf/cerebrum.md | 1 + .wolf/memory.md | 34 + src/controller/cluster.c | 29 + src/controller/config_repl.c | 138 ++- src/controller/config_repl.h | 36 +- src/controller/ha.c | 71 +- src/controller/join.c | 7 +- src/controller/repl.c | 68 +- .../testsuite/test_commit_barrier_rollout.c | 172 ++++ src/include/repl.h | 11 +- 12 files changed, 1034 insertions(+), 399 deletions(-) diff --git a/.kiro/specs/ha-commit-barrier-rollout/tasks.md b/.kiro/specs/ha-commit-barrier-rollout/tasks.md index e6ceef9e..61a6342f 100644 --- a/.kiro/specs/ha-commit-barrier-rollout/tasks.md +++ b/.kiro/specs/ha-commit-barrier-rollout/tasks.md @@ -72,7 +72,7 @@ - Observable completion: new cases FAIL (red) against the current logged-only result (config_repl.c:916-929) and unconditional success ACK (config_repl.c:1062-1073) - _Requirements: 4.1, 4.2, 4.3, 4.4_ -- [ ] 5.2 Implement the config barrier to green +- [x] 5.2 Implement the config barrier to green (+ remediation r1: wired production config-ACK ingestion, kept barrier off iomgr thread) - repl.c: configAck branch takes the in-flight push tag (signature change in src/include/repl.h), counts `acked_config_lsn >= push_tag`, majority `1 + count > n_peers / 2` over configured members, `n_peers <= 1` immediate success - config_repl.c: `push_config_to_standbys` gains `int *acked_out` and returns the barrier result, passing `new_tag` to the wait; `do_upgrade_config_with_bytes` (config_repl.c:1056-1094 anchor) on non-zero return sends `send_upgrade_error`, skips the success ACK and skips the restart request; `committed_config_tag` advance stays success-branch-only - Observable completion: all task 5.1 tests PASS; a failed upgrade leaves the admin tool with an explicit error and the primary running on the old committed tag @@ -112,6 +112,7 @@ - (1.1) wal_entry_type has 13 values (wal_types.h:44-58), no WAL_NOOP yet (lands in ha-single-leader-role, which must add a classification row + sweep entry — the totality sweep will catch it). - (1.1) Suite baseline is ZERO failures since ha-commit-engine 6.1. detect_leaks=0 remains the harness norm (6 inventoried pre-existing leak groups). - (1.2) CENSUS METHOD: only ~9 of 48 groups emit Unity summary blocks; the rest report per-test PASS lines only — count distinct Unity results (~465) and grep FAIL lines, never sum summary blocks. commit_submit(RETAIN) early-returns BEFORE taking ce->lock (commit.c:233-239) — registration is lock-free, safe with entity write locks held. wal_hook_append_classified is the single classified-append seam; appended_lsn read = current_lsn - 1 under p->lock (benign race, commented). +- (5.2) **REJECTED then remediated (r1)** — two CRITICAL findings the first pass missed, both fixed: (F1) the config-ACK signal was never produced in production (repl_on_config_ack had zero callers; the standby's replconfigpushreplymsg reply was dropped on the primary) so the new authoritative barrier would have BRICKED every multi-member config upgrade — this was the unlanded half of ha-config-bootstrap task 8.1. Fixed by a replconfigpushreplymsg decode branch in repl_ack_decode_and_dispatch (ha.c) → repl_on_config_ack on the repl_add_standby-registered promoted-vote ep (production-normal path), + a defensive cluster.c OPCode_reply ingestion for inbound standbys (no-ops against unregistered slots, forward-looking). Schema unchanged (ReplConfigPushReply already carried versionTag). (F2) the 500ms barrier wait reached the iomgr epoll thread via 5 ha.c push sites. Fixed by exporting push_config_transport_only (non-blocking fan-out) for all best-effort ha.c/join.c streaming sites; push_config_to_standbys (transport+barrier) now has exactly ONE caller — do_upgrade_config_with_bytes in vtask context. LESSON: the design's "handlers run on vtask threads" claim was only true for cluster.c-dispatched opcodes, not the ha.c peer-channel push sites the design never analyzed (ha.c was outside its modified-files list). Wire-path test drives real capnp ACK bytes through the production decode branch. - (3.3) **bug-451 DISCOVERED (reviewer-verified byte-precise, NOT fixed — boundary)**: update_job_rusages fires HOOK_DETAIL_JOB_RUSAGE_UPDATE with (cluster_t*, xce_job_rusages_t*) (job.c:1014-1015) but persist_job_hook_fun's default arm (persist.c:1355-1359) reads vararg 2 as job_t* and dereferences j->job.state at offset 56 — one past the end of the exactly-56-byte xce_job_rusages_t → 4-byte OOB read + garbage state field in every WAL_JOB_RUSAGE payload (jobid lines up by accident). Production fix candidate for task 7.1's sweep or a follow-up: persist arm needs a RUSAGE-specific va_list signature. Census now 488. - (3.2) bug-447 FIXED via rc normalization inside the three ctrl fns (real errors/misses preserved; reviewer-verified per-fn). Semantics change shipped intentionally: idempotent no-op control actions now return 0 (was leaked 1); queue/host mutations now append WAL + replicate. Join barrier failure → error reply + on_worker_join SKIPPED (coherent reading of design; worker retries via idempotent rejoin; documented at join.c:108-113). PRE-EXISTING ref leak: job_ctrl takes reference(j) (job.c:317) but normal exit never dereferences (bug-448) — out of scope, future fix. - (3.1) **bug-447 CONFIRMED (reviewer-verified to librtti disassembly)**: key_find returns 1 on found (unnormalized from xkvmgr_find/kvtab_find); queue_ctrl (queue.c:61, gate :106) and worker_ctrl (worker.c:644, gate :678) leak rc=1 → AFTER_EVENT never fires on the found path → **queue/host control silently unpersisted+unreplicated in production today**; job_ctrl appends via mark_job_* hooks but still replies status 1. Fix point = 3.2's handler rc normalization (key_find is in binary librtti, out of repo). RED tests pin it. diff --git a/.wolf/buglog.json b/.wolf/buglog.json index a46257a9..764c2e78 100644 --- a/.wolf/buglog.json +++ b/.wolf/buglog.json @@ -7,7 +7,7 @@ "error_message": "Significant refactor of ", "file": "CLAUDE.md", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 23\u219230 lines (3 removed)", + "fix": "Rewrote 23→30 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -39,7 +39,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_config_repl.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 14\u219219 lines (2 removed)", + "fix": "Rewrote 14→19 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -55,7 +55,7 @@ "error_message": "Wrong reference: g_tmpdir should be g_instdir", "file": "src/controller/testsuite/test_config_repl.c", "root_cause": "Used \"g_tmpdir\" instead of \"g_instdir\"", - "fix": "Changed g_tmpdir \u2192 g_instdir", + "fix": "Changed g_tmpdir → g_instdir", "tags": [ "auto-detected", "wrong-reference", @@ -87,7 +87,7 @@ "error_message": "Significant refactor of ", "file": "src/docs/config-replication-design.md", "root_cause": "7 lines replaced/restructured", - "fix": "Rewrote 17\u219220 lines (7 removed)", + "fix": "Rewrote 17→20 lines (7 removed)", "tags": [ "auto-detected", "refactor", @@ -135,7 +135,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.h", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 6\u21929 lines (2 removed)", + "fix": "Rewrote 6→9 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -151,7 +151,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 11\u21925 lines (5 removed)", + "fix": "Rewrote 11→5 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -167,7 +167,7 @@ "error_message": "Significant refactor of ", "file": "src/docs/config-replication-impl-status.md", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 10\u219212 lines (4 removed) | Also: | `handle_config_request()` (primary handler) | \u2705 ; | `fetch_config_from_primary()` (standby fetch) | | Also: | `handle_upgrade_config()` | \u2705 Compiled, gated | ; | `push_config_to_standbys()` | \u26a0 Stub (returns 0)", + "fix": "Rewrote 10→12 lines (4 removed) | Also: | `handle_config_request()` (primary handler) | ✅ ; | `fetch_config_from_primary()` (standby fetch) | | Also: | `handle_upgrade_config()` | ✅ Compiled, gated | ; | `push_config_to_standbys()` | ⚠ Stub (returns 0)", "tags": [ "auto-detected", "refactor", @@ -183,7 +183,7 @@ "error_message": "Significant refactor of ", "file": "src/docs/config-replication-impl-status.md", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 17\u219221 lines (4 removed)", + "fix": "Rewrote 17→21 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -199,7 +199,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 6\u21927 lines (3 removed)", + "fix": "Rewrote 6→7 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -215,7 +215,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "6 lines replaced/restructured", - "fix": "Rewrote 13\u219224 lines (6 removed)", + "fix": "Rewrote 13→24 lines (6 removed)", "tags": [ "auto-detected", "refactor", @@ -231,7 +231,7 @@ "error_message": "Significant refactor of ", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 4\u219210 lines (4 removed)", + "fix": "Rewrote 4→10 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -247,7 +247,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 8\u21929 lines (4 removed)", + "fix": "Rewrote 8→9 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -262,7 +262,7 @@ "timestamp": "2026-05-25T11:05:06.112Z", "error_message": "Missing error handling in send", "file": ".kiro/specs/ha-config-bootstrap/brief.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -278,7 +278,7 @@ "timestamp": "2026-05-25T11:06:26.871Z", "error_message": "Missing error handling in unknown", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -295,7 +295,7 @@ "error_message": "Significant refactor of ", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 23\u219240 lines (2 removed)", + "fix": "Rewrote 23→40 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -326,7 +326,7 @@ "timestamp": "2026-05-25T11:32:18.221Z", "error_message": "Missing error handling in types", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -343,7 +343,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "36 lines replaced/restructured", - "fix": "Rewrote 63\u219279 lines (36 removed) | Also: 6. **No simultaneous primaries**: Only one node ca; 7. **Majority-commit WAL replication (CP writes)** | Also: - `handle_request_vote()` handler on all peers; - Config tag + LSN comparison for vote decision", + "fix": "Rewrote 63→79 lines (36 removed) | Also: 6. **No simultaneous primaries**: Only one node ca; 7. **Majority-commit WAL replication (CP writes)** | Also: - `handle_request_vote()` handler on all peers; - Config tag + LSN comparison for vote decision", "tags": [ "auto-detected", "refactor", @@ -391,7 +391,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 11\u219228 lines (3 removed)", + "fix": "Rewrote 11→28 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -439,7 +439,7 @@ "error_message": "Significant refactor of ", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 13\u219228 lines (2 removed)", + "fix": "Rewrote 13→28 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -471,7 +471,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "11 lines replaced/restructured", - "fix": "Rewrote 17\u219228 lines (11 removed)", + "fix": "Rewrote 17→28 lines (11 removed)", "tags": [ "auto-detected", "refactor", @@ -487,7 +487,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 28\u219245 lines (4 removed)", + "fix": "Rewrote 28→45 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -519,7 +519,7 @@ "error_message": "Significant refactor of ", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", "root_cause": "13 lines replaced/restructured", - "fix": "Rewrote 22\u219235 lines (13 removed) | Also: **Files**: `src/controller/main.c`, `src/lib/netio; 1. **Peer-count validation in `main.c`** (before `", + "fix": "Rewrote 22→35 lines (13 removed) | Also: **Files**: `src/controller/main.c`, `src/lib/netio; 1. **Peer-count validation in `main.c`** (before `", "tags": [ "auto-detected", "refactor", @@ -567,7 +567,7 @@ "error_message": "Significant refactor of ", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 4\u21927 lines (2 removed)", + "fix": "Rewrote 4→7 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -583,7 +583,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 9\u219211 lines (2 removed)", + "fix": "Rewrote 9→11 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -631,7 +631,7 @@ "error_message": "Significant refactor of ", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", "root_cause": "18 lines replaced/restructured", - "fix": "Rewrote 29\u219234 lines (18 removed)", + "fix": "Rewrote 29→34 lines (18 removed)", "tags": [ "auto-detected", "refactor", @@ -679,7 +679,7 @@ "error_message": "Significant refactor of ", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 13\u219229 lines (8 removed) | Also: 8. **Register OP_requestVote handler** in cluster'; switch at line ~195): since vtaskmgr and iomgr run", + "fix": "Rewrote 13→29 lines (8 removed) | Also: 8. **Register OP_requestVote handler** in cluster'; switch at line ~195): since vtaskmgr and iomgr run", "tags": [ "auto-detected", "refactor", @@ -695,7 +695,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "9 lines replaced/restructured", - "fix": "Rewrote 13\u219260 lines (9 removed) | Also: 3. **Replace timestamp polling with `EPOLLRDHUP`**; is replaced by an epoll event handler. On `EPOLLRD | Also: - **Split vote recovery**: After failing quorum, s; (using existing `jitter_seed` xorshift64), retry o", + "fix": "Rewrote 13→60 lines (9 removed) | Also: 3. **Replace timestamp polling with `EPOLLRDHUP`**; is replaced by an epoll event handler. On `EPOLLRD | Also: - **Split vote recovery**: After failing quorum, s; (using existing `jitter_seed` xorshift64), retry o", "tags": [ "auto-detected", "refactor", @@ -711,7 +711,7 @@ "error_message": "Significant refactor of ", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 8\u219234 lines (8 removed) | Also: 2. **Replace `ha_check_heartbeat_nocluster()` with; - On `EPOLLRDHUP` from `primary_ctrl_fd`: **close ", + "fix": "Rewrote 8→34 lines (8 removed) | Also: 2. **Replace `ha_check_heartbeat_nocluster()` with; - On `EPOLLRDHUP` from `primary_ctrl_fd`: **close ", "tags": [ "auto-detected", "refactor", @@ -726,7 +726,7 @@ "timestamp": "2026-05-26T13:51:32.274Z", "error_message": "Missing error handling in unknown", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: - [ ] `test_heartbeat_carries_committed_lsn`: heartbeat payl; `committed_lsn`; standby updates `ha.last_known_committed_ls", "tags": [ "auto-detected", @@ -742,7 +742,7 @@ "timestamp": "2026-05-26T13:52:49.044Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/ha-config-bootstrap/brief.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: // Catch-up gate (Clarification 6): behind nodes must not in; if (c->ha.in_catch_up) {", "tags": [ "auto-detected", @@ -758,7 +758,7 @@ "timestamp": "2026-05-27T03:02:34.912Z", "error_message": "Missing error handling in unknown", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: name, key, applied_lsn, and config_tag immediately after TCP; - [ ] `test_ctrl_hello_registers_standby_in_repl`: handle_ct", "tags": [ "auto-detected", @@ -774,7 +774,7 @@ "timestamp": "2026-05-27T03:03:26.016Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/ha-config-bootstrap/brief.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -791,7 +791,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "9 lines replaced/restructured", - "fix": "Rewrote 37\u219251 lines (9 removed)", + "fix": "Rewrote 37→51 lines (9 removed)", "tags": [ "auto-detected", "refactor", @@ -807,7 +807,7 @@ "error_message": "Significant refactor of ", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 8\u219223 lines (8 removed) | Also: - [ ] `test_persistent_conn_single_node_noop`: ha_; when n_peers == 1; primary_ctrl_fd remains -1 (uni", + "fix": "Rewrote 8→23 lines (8 removed) | Also: - [ ] `test_persistent_conn_single_node_noop`: ha_; when n_peers == 1; primary_ctrl_fd remains -1 (uni", "tags": [ "auto-detected", "refactor", @@ -823,7 +823,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "10 lines replaced/restructured", - "fix": "Rewrote 34\u219251 lines (10 removed)", + "fix": "Rewrote 34→51 lines (10 removed)", "tags": [ "auto-detected", "refactor", @@ -838,7 +838,7 @@ "timestamp": "2026-05-27T03:56:41.037Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/ha-config-bootstrap/brief.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -855,7 +855,7 @@ "error_message": "Significant refactor of ", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", "root_cause": "11 lines replaced/restructured", - "fix": "Rewrote 21\u219228 lines (11 removed)", + "fix": "Rewrote 21→28 lines (11 removed)", "tags": [ "auto-detected", "refactor", @@ -870,7 +870,7 @@ "timestamp": "2026-05-27T03:59:16.145Z", "error_message": "Missing error handling in unknown", "file": "../../../../../claude/.claude/plans/prancy-herding-nest.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -887,7 +887,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 10\u219213 lines (2 removed) | Also: **Replace LSF priority election with Raft-style qu; Five focused changes: | Also: 8. **Hard SSL/cert gate**: Controller refuses to b; 9. **Observable via logs**: Election results, vote", + "fix": "Rewrote 10→13 lines (2 removed) | Also: **Replace LSF priority election with Raft-style qu; Five focused changes: | Also: 8. **Hard SSL/cert gate**: Controller refuses to b; 9. **Observable via logs**: Election results, vote", "tags": [ "auto-detected", "refactor", @@ -902,7 +902,7 @@ "timestamp": "2026-05-27T06:45:34.951Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/ha-config-bootstrap/brief.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -919,7 +919,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/brief.md", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 8\u219211 lines (2 removed)", + "fix": "Rewrote 8→11 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -967,7 +967,7 @@ "error_message": "Significant refactor of ", "file": "src/schema/controller.capnp", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 5\u21927 lines (2 removed)", + "fix": "Rewrote 5→7 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -982,7 +982,7 @@ "timestamp": "2026-05-28T02:55:40.362Z", "error_message": "Missing error handling in unknown", "file": "src/schema/controller.capnp", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -999,7 +999,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.h", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 5\u219240 lines (2 removed)", + "fix": "Rewrote 5→40 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -1014,7 +1014,7 @@ "timestamp": "2026-05-28T03:39:14.341Z", "error_message": "Missing error handling in struct", "file": "src/controller/ha.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1030,7 +1030,7 @@ "timestamp": "2026-05-28T03:40:17.891Z", "error_message": "Missing error handling in struct", "file": "src/include/typemap.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1047,7 +1047,7 @@ "error_message": "Significant refactor of ", "file": "src/include/typemap.h", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 4\u21926 lines (2 removed)", + "fix": "Rewrote 4→6 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -1095,7 +1095,7 @@ "error_message": "Wrong reference: vcpus should be nres", "file": "src/include/clusterconfig.h", "root_cause": "Used \"vcpus\" instead of \"nres\"", - "fix": "Changed vcpus \u2192 nres", + "fix": "Changed vcpus → nres", "tags": [ "auto-detected", "wrong-reference", @@ -1111,7 +1111,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 12\u21926 lines (8 removed)", + "fix": "Rewrote 12→6 lines (8 removed)", "tags": [ "auto-detected", "refactor", @@ -1143,7 +1143,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_ha_startup.c", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 12\u219218 lines (8 removed)", + "fix": "Rewrote 12→18 lines (8 removed)", "tags": [ "auto-detected", "refactor", @@ -1159,7 +1159,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 109\u2192144 lines (4 removed)", + "fix": "Rewrote 109→144 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -1206,7 +1206,7 @@ "timestamp": "2026-05-28T07:13:42.089Z", "error_message": "Missing error handling in struct", "file": "src/controller/ha.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1222,7 +1222,7 @@ "timestamp": "2026-05-28T07:14:03.724Z", "error_message": "Missing error handling in unknown", "file": "src/controller/ha.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1255,7 +1255,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "14 lines replaced/restructured", - "fix": "Rewrote 92\u2192344 lines (14 removed)", + "fix": "Rewrote 92→344 lines (14 removed)", "tags": [ "auto-detected", "refactor", @@ -1271,7 +1271,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/myapp.c", "root_cause": "6 lines replaced/restructured", - "fix": "Rewrote 25\u219249 lines (6 removed)", + "fix": "Rewrote 25→49 lines (6 removed)", "tags": [ "auto-detected", "refactor", @@ -1335,7 +1335,7 @@ "error_message": "Wrong reference: capn_str_null should be str", "file": "src/controller/config_repl.c", "root_cause": "Used \"capn_str_null\" instead of \"str\"", - "fix": "Changed capn_str_null \u2192 str", + "fix": "Changed capn_str_null → str", "tags": [ "auto-detected", "wrong-reference", @@ -1351,7 +1351,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "7 lines replaced/restructured", - "fix": "Rewrote 17\u219284 lines (7 removed)", + "fix": "Rewrote 17→84 lines (7 removed)", "tags": [ "auto-detected", "refactor", @@ -1398,7 +1398,7 @@ "timestamp": "2026-05-28T13:34:49.513Z", "error_message": "Missing error handling in unknown", "file": "src/controller/repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1415,7 +1415,7 @@ "error_message": "Significant refactor of ", "file": "src/include/repl.h", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 3\u21924 lines (3 removed) | Also: /* ACK handling \u2014 call from io_worker read callbac; /* Majority-quorum ACK wait \u2014 stub until task 7.3 ", + "fix": "Rewrote 3→4 lines (3 removed) | Also: /* ACK handling — call from io_worker read callbac; /* Majority-quorum ACK wait — stub until task 7.3 ", "tags": [ "auto-detected", "refactor", @@ -1430,7 +1430,7 @@ "timestamp": "2026-05-28T13:52:52.636Z", "error_message": "Missing error handling in unknown", "file": "src/controller/repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1447,7 +1447,7 @@ "error_message": "Wrong reference: in_catch_up should be acked_config_lsn", "file": "src/controller/repl.c", "root_cause": "Used \"in_catch_up\" instead of \"acked_config_lsn\"", - "fix": "Changed in_catch_up \u2192 acked_config_lsn", + "fix": "Changed in_catch_up → acked_config_lsn", "tags": [ "auto-detected", "wrong-reference", @@ -1479,7 +1479,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/persist.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 20\u219229 lines (4 removed)", + "fix": "Rewrote 20→29 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -1495,7 +1495,7 @@ "error_message": "Wrong reference: ha_state_t should be _Atomic", "file": "src/controller/ha.h", "root_cause": "Used \"ha_state_t\" instead of \"_Atomic\"", - "fix": "Changed ha_state_t \u2192 _Atomic", + "fix": "Changed ha_state_t → _Atomic", "tags": [ "auto-detected", "wrong-reference", @@ -1511,7 +1511,7 @@ "error_message": "Wrong reference: socket should be epoll", "file": "src/controller/ha.c", "root_cause": "Used \"socket\" instead of \"epoll\"", - "fix": "Changed socket \u2192 epoll", + "fix": "Changed socket → epoll", "tags": [ "auto-detected", "wrong-reference", @@ -1559,7 +1559,7 @@ "error_message": "Wrong reference: XCE_ERR_STANDBY should be E_XCE_STANDBY", "file": "src/lib/request.c", "root_cause": "Used \"XCE_ERR_STANDBY\" instead of \"E_XCE_STANDBY\"", - "fix": "Changed XCE_ERR_STANDBY \u2192 E_XCE_STANDBY", + "fix": "Changed XCE_ERR_STANDBY → E_XCE_STANDBY", "tags": [ "auto-detected", "wrong-reference", @@ -1575,7 +1575,7 @@ "error_message": "Wrong reference: XCE_ERR_NOT_READY should be E_XCE_NOT_READY", "file": "src/lib/testsuite/test_libxce_retry.c", "root_cause": "Used \"XCE_ERR_NOT_READY\" instead of \"E_XCE_NOT_READY\"", - "fix": "Changed XCE_ERR_NOT_READY \u2192 E_XCE_NOT_READY", + "fix": "Changed XCE_ERR_NOT_READY → E_XCE_NOT_READY", "tags": [ "auto-detected", "wrong-reference", @@ -1590,7 +1590,7 @@ "timestamp": "2026-05-29T03:27:20.556Z", "error_message": "Missing error handling in unknown", "file": "src/controller/ha.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1606,7 +1606,7 @@ "timestamp": "2026-05-29T03:29:44.753Z", "error_message": "Missing error handling in unknown", "file": "src/controller/config_repl.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1623,7 +1623,7 @@ "error_message": "Wrong reference: data should be ctrl_hello_reply_t", "file": "src/include/typemap.h", "root_cause": "Used \"data\" instead of \"ctrl_hello_reply_t\"", - "fix": "Changed data \u2192 ctrl_hello_reply_t", + "fix": "Changed data → ctrl_hello_reply_t", "tags": [ "auto-detected", "wrong-reference", @@ -1638,7 +1638,7 @@ "timestamp": "2026-05-29T03:53:48.559Z", "error_message": "Missing error handling in unknown", "file": "src/schema/controller.capnp", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1654,7 +1654,7 @@ "timestamp": "2026-05-29T03:54:23.205Z", "error_message": "Missing error handling in struct", "file": "src/include/typemap.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: wal_catchup_entry_t* walcatchupentrymsg;; snapshot_transfer_t* snapshottransfermsg; | Also: /* Forward declarations for types defined later in this file; typedef struct snapshot_transfer_t snapshot_transfer_fw", "tags": [ "auto-detected", @@ -1671,7 +1671,7 @@ "error_message": "Significant refactor of ", "file": "src/include/typemap.h", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 5\u21925 lines (2 removed)", + "fix": "Rewrote 5→5 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -1687,7 +1687,7 @@ "error_message": "Significant refactor of ", "file": "src/schema/controller.capnp", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 5\u21923 lines (2 removed)", + "fix": "Rewrote 5→3 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -1702,7 +1702,7 @@ "timestamp": "2026-05-29T04:04:12.693Z", "error_message": "Missing error handling in unknown", "file": "src/controller/config_repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1719,7 +1719,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 10\u219212 lines (2 removed)", + "fix": "Rewrote 10→12 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -1751,7 +1751,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 10\u219220 lines (5 removed)", + "fix": "Rewrote 10→20 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -1783,7 +1783,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/manual-tests.md", "root_cause": "9 lines replaced/restructured", - "fix": "Rewrote 18\u219220 lines (9 removed)", + "fix": "Rewrote 18→20 lines (9 removed)", "tags": [ "auto-detected", "refactor", @@ -1798,7 +1798,7 @@ "timestamp": "2026-05-29T10:59:37.144Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/ha-config-bootstrap/manual-tests.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1815,7 +1815,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/manual-tests.md", "root_cause": "10 lines replaced/restructured", - "fix": "Rewrote 20\u219236 lines (10 removed) | Also: ### 11.1 Concurrent Startup (3 nodes); - Result: PASS / FAIL", + "fix": "Rewrote 20→36 lines (10 removed) | Also: ### 11.1 Concurrent Startup (3 nodes); - Result: PASS / FAIL", "tags": [ "auto-detected", "refactor", @@ -1831,7 +1831,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 29\u219245 lines (5 removed)", + "fix": "Rewrote 29→45 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -1847,7 +1847,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_ha_bootstrap.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 4\u21928 lines (2 removed)", + "fix": "Rewrote 4→8 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -1879,7 +1879,7 @@ "error_message": "Wrong reference: nres should be vcpus", "file": "src/include/clusterconfig.h", "root_cause": "Used \"nres\" instead of \"vcpus\"", - "fix": "Changed nres \u2192 vcpus", + "fix": "Changed nres → vcpus", "tags": [ "auto-detected", "wrong-reference", @@ -1895,7 +1895,7 @@ "error_message": "Wrong reference: data should be request_vote_reply_t", "file": "src/include/typemap.h", "root_cause": "Used \"data\" instead of \"request_vote_reply_t\"", - "fix": "Changed data \u2192 request_vote_reply_t", + "fix": "Changed data → request_vote_reply_t", "tags": [ "auto-detected", "wrong-reference", @@ -1911,7 +1911,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "10 lines replaced/restructured", - "fix": "Rewrote 19\u219225 lines (10 removed)", + "fix": "Rewrote 19→25 lines (10 removed)", "tags": [ "auto-detected", "refactor", @@ -1926,7 +1926,7 @@ "timestamp": "2026-05-29T16:36:41.731Z", "error_message": "Missing error handling in unknown", "file": "src/controller/cluster.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -1943,7 +1943,7 @@ "error_message": "Wrong reference: cluster_init_phase2 should be cluster_register_listener", "file": "src/controller/include/cluster.h", "root_cause": "Used \"cluster_init_phase2\" instead of \"cluster_register_listener\"", - "fix": "Changed cluster_init_phase2 \u2192 cluster_register_listener", + "fix": "Changed cluster_init_phase2 → cluster_register_listener", "tags": [ "auto-detected", "wrong-reference", @@ -1975,7 +1975,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/myapp.c", "root_cause": "30 lines replaced/restructured", - "fix": "Rewrote 79\u219261 lines (30 removed)", + "fix": "Rewrote 79→61 lines (30 removed)", "tags": [ "auto-detected", "refactor", @@ -1991,7 +1991,7 @@ "error_message": "Significant refactor of ", "file": "src/include/xce/cluster_def.h", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 8\u219215 lines (5 removed)", + "fix": "Rewrote 8→15 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -2007,7 +2007,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 7\u219216 lines (4 removed)", + "fix": "Rewrote 7→16 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -2023,7 +2023,7 @@ "error_message": "Wrong reference: CTRL_PERF_NHASH should be c", "file": "src/controller/cluster.c", "root_cause": "Used \"CTRL_PERF_NHASH\" instead of \"c\"", - "fix": "Changed CTRL_PERF_NHASH \u2192 c", + "fix": "Changed CTRL_PERF_NHASH → c", "tags": [ "auto-detected", "wrong-reference", @@ -2039,7 +2039,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "36 lines replaced/restructured", - "fix": "Rewrote 77\u219241 lines (36 removed)", + "fix": "Rewrote 77→41 lines (36 removed)", "tags": [ "auto-detected", "refactor", @@ -2055,7 +2055,7 @@ "error_message": "Wrong reference: true should be false", "file": ".claude/skills/xce-build/SKILL.md", "root_cause": "Used \"true\" instead of \"false\"", - "fix": "Changed true \u2192 false", + "fix": "Changed true → false", "tags": [ "auto-detected", "wrong-reference", @@ -2071,7 +2071,7 @@ "error_message": "Wrong reference: 120 should be 300", "file": "src/controller/meson.build", "root_cause": "Used \"120\" instead of \"300\"", - "fix": "Changed 120 \u2192 300", + "fix": "Changed 120 → 300", "tags": [ "auto-detected", "wrong-reference", @@ -2086,7 +2086,7 @@ "timestamp": "2026-05-30T02:37:40.267Z", "error_message": "Missing error handling in unknown", "file": "src/controller/myapp.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -2103,7 +2103,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 15\u219221 lines (2 removed)", + "fix": "Rewrote 15→21 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -2119,7 +2119,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 9\u219213 lines (2 removed)", + "fix": "Rewrote 9→13 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -2135,7 +2135,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/election-iomgr-refactor/design.md", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 10\u219227 lines (3 removed)", + "fix": "Rewrote 10→27 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -2166,8 +2166,8 @@ "timestamp": "2026-05-30T06:38:09.325Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/election-iomgr-refactor/design.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", - "fix": "Added try/catch block | Also: /* After election determines role: */; int is_first_boot = !c->running; /* running=1 only after cl | Also: #### `ha_dispatch_gate` \u2014 CTRL_PRIMARY_COMMITTING case; ```c | Also: ### 3-node startup \u2014 ctrl-a wins (with COMMITTING phase); participant PeerC as ctrl-c endpoint | Also: - **`ha_start_election_async()` is fully non-blocking**: Arm; - *Initial startup* (`ha->phase2_done == 0`): calls `cluster", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", + "fix": "Added try/catch block | Also: /* After election determines role: */; int is_first_boot = !c->running; /* running=1 only after cl | Also: #### `ha_dispatch_gate` — CTRL_PRIMARY_COMMITTING case; ```c | Also: ### 3-node startup — ctrl-a wins (with COMMITTING phase); participant PeerC as ctrl-c endpoint | Also: - **`ha_start_election_async()` is fully non-blocking**: Arm; - *Initial startup* (`ha->phase2_done == 0`): calls `cluster", "tags": [ "auto-detected", "error-handling", @@ -2183,7 +2183,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/election-iomgr-refactor/design.md", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 19\u219226 lines (4 removed) | Also: \u251c\u2500\u2500 ha.h # Add to ha_state_t: clie; \u2502 # ha_repl_ack_re | Also: \u2514\u2500\u2500 myapp.c # Restructured: phase1 + ; # for(;;) retry loop, cluster_init_phase2,", + "fix": "Rewrote 19→26 lines (4 removed) | Also: ├── ha.h # Add to ha_state_t: clie; │ # ha_repl_ack_re | Also: └── myapp.c # Restructured: phase1 + ; # for(;;) retry loop, cluster_init_phase2,", "tags": [ "auto-detected", "refactor", @@ -2199,7 +2199,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/election-iomgr-refactor/design.md", "root_cause": "12 lines replaced/restructured", - "fix": "Rewrote 15\u219220 lines (12 removed) | Also: - `src/controller/ha.h` \u2014 Add `client_ssl_ctx`, `r; - `src/controller/ha.c` \u2014 Main changes above; `ha_ | Also: \u251c\u2500\u2500 delay.c # Add delay_task_ms() (ms; \u2502 # Add delay_task_cancel()", + "fix": "Rewrote 15→20 lines (12 removed) | Also: - `src/controller/ha.h` — Add `client_ssl_ctx`, `r; - `src/controller/ha.c` — Main changes above; `ha_ | Also: ├── delay.c # Add delay_task_ms() (ms; │ # Add delay_task_cancel()", "tags": [ "auto-detected", "refactor", @@ -2231,7 +2231,7 @@ "error_message": "Significant refactor of ", "file": "src/lib/delay.c", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 53\u219285 lines (3 removed)", + "fix": "Rewrote 53→85 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -2247,7 +2247,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.h", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 7\u21928 lines (4 removed)", + "fix": "Rewrote 7→8 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -2327,7 +2327,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 14\u219217 lines (3 removed)", + "fix": "Rewrote 14→17 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -2359,7 +2359,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_ha_start_election_async.c", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 24\u219242 lines (8 removed)", + "fix": "Rewrote 24→42 lines (8 removed)", "tags": [ "auto-detected", "refactor", @@ -2390,7 +2390,7 @@ "timestamp": "2026-05-30T11:08:59.808Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/ha.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -2406,7 +2406,7 @@ "timestamp": "2026-05-30T11:09:47.905Z", "error_message": "Missing error handling in unknown", "file": "src/controller/ha.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -2423,7 +2423,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 62\u2192164 lines (8 removed) | Also: /* Store candidate own tag/lsn in votes[0] for eva; * n_peers==1 \u2192 n_other==0, so votes[] has 0 entrie | Also: * election_timeout_ctx_t: argument for the delay_t; * ha_elect_evaluate_and_signal when the election d", + "fix": "Rewrote 62→164 lines (8 removed) | Also: /* Store candidate own tag/lsn in votes[0] for eva; * n_peers==1 → n_other==0, so votes[] has 0 entrie | Also: * election_timeout_ctx_t: argument for the delay_t; * ha_elect_evaluate_and_signal when the election d", "tags": [ "auto-detected", "refactor", @@ -2454,7 +2454,7 @@ "timestamp": "2026-05-30T11:43:44.829Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/ha.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: /* Push config to standbys with lower config_tag (Req 7.1) *; #ifdef XCE_TESTING", "tags": [ "auto-detected", @@ -2470,7 +2470,7 @@ "timestamp": "2026-05-30T11:44:15.059Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/ha.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -2486,7 +2486,7 @@ "timestamp": "2026-05-30T11:45:24.946Z", "error_message": "Missing error handling in unknown", "file": "src/controller/cluster.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -2519,7 +2519,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 22\u219232 lines (2 removed)", + "fix": "Rewrote 22→32 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -2631,7 +2631,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "10 lines replaced/restructured", - "fix": "Rewrote 15\u21925 lines (10 removed)", + "fix": "Rewrote 15→5 lines (10 removed)", "tags": [ "auto-detected", "refactor", @@ -2695,7 +2695,7 @@ "error_message": "Wrong reference: third should be second", "file": "src/controller/testsuite/test_ha_repl_ack.c", "root_cause": "Used \"third\" instead of \"second\"", - "fix": "Changed third \u2192 second", + "fix": "Changed third → second", "tags": [ "auto-detected", "wrong-reference", @@ -2727,7 +2727,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/myapp.c", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 10\u219215 lines (5 removed)", + "fix": "Rewrote 10→15 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -2759,7 +2759,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "15 lines replaced/restructured", - "fix": "Rewrote 23\u219210 lines (15 removed)", + "fix": "Rewrote 23→10 lines (15 removed)", "tags": [ "auto-detected", "refactor", @@ -2775,7 +2775,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 5\u21929 lines (2 removed)", + "fix": "Rewrote 5→9 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -2791,7 +2791,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 14\u219216 lines (2 removed)", + "fix": "Rewrote 14→16 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -2823,7 +2823,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "12 lines replaced/restructured", - "fix": "Rewrote 15\u219220 lines (12 removed)", + "fix": "Rewrote 15→20 lines (12 removed)", "tags": [ "auto-detected", "refactor", @@ -2839,7 +2839,7 @@ "error_message": "Significant refactor of ", "file": "src/include/typemap.h", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 4\u21928 lines (2 removed)", + "fix": "Rewrote 4→8 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -2855,7 +2855,7 @@ "error_message": "Significant refactor of ", "file": "src/xce.build/lib/controller.capnp.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 11\u219219 lines (4 removed) | Also: encode_Data_ptr(cs, &(d->sha256), s->sha256);; encode_Data_ptr(cs, &(d->bytes), s->bytes);", + "fix": "Rewrote 11→19 lines (4 removed) | Also: encode_Data_ptr(cs, &(d->sha256), s->sha256);; encode_Data_ptr(cs, &(d->bytes), s->bytes);", "tags": [ "auto-detected", "refactor", @@ -2871,7 +2871,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "28 lines replaced/restructured", - "fix": "Rewrote 31\u21924 lines (28 removed)", + "fix": "Rewrote 31→4 lines (28 removed)", "tags": [ "auto-detected", "refactor", @@ -2887,7 +2887,7 @@ "error_message": "Significant refactor of ", "file": "src/include/typemap.h", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 8\u219218 lines (4 removed)", + "fix": "Rewrote 8→18 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -2903,7 +2903,7 @@ "error_message": "Significant refactor of ", "file": "src/include/request.h", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 10\u219219 lines (4 removed)", + "fix": "Rewrote 10→19 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -2935,7 +2935,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 5\u219210 lines (2 removed)", + "fix": "Rewrote 5→10 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -2951,7 +2951,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 9\u219211 lines (2 removed)", + "fix": "Rewrote 9→11 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -2966,7 +2966,7 @@ "timestamp": "2026-05-31T04:11:45.534Z", "error_message": "Missing error handling in struct", "file": ".kiro/specs/raft-library/design.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: Caller->>RaftSM: raft_on_vote_request(state, from, term); alt term < current_term", "tags": [ "auto-detected", @@ -2983,7 +2983,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/raft-library/design.md", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 8\u219226 lines (5 removed) | Also: * raft_init: initialize state for a node.; * All required callbacks (except on_trace and on_v | Also: - C standard library only (`stdint.h`, `pthread.h`; - `raft.c` may include `` for lock-fr | Also: | Threading | `pthread_mutex_t` (caller-owned) | P; | Atomics | None in library | Caller uses atomics ", + "fix": "Rewrote 8→26 lines (5 removed) | Also: * raft_init: initialize state for a node.; * All required callbacks (except on_trace and on_v | Also: - C standard library only (`stdint.h`, `pthread.h`; - `raft.c` may include `` for lock-fr | Also: | Threading | `pthread_mutex_t` (caller-owned) | P; | Atomics | None in library | Caller uses atomics ", "tags": [ "auto-detected", "refactor", @@ -3014,7 +3014,7 @@ "timestamp": "2026-05-31T04:58:21.609Z", "error_message": "Missing error handling in RAFT_H", "file": "src/lib/raft/raft.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -3063,7 +3063,7 @@ "error_message": "Significant refactor of ", "file": "src/lib/testsuite/test_raft.c", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 77\u2192107 lines (8 removed)", + "fix": "Rewrote 77→107 lines (8 removed)", "tags": [ "auto-detected", "refactor", @@ -3079,7 +3079,7 @@ "error_message": "Significant refactor of ", "file": "src/lib/raft/raft.c", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 9\u219262 lines (5 removed)", + "fix": "Rewrote 9→62 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -3095,7 +3095,7 @@ "error_message": "Significant refactor of ", "file": "src/lib/raft/raft.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 8\u219257 lines (4 removed)", + "fix": "Rewrote 8→57 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -3111,7 +3111,7 @@ "error_message": "Significant refactor of ", "file": "src/lib/raft/raft.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 15\u219238 lines (4 removed)", + "fix": "Rewrote 15→38 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -3127,7 +3127,7 @@ "error_message": "Significant refactor of ", "file": "src/lib/raft/raft_sim.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 6\u21923 lines (2 removed)", + "fix": "Rewrote 6→3 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -3159,7 +3159,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "32 lines replaced/restructured", - "fix": "Rewrote 49\u219216 lines (32 removed)", + "fix": "Rewrote 49→16 lines (32 removed)", "tags": [ "auto-detected", "refactor", @@ -3174,7 +3174,7 @@ "timestamp": "2026-05-31T07:26:39.758Z", "error_message": "Missing error handling in before", "file": "src/controller/ha.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -3191,7 +3191,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 6\u219219 lines (3 removed) | Also: typedef struct {; ha_state_t *ha; | Also: /* \u2500\u2500 Type definitions needed by ha_elect_evaluate; *", + "fix": "Rewrote 6→19 lines (3 removed) | Also: typedef struct {; ha_state_t *ha; | Also: /* ── Type definitions needed by ha_elect_evaluate; *", "tags": [ "auto-detected", "refactor", @@ -3207,7 +3207,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "167 lines replaced/restructured", - "fix": "Rewrote 291\u2192137 lines (167 removed) | Also: /* Clear voted_for: the old primary is dead; any v; * is stale and must not block the new election. */", + "fix": "Rewrote 291→137 lines (167 removed) | Also: /* Clear voted_for: the old primary is dead; any v; * is stale and must not block the new election. */", "tags": [ "auto-detected", "refactor", @@ -3223,7 +3223,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "12 lines replaced/restructured", - "fix": "Rewrote 28\u219268 lines (12 removed)", + "fix": "Rewrote 28→68 lines (12 removed)", "tags": [ "auto-detected", "refactor", @@ -3239,7 +3239,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 16\u219221 lines (2 removed) | Also: /* Task 6.3: delegate vote reply accumulation to r; * raft_on_vote_reply owns quorum tracking and fire", + "fix": "Rewrote 16→21 lines (2 removed) | Also: /* Task 6.3: delegate vote reply accumulation to r; * raft_on_vote_reply owns quorum tracking and fire", "tags": [ "auto-detected", "refactor", @@ -3255,7 +3255,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "7 lines replaced/restructured", - "fix": "Rewrote 10\u21924 lines (7 removed)", + "fix": "Rewrote 10→4 lines (7 removed)", "tags": [ "auto-detected", "refactor", @@ -3271,7 +3271,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 5\u219214 lines (2 removed)", + "fix": "Rewrote 5→14 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -3284,7 +3284,7 @@ { "id": "bug-206", "timestamp": "2026-05-31T08:40:00.000Z", - "error_message": "test_vote_reply_handler_term_fencing FAIL: Expected 10 Was 3 \u2014 ha->term must be atomically updated to peer term when peer term > ours", + "error_message": "test_vote_reply_handler_term_fencing FAIL: Expected 10 Was 3 — ha->term must be atomically updated to peer term when peer term > ours", "file": "src/controller/ha.c", "root_cause": "vote_reply_msg_handler delegated term fencing entirely to raft_cb_become_follower (via raft_on_vote_reply). In XCE_TESTING builds, vote_reply_dispatch calls xce_vote_reply_hook and returns early, bypassing raft_on_vote_reply and thus never updating ha->term.", "fix": "Added explicit CAS loop in vote_reply_msg_handler before vote_reply_dispatch to advance ha->term when vr_term > ha->term. This applies on both the production path and the XCE_TESTING hook path.", @@ -3306,7 +3306,7 @@ "error_message": "Wrong reference: test_raft_init_zero_peers_returns_einval should be test_raft_init_even_peers_returns_einval", "file": "src/lib/testsuite/test_raft.c", "root_cause": "Used \"test_raft_init_zero_peers_returns_einval\" instead of \"test_raft_init_even_peers_returns_einval\"", - "fix": "Changed test_raft_init_zero_peers_returns_einval \u2192 test_raft_init_even_peers_returns_einval", + "fix": "Changed test_raft_init_zero_peers_returns_einval → test_raft_init_even_peers_returns_einval", "tags": [ "auto-detected", "wrong-reference", @@ -3319,7 +3319,7 @@ { "id": "bug-208", "timestamp": "2026-05-31", - "error_message": "ASAN LeakSanitizer: detected memory leaks in scheduler testsuite \u2014 build exits with error code", + "error_message": "ASAN LeakSanitizer: detected memory leaks in scheduler testsuite — build exits with error code", "file": "src/scheduler/testsuite/test.c", "root_cause": "sched_meet_host_selection() lazily allocates w->host_eval_ctx via build_host_eval_ctx(). Four test functions (test_meet_host_selection_always_true_passes, test_meet_host_selection_always_false_fails, test_meet_host_selection_numeric_match, test_meet_host_selection_string_match) called zarray_fini() without first calling sched_worker_release_host_ctx() to free the lazily-allocated host_eval_ctx.", "fix": "Added sched_worker_release_host_ctx(&w) calls before zarray_fini() in all four affected test functions.", @@ -3356,7 +3356,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "24 lines replaced/restructured", - "fix": "Rewrote 28\u21923 lines (24 removed) | Also: if (my_config_tag == 0) {; INFO(\"ha_start_election_async: no local config (ta", + "fix": "Rewrote 28→3 lines (24 removed) | Also: if (my_config_tag == 0) {; INFO(\"ha_start_election_async: no local config (ta", "tags": [ "auto-detected", "refactor", @@ -3372,7 +3372,7 @@ "error_message": "Wrong reference: 2000 should be 5000", "file": "src/controller/ha.h", "root_cause": "Used \"2000\" instead of \"5000\"", - "fix": "Changed 2000 \u2192 5000", + "fix": "Changed 2000 → 5000", "tags": [ "auto-detected", "wrong-reference", @@ -3388,7 +3388,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/myapp.c", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 15\u219225 lines (3 removed)", + "fix": "Rewrote 15→25 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -3404,7 +3404,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.h", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 10\u219214 lines (8 removed)", + "fix": "Rewrote 10→14 lines (8 removed)", "tags": [ "auto-detected", "refactor", @@ -3420,7 +3420,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 7\u219217 lines (4 removed) | Also: /* Trigger async config fetch from primary so stan; #ifndef XCE_TESTING | Also: handle_request_vote_reply(vctx->ha, vctx->ctx, vct; /* Fix 5 (HIGH): do NOT free vctx here.", + "fix": "Rewrote 7→17 lines (4 removed) | Also: /* Trigger async config fetch from primary so stan; #ifndef XCE_TESTING | Also: handle_request_vote_reply(vctx->ha, vctx->ctx, vct; /* Fix 5 (HIGH): do NOT free vctx here.", "tags": [ "auto-detected", "refactor", @@ -3467,7 +3467,7 @@ "timestamp": "2026-05-31T12:54:26.372Z", "error_message": "Missing error handling in unknown", "file": "src/controller/ha.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -3484,7 +3484,7 @@ "error_message": "Wrong reference: Request_body_nodata should be Request_body_nomsg", "file": "src/controller/ha.c", "root_cause": "Used \"Request_body_nodata\" instead of \"Request_body_nomsg\"", - "fix": "Changed Request_body_nodata \u2192 Request_body_nomsg", + "fix": "Changed Request_body_nodata → Request_body_nomsg", "tags": [ "auto-detected", "wrong-reference", @@ -3500,7 +3500,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 10\u219218 lines (4 removed)", + "fix": "Rewrote 10→18 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -3516,7 +3516,7 @@ "error_message": "Wrong reference: vote_reply_msg_handler should be vote_reply_msg_handler_for_test", "file": "src/controller/ha.c", "root_cause": "Used \"vote_reply_msg_handler\" instead of \"vote_reply_msg_handler_for_test\"", - "fix": "Changed vote_reply_msg_handler \u2192 vote_reply_msg_handler_for_test", + "fix": "Changed vote_reply_msg_handler → vote_reply_msg_handler_for_test", "tags": [ "auto-detected", "wrong-reference", @@ -3532,7 +3532,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 13\u219236 lines (3 removed)", + "fix": "Rewrote 13→36 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -3548,7 +3548,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "20 lines replaced/restructured", - "fix": "Rewrote 36\u219213 lines (20 removed)", + "fix": "Rewrote 36→13 lines (20 removed)", "tags": [ "auto-detected", "refactor", @@ -3563,7 +3563,7 @@ "timestamp": "2026-06-01T01:49:30.537Z", "error_message": "Missing error handling in unknown", "file": "src/controller/cluster.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -3596,7 +3596,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "42 lines replaced/restructured", - "fix": "Rewrote 64\u219219 lines (42 removed)", + "fix": "Rewrote 64→19 lines (42 removed)", "tags": [ "auto-detected", "refactor", @@ -3611,7 +3611,7 @@ "timestamp": "2026-06-01T02:27:55.942Z", "error_message": "Missing error handling in struct", "file": "src/controller/ha.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -3628,7 +3628,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 10\u219214 lines (5 removed)", + "fix": "Rewrote 10→14 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -3644,7 +3644,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/CLAUDE.md", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 5\u219215 lines (5 removed)", + "fix": "Rewrote 5→15 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -3660,7 +3660,7 @@ "error_message": "Wrong operator: && should be !=", "file": "src/controller/ha.c", "root_cause": "Used \"&&\" instead of \"!=\"", - "fix": "Changed operator && \u2192 !=", + "fix": "Changed operator && → !=", "tags": [ "auto-detected", "operator-fix", @@ -3676,7 +3676,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "10 lines replaced/restructured", - "fix": "Rewrote 16\u21927 lines (10 removed)", + "fix": "Rewrote 16→7 lines (10 removed)", "tags": [ "auto-detected", "refactor", @@ -3692,7 +3692,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "140 lines replaced/restructured", - "fix": "Rewrote 173\u219211 lines (140 removed)", + "fix": "Rewrote 173→11 lines (140 removed)", "tags": [ "auto-detected", "refactor", @@ -3708,7 +3708,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/myapp.c", "root_cause": "16 lines replaced/restructured", - "fix": "Rewrote 25\u219212 lines (16 removed)", + "fix": "Rewrote 25→12 lines (16 removed)", "tags": [ "auto-detected", "refactor", @@ -3740,7 +3740,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 31\u219237 lines (2 removed)", + "fix": "Rewrote 31→37 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -3788,7 +3788,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.h", "root_cause": "21 lines replaced/restructured", - "fix": "Rewrote 28\u21925 lines (21 removed)", + "fix": "Rewrote 28→5 lines (21 removed)", "tags": [ "auto-detected", "refactor", @@ -3852,7 +3852,7 @@ "error_message": "Wrong reference: test_ha_connect should be test_ha_ctrlhello", "file": "src/controller/testsuite/controller_test.c", "root_cause": "Used \"test_ha_connect\" instead of \"test_ha_ctrlhello\"", - "fix": "Changed test_ha_connect \u2192 test_ha_ctrlhello", + "fix": "Changed test_ha_connect → test_ha_ctrlhello", "tags": [ "auto-detected", "wrong-reference", @@ -3868,7 +3868,7 @@ "error_message": "Wrong reference: vcpu should be vcpus", "file": "src/tools/deploy-ha.sh", "root_cause": "Used \"vcpu\" instead of \"vcpus\"", - "fix": "Changed vcpu \u2192 vcpus", + "fix": "Changed vcpu → vcpus", "tags": [ "auto-detected", "wrong-reference", @@ -3884,7 +3884,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "10 lines replaced/restructured", - "fix": "Rewrote 10\u21926 lines (10 removed)", + "fix": "Rewrote 10→6 lines (10 removed)", "tags": [ "auto-detected", "refactor", @@ -3900,7 +3900,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 7\u219222 lines (2 removed)", + "fix": "Rewrote 7→22 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -3916,7 +3916,7 @@ "error_message": "Significant refactor of ", "file": "src/include/typemap.h", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 5\u219210 lines (3 removed)", + "fix": "Rewrote 5→10 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -3932,7 +3932,7 @@ "error_message": "Significant refactor of ", "file": "src/lib/request.c", "root_cause": "32 lines replaced/restructured", - "fix": "Rewrote 49\u219260 lines (32 removed)", + "fix": "Rewrote 49→60 lines (32 removed)", "tags": [ "auto-detected", "refactor", @@ -3948,7 +3948,7 @@ "error_message": "Significant refactor of ", "file": ".claude/skills/xce-deploy-ha/SKILL.md", "root_cause": "7 lines replaced/restructured", - "fix": "Rewrote 17\u219232 lines (7 removed)", + "fix": "Rewrote 17→32 lines (7 removed)", "tags": [ "auto-detected", "refactor", @@ -3964,7 +3964,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/CLAUDE.md", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 6\u21929 lines (3 removed)", + "fix": "Rewrote 6→9 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -3979,7 +3979,7 @@ "timestamp": "2026-06-01T07:28:15.703Z", "error_message": "Missing error handling in unknown", "file": "src/controller/testsuite/test_ha_connection_reuse.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -4012,7 +4012,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_ha_connection_reuse.c", "root_cause": "19 lines replaced/restructured", - "fix": "Rewrote 67\u219286 lines (19 removed)", + "fix": "Rewrote 67→86 lines (19 removed)", "tags": [ "auto-detected", "refactor", @@ -4044,7 +4044,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-heartbeat-liveness/requirements.md", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 10\u219217 lines (5 removed)", + "fix": "Rewrote 10→17 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -4060,7 +4060,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 6\u219210 lines (2 removed)", + "fix": "Rewrote 6→10 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -4092,7 +4092,7 @@ "error_message": "Wrong reference: startup_validation should be testsuite_test_ha_liveness", "file": "src/xce.build/build.ninja", "root_cause": "Used \"startup_validation\" instead of \"testsuite_test_ha_liveness\"", - "fix": "Changed startup_validation \u2192 testsuite_test_ha_liveness", + "fix": "Changed startup_validation → testsuite_test_ha_liveness", "tags": [ "auto-detected", "wrong-reference", @@ -4108,7 +4108,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "76 lines replaced/restructured", - "fix": "Rewrote 112\u2192155 lines (76 removed)", + "fix": "Rewrote 112→155 lines (76 removed)", "tags": [ "auto-detected", "refactor", @@ -4124,7 +4124,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_ha_liveness.c", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 21\u219224 lines (5 removed)", + "fix": "Rewrote 21→24 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -4140,7 +4140,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_ha_failover.c", "root_cause": "7 lines replaced/restructured", - "fix": "Rewrote 29\u219247 lines (7 removed)", + "fix": "Rewrote 29→47 lines (7 removed)", "tags": [ "auto-detected", "refactor", @@ -4172,7 +4172,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "22 lines replaced/restructured", - "fix": "Rewrote 53\u219221 lines (22 removed)", + "fix": "Rewrote 53→21 lines (22 removed)", "tags": [ "auto-detected", "refactor", @@ -4188,7 +4188,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 8\u219210 lines (2 removed)", + "fix": "Rewrote 8→10 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -4219,7 +4219,7 @@ "timestamp": "2026-06-03T05:20:44.299Z", "error_message": "Missing error handling in lives", "file": ".kiro/specs/ha-config-bootstrap/research.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -4268,7 +4268,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.h", "root_cause": "11 lines replaced/restructured", - "fix": "Rewrote 14\u21923 lines (11 removed)", + "fix": "Rewrote 14→3 lines (11 removed)", "tags": [ "auto-detected", "refactor", @@ -4284,7 +4284,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_ha_role.c", "root_cause": "79 lines replaced/restructured", - "fix": "Rewrote 87\u21923 lines (79 removed)", + "fix": "Rewrote 87→3 lines (79 removed)", "tags": [ "auto-detected", "refactor", @@ -4300,7 +4300,7 @@ "error_message": "Wrong reference: test_am_i_primary should be test_ha_cluster", "file": "src/controller/testsuite/controller_test.c", "root_cause": "Used \"test_am_i_primary\" instead of \"test_ha_cluster\"", - "fix": "Changed test_am_i_primary \u2192 test_ha_cluster", + "fix": "Changed test_am_i_primary → test_ha_cluster", "tags": [ "auto-detected", "wrong-reference", @@ -4316,7 +4316,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.h", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 10\u21923 lines (8 removed)", + "fix": "Rewrote 10→3 lines (8 removed)", "tags": [ "auto-detected", "refactor", @@ -4332,7 +4332,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "21 lines replaced/restructured", - "fix": "Rewrote 26\u21924 lines (21 removed) | Also: /* ha_check_heartbeat_nocluster() removed from the; * ha_watchdog_thread(). XCE_TESTING shim preserve", + "fix": "Rewrote 26→4 lines (21 removed) | Also: /* ha_check_heartbeat_nocluster() removed from the; * ha_watchdog_thread(). XCE_TESTING shim preserve", "tags": [ "auto-detected", "refactor", @@ -4348,7 +4348,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_ha_role.c", "root_cause": "63 lines replaced/restructured", - "fix": "Rewrote 68\u21924 lines (63 removed) | Also: /* SIGUSR1 must set failover_requested via async-s; static void test_sigusr1_sets_failover_requested(v | Also: RUN_TEST(test_watchdog_starts_and_stops);; RUN_TEST(test_heartbeat_no_action_when_no_primary)", + "fix": "Rewrote 68→4 lines (63 removed) | Also: /* SIGUSR1 must set failover_requested via async-s; static void test_sigusr1_sets_failover_requested(v | Also: RUN_TEST(test_watchdog_starts_and_stops);; RUN_TEST(test_heartbeat_no_action_when_no_primary)", "tags": [ "auto-detected", "refactor", @@ -4364,7 +4364,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/manual-tests.md", "root_cause": "6 lines replaced/restructured", - "fix": "Rewrote 8\u21928 lines (6 removed) | Also: **Expected within 1 second on both standbys:**; **Expected on the winner (needs 1 vote from the ot | Also: defer_config_fetch=1; Config fetch successful from primary (tag=...) | Also: Date:; Commit: (run: cd /home/rshen/projects/xce/worktree", + "fix": "Rewrote 8→8 lines (6 removed) | Also: **Expected within 1 second on both standbys:**; **Expected on the winner (needs 1 vote from the ot | Also: defer_config_fetch=1; Config fetch successful from primary (tag=...) | Also: Date:; Commit: (run: cd /home/rshen/projects/xce/worktree", "tags": [ "auto-detected", "refactor", @@ -4380,7 +4380,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-config-bootstrap/manual-tests.md", "root_cause": "95 lines replaced/restructured", - "fix": "Rewrote 158\u219265 lines (95 removed) | Also: - [ ] `ha-term.bin` file exists after election; - [ ] Magic bytes are `0xDEADBEEFCAFEBABE` (little", + "fix": "Rewrote 158→65 lines (95 removed) | Also: - [ ] `ha-term.bin` file exists after election; - [ ] Magic bytes are `0xDEADBEEFCAFEBABE` (little", "tags": [ "auto-detected", "refactor", @@ -4396,7 +4396,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "6 lines replaced/restructured", - "fix": "Rewrote 45\u219262 lines (6 removed)", + "fix": "Rewrote 45→62 lines (6 removed)", "tags": [ "auto-detected", "refactor", @@ -4411,7 +4411,7 @@ "timestamp": "2026-06-04T00:24:46.380Z", "error_message": "Missing error handling in unknown", "file": "src/controller/myapp.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -4428,7 +4428,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/CLAUDE.md", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 5\u21927 lines (4 removed)", + "fix": "Rewrote 5→7 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -4443,7 +4443,7 @@ "timestamp": "2026-06-04T09:46:14.610Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/ha-repl-hardening/design.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -4476,7 +4476,7 @@ "error_message": "Significant refactor of ", "file": "src/schema/controller.capnp", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 4\u21924 lines (3 removed)", + "fix": "Rewrote 4→4 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -4492,7 +4492,7 @@ "error_message": "Significant refactor of ", "file": "src/include/typemap.h", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 5\u21927 lines (4 removed)", + "fix": "Rewrote 5→7 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -4508,7 +4508,7 @@ "error_message": "Significant refactor of ", "file": "src/schema/controller.capnp", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 4\u219213 lines (2 removed)", + "fix": "Rewrote 4→13 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -4524,7 +4524,7 @@ "error_message": "Significant refactor of ", "file": "src/include/typemap.h", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 4\u21928 lines (2 removed)", + "fix": "Rewrote 4→8 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -4539,8 +4539,8 @@ "timestamp": "2026-06-04T14:33:03.464Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/testsuite/test_config_repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", - "fix": "Added try/catch block | Also: /* \u2500\u2500 Task 2.1: OPCode_walCatchupEntry handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500; #ifdef XCE_TESTING | Also: /* Task 2.1: OPCode_walCatchupEntry handler */; RUN_TEST(test_wal_catchup_entry_advances_applied_lsn);", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", + "fix": "Added try/catch block | Also: /* ── Task 2.1: OPCode_walCatchupEntry handler ─────────────; #ifdef XCE_TESTING | Also: /* Task 2.1: OPCode_walCatchupEntry handler */; RUN_TEST(test_wal_catchup_entry_advances_applied_lsn);", "tags": [ "auto-detected", "error-handling", @@ -4555,7 +4555,7 @@ "timestamp": "2026-06-04T14:41:35.073Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/cluster.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: case OPCode_walCatchupEntry:; /* Task 2.1: standby applies a single WAL catch-up entry fro", "tags": [ "auto-detected", @@ -4572,7 +4572,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "57 lines replaced/restructured", - "fix": "Rewrote 82\u219216 lines (57 removed)", + "fix": "Rewrote 82→16 lines (57 removed)", "tags": [ "auto-detected", "refactor", @@ -4587,7 +4587,7 @@ "timestamp": "2026-06-04T14:45:42.730Z", "error_message": "Missing error handling in unknown", "file": "src/controller/ha.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -4603,7 +4603,7 @@ "timestamp": "2026-06-04T14:46:44.652Z", "error_message": "Missing error handling in unknown", "file": "src/controller/ha.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: #include \"persist.h\" /* persist_t, apply_wal_entry */; #include \"typemap.h\" /* wal_catchup_entry_t */", "tags": [ "auto-detected", @@ -4619,7 +4619,7 @@ "timestamp": "2026-06-04T15:09:54.280Z", "error_message": "Missing error handling in unknown", "file": "src/controller/testsuite/test_config_repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -4636,7 +4636,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "22 lines replaced/restructured", - "fix": "Rewrote 58\u2192114 lines (22 removed)", + "fix": "Rewrote 58→114 lines (22 removed)", "tags": [ "auto-detected", "refactor", @@ -4652,7 +4652,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 10\u219215 lines (3 removed)", + "fix": "Rewrote 10→15 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -4684,7 +4684,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 44\u219289 lines (4 removed) | Also: /* Decode stateReport and call convergence tracker; memset(&sr, 0, sizeof(sr));", + "fix": "Rewrote 44→89 lines (4 removed) | Also: /* Decode stateReport and call convergence tracker; memset(&sr, 0, sizeof(sr));", "tags": [ "auto-detected", "refactor", @@ -4732,7 +4732,7 @@ "error_message": "Wrong reference: test_evaluate_repl_action_stream_wal_from_zero should be test_evaluate_repl_action_send_snapshot_lsn_zero", "file": "src/controller/testsuite/test_ha_role.c", "root_cause": "Used \"test_evaluate_repl_action_stream_wal_from_zero\" instead of \"test_evaluate_repl_action_send_snapshot_lsn_zero\"", - "fix": "Changed test_evaluate_repl_action_stream_wal_from_zero \u2192 test_evaluate_repl_action_send_snapshot_lsn_zero", + "fix": "Changed test_evaluate_repl_action_stream_wal_from_zero → test_evaluate_repl_action_send_snapshot_lsn_zero", "tags": [ "auto-detected", "wrong-reference", @@ -4763,7 +4763,7 @@ "timestamp": "2026-06-04T17:34:23.150Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/testsuite/test_config_repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -4780,7 +4780,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 71\u219293 lines (4 removed)", + "fix": "Rewrote 71→93 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -4795,7 +4795,7 @@ "timestamp": "2026-06-04T17:40:05.798Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/config_repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -4844,7 +4844,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 6\u219215 lines (2 removed)", + "fix": "Rewrote 6→15 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -4859,7 +4859,7 @@ "timestamp": "2026-06-04T23:21:45.737Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/config_repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -4876,7 +4876,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_config_repl.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 9\u219212 lines (2 removed)", + "fix": "Rewrote 9→12 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -4892,7 +4892,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-repl-hardening/tasks.md", "root_cause": "9 lines replaced/restructured", - "fix": "Rewrote 9\u219235 lines (9 removed)", + "fix": "Rewrote 9→35 lines (9 removed)", "tags": [ "auto-detected", "refactor", @@ -4908,7 +4908,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 7\u219210 lines (4 removed) | Also: /* Task 3.5: snapshot transfer.; * Creates a checkpoint in the delaymgr thread (not", + "fix": "Rewrote 7→10 lines (4 removed) | Also: /* Task 3.5: snapshot transfer.; * Creates a checkpoint in the delaymgr thread (not", "tags": [ "auto-detected", "refactor", @@ -4988,7 +4988,7 @@ "error_message": "Wrong reference: uintptr_t should be void", "file": "src/controller/testsuite/test_ha_evaluate_committing.c", "root_cause": "Used \"uintptr_t\" instead of \"void\"", - "fix": "Changed uintptr_t \u2192 void", + "fix": "Changed uintptr_t → void", "tags": [ "auto-detected", "wrong-reference", @@ -5004,7 +5004,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "149 lines replaced/restructured", - "fix": "Rewrote 207\u219238 lines (149 removed)", + "fix": "Rewrote 207→38 lines (149 removed)", "tags": [ "auto-detected", "refactor", @@ -5036,7 +5036,7 @@ "error_message": "Wrong reference: 10000 should be 60000", "file": "src/controller/ha.h", "root_cause": "Used \"10000\" instead of \"60000\"", - "fix": "Changed 10000 \u2192 60000", + "fix": "Changed 10000 → 60000", "tags": [ "auto-detected", "wrong-reference", @@ -5052,7 +5052,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 24\u219242 lines (5 removed)", + "fix": "Rewrote 24→42 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -5068,7 +5068,7 @@ "error_message": "Wrong reference: 10000 should be HA_DEFAULT_COMMITTING_TIMEOUT_MS", "file": "src/controller/testsuite/test_ha_evaluate_committing.c", "root_cause": "Used \"10000\" instead of \"HA_DEFAULT_COMMITTING_TIMEOUT_MS\"", - "fix": "Changed 10000 \u2192 HA_DEFAULT_COMMITTING_TIMEOUT_MS", + "fix": "Changed 10000 → HA_DEFAULT_COMMITTING_TIMEOUT_MS", "tags": [ "auto-detected", "wrong-reference", @@ -5083,7 +5083,7 @@ "timestamp": "2026-06-05T05:10:13.646Z", "error_message": "Missing error handling in unknown", "file": "src/controller/config_repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -5099,7 +5099,7 @@ "timestamp": "2026-06-05T05:39:48.707Z", "error_message": "Missing error handling in unknown", "file": "src/controller/testsuite/test_config_repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -5115,7 +5115,7 @@ "timestamp": "2026-06-05T06:50:00Z", "error_message": "3-node startup: majority convergence never fires; cluster falls through to committing_timeout degraded-mode WARN", "file": "src/controller/ha.c", - "root_cause": "ha->n_peers was set to candidates_count (3, total including self) in ha_start_election_async. ha_check_majority_convergence loops i=0..n_peers-1 checking standby_converged[], but standby_converged[] is indexed by votes_idx (relative, self excluded: 0..n_other-1). With n_peers=3, it checked standby_converged[2] which is never set, so n_converged (2) < n_peers (3) always \u2014 majority convergence path could never succeed.", + "root_cause": "ha->n_peers was set to candidates_count (3, total including self) in ha_start_election_async. ha_check_majority_convergence loops i=0..n_peers-1 checking standby_converged[], but standby_converged[] is indexed by votes_idx (relative, self excluded: 0..n_other-1). With n_peers=3, it checked standby_converged[2] which is never set, so n_converged (2) < n_peers (3) always — majority convergence path could never succeed.", "fix": "Changed ha->n_peers = n_peers to ha->n_peers = n_other (= n_peers-1 = other-node count). Moved the assignment after n_other is computed. ha_repl_ack_received quorum formula (ha->n_peers+2)/2 still yields correct majority for both old and new value.", "tags": [ "ha", @@ -5134,7 +5134,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 7\u219212 lines (2 removed) | Also: /* Disarm any existing poll ctx */; free(old);", + "fix": "Rewrote 7→12 lines (2 removed) | Also: /* Disarm any existing poll ctx */; free(old);", "tags": [ "auto-detected", "refactor", @@ -5149,7 +5149,7 @@ "timestamp": "2026-06-05T07:23:00.000Z", "error_message": "AddressSanitizer: attempting double-free on sctx in ha_connect_to_peer", "file": "src/controller/ha.c", - "root_cause": "endpoint_fini calls FREEUP(ep->data) which frees sctx, then ha_connect_to_peer also calls free(sctx) \u2014 double-free at lines 3594-3595 and 3600-3601", + "root_cause": "endpoint_fini calls FREEUP(ep->data) which frees sctx, then ha_connect_to_peer also calls free(sctx) — double-free at lines 3594-3595 and 3600-3601", "fix": "Set ep->data = NULL before endpoint_fini so FREEUP becomes a no-op; then free(sctx) explicitly in the error paths", "tags": [ "double-free", @@ -5165,7 +5165,7 @@ { "id": "bug-007", "timestamp": "2026-06-05T07:23:00.000Z", - "error_message": "AddressSanitizer: heap-use-after-free in liveness_poll_fn at ha.c:3293 \u2014 reading ctx->cancelled after ha_arm_heartbeat_timeout_vtask freed the old ctx", + "error_message": "AddressSanitizer: heap-use-after-free in liveness_poll_fn at ha.c:3293 — reading ctx->cancelled after ha_arm_heartbeat_timeout_vtask freed the old ctx", "file": "src/controller/ha.c", "root_cause": "ha_arm_heartbeat_timeout_vtask freed the old liveness_poll_ctx_t immediately after setting cancelled=1, but the delaymgr task was already in flight reading ctx->cancelled=0 and then continued to read ctx->ha after free", "fix": "ha_arm_heartbeat_timeout_vtask no longer frees old ctx; liveness_poll_fn now frees ctx when it fires with cancelled=1 (guarded by #ifndef XCE_TESTING to avoid bad-free on stack-allocated test ctxs)", @@ -5187,7 +5187,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 41\u219287 lines (2 removed)", + "fix": "Rewrote 41→87 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -5203,7 +5203,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "6 lines replaced/restructured", - "fix": "Rewrote 8\u21925 lines (6 removed)", + "fix": "Rewrote 8→5 lines (6 removed)", "tags": [ "auto-detected", "refactor", @@ -5219,7 +5219,7 @@ "error_message": "Wrong reference: DEBUG2 should be INFO", "file": "src/controller/ha.c", "root_cause": "Used \"DEBUG2\" instead of \"INFO\"", - "fix": "Changed DEBUG2 \u2192 INFO", + "fix": "Changed DEBUG2 → INFO", "tags": [ "auto-detected", "wrong-reference", @@ -5283,7 +5283,7 @@ "error_message": "Significant refactor of ", "file": "src/docs/cluster-config-split-analysis.md", "root_cause": "8 lines replaced/restructured", - "fix": "Rewrote 11\u219221 lines (8 removed) | Also: ### Phase 1: Schema split (wire-compatible); 4. **Keep the old `ClusterConfig` fields intact** ", + "fix": "Rewrote 11→21 lines (8 removed) | Also: ### Phase 1: Schema split (wire-compatible); 4. **Keep the old `ClusterConfig` fields intact** ", "tags": [ "auto-detected", "refactor", @@ -5299,7 +5299,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/myapp.c", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 27\u219236 lines (5 removed)", + "fix": "Rewrote 27→36 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -5315,7 +5315,7 @@ "error_message": "Significant refactor of ", "file": "src/lib/raft/raft.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 9\u219215 lines (4 removed)", + "fix": "Rewrote 9→15 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -5331,7 +5331,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "37 lines replaced/restructured", - "fix": "Rewrote 41\u21924 lines (37 removed)", + "fix": "Rewrote 41→4 lines (37 removed)", "tags": [ "auto-detected", "refactor", @@ -5471,7 +5471,7 @@ "error_message": "Wrong reference: last_heartbeat should be voted_for_term", "file": "src/controller/ha.h", "root_cause": "Used \"last_heartbeat\" instead of \"voted_for_term\"", - "fix": "Changed last_heartbeat \u2192 voted_for_term", + "fix": "Changed last_heartbeat → voted_for_term", "tags": [ "auto-detected", "wrong-reference", @@ -5487,7 +5487,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/myapp.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 13\u219217 lines (4 removed)", + "fix": "Rewrote 13→17 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -5538,7 +5538,7 @@ "timestamp": "2026-06-10T01:36:59.112Z", "error_message": "Missing error handling in unknown", "file": "src/schema/controller.capnp", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -5554,7 +5554,7 @@ "timestamp": "2026-06-10T01:37:29.771Z", "error_message": "Missing error handling in struct", "file": "src/include/typemap.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -5571,7 +5571,7 @@ "error_message": "Significant refactor of ", "file": "src/include/typemap.h", "root_cause": "6 lines replaced/restructured", - "fix": "Rewrote 16\u219231 lines (6 removed)", + "fix": "Rewrote 16→31 lines (6 removed)", "tags": [ "auto-detected", "refactor", @@ -5586,7 +5586,7 @@ "timestamp": "2026-06-10T01:38:31.262Z", "error_message": "Missing error handling in unknown", "file": "src/include/request.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -5618,7 +5618,7 @@ "timestamp": "2026-06-10T01:39:31.683Z", "error_message": "Missing error handling in unknown", "file": "src/lib/request.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: if (req->body_which == Request_body_walcatchupentrymsg; && req->body.walcatchupentrymsg != NULL) { | Also: /* wal_entry_to_wire: fill a wire WalCatchupEntry from a C e; * fields and raw payload bytes. Sub-entry nesting is one le", "tags": [ "auto-detected", @@ -5653,7 +5653,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 29\u219232 lines (4 removed)", + "fix": "Rewrote 29→32 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -5669,7 +5669,7 @@ "error_message": "Wrong reference: payload should be raw_payload", "file": "src/controller/config_repl.c", "root_cause": "Used \"payload\" instead of \"raw_payload\"", - "fix": "Changed payload \u2192 raw_payload", + "fix": "Changed payload → raw_payload", "tags": [ "auto-detected", "wrong-reference", @@ -5685,7 +5685,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 13\u219216 lines (2 removed)", + "fix": "Rewrote 13→16 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -5701,7 +5701,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_ha_role.c", "root_cause": "7 lines replaced/restructured", - "fix": "Rewrote 49\u2192137 lines (7 removed)", + "fix": "Rewrote 49→137 lines (7 removed)", "tags": [ "auto-detected", "refactor", @@ -5716,7 +5716,7 @@ "timestamp": "2026-06-10T01:49:36.997Z", "error_message": "Missing error handling in unknown", "file": "src/controller/ha.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -5785,7 +5785,7 @@ "error_message": "Significant refactor of ", "file": "src/scheduler/src/scheduler.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 4\u21926 lines (4 removed)", + "fix": "Rewrote 4→6 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -5838,7 +5838,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 9\u219215 lines (4 removed)", + "fix": "Rewrote 9→15 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -5870,7 +5870,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/cluster.c", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 8\u219213 lines (3 removed) | Also: /* Req 4.1: ctrlhello counts as primary activity \u2014; if ((ctrl_role_t)atomic_load_explicit(&c->ha.role,", + "fix": "Rewrote 8→13 lines (3 removed) | Also: /* Req 4.1: ctrlhello counts as primary activity —; if ((ctrl_role_t)atomic_load_explicit(&c->ha.role,", "tags": [ "auto-detected", "refactor", @@ -5886,7 +5886,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/config_repl.c", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 6\u219212 lines (3 removed)", + "fix": "Rewrote 6→12 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -5901,7 +5901,7 @@ "timestamp": "2026-06-10T07:56:21.347Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/testsuite/test_repl_unit.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: /* =========================================================; * Task 1.1: repl_broadcast send path tests", "tags": [ "auto-detected", @@ -5918,7 +5918,7 @@ "error_message": "Wrong reference: WAL_JOB should be WAL_JOB_SUBMIT", "file": "src/controller/testsuite/test_repl_unit.c", "root_cause": "Used \"WAL_JOB\" instead of \"WAL_JOB_SUBMIT\"", - "fix": "Changed WAL_JOB \u2192 WAL_JOB_SUBMIT", + "fix": "Changed WAL_JOB → WAL_JOB_SUBMIT", "tags": [ "auto-detected", "wrong-reference", @@ -5933,7 +5933,7 @@ "timestamp": "2026-06-10T07:58:15.167Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -5950,7 +5950,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/repl.c", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 42\u219265 lines (3 removed)", + "fix": "Rewrote 42→65 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -5966,7 +5966,7 @@ "error_message": "Wrong reference: DEBUG3 should be TRACE", "file": "src/controller/repl.c", "root_cause": "Used \"DEBUG3\" instead of \"TRACE\"", - "fix": "Changed DEBUG3 \u2192 TRACE", + "fix": "Changed DEBUG3 → TRACE", "tags": [ "auto-detected", "wrong-reference", @@ -5982,7 +5982,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/ha.h", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 12\u219216 lines (3 removed)", + "fix": "Rewrote 12→16 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -5998,7 +5998,7 @@ "error_message": "Wrong reference: request should be ep", "file": "src/controller/cluster.c", "root_cause": "Used \"request\" instead of \"ep\"", - "fix": "Changed request \u2192 ep", + "fix": "Changed request → ep", "tags": [ "auto-detected", "wrong-reference", @@ -6013,7 +6013,7 @@ "timestamp": "2026-06-10T08:47:48.296Z", "error_message": "Missing error handling in unknown", "file": "src/controller/testsuite/test_ha_role.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block | Also: /* Task 3.3 (ha-wal-replication): dispatch gate standby-role; RUN_TEST(test_dispatch_gate_allows_wal_catchup_in_standby_ro", "tags": [ "auto-detected", @@ -6029,7 +6029,7 @@ "timestamp": "2026-06-10T08:50:27.397Z", "error_message": "Missing error handling in struct", "file": "src/include/typemap.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6061,7 +6061,7 @@ "timestamp": "2026-06-10T09:59:08.979Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/testsuite/test_p2_integration.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6096,7 +6096,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_p2_integration.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 50\u219272 lines (4 removed)", + "fix": "Rewrote 50→72 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -6112,7 +6112,7 @@ "error_message": "Wrong reference: test_catchup_full_lifecycle_integration should be test_single_node_committed_lsn_unchanged", "file": "src/controller/testsuite/test_p2_integration.c", "root_cause": "Used \"test_catchup_full_lifecycle_integration\" instead of \"test_single_node_committed_lsn_unchanged\"", - "fix": "Changed test_catchup_full_lifecycle_integration \u2192 test_single_node_committed_lsn_unchanged", + "fix": "Changed test_catchup_full_lifecycle_integration → test_single_node_committed_lsn_unchanged", "tags": [ "auto-detected", "wrong-reference", @@ -6127,7 +6127,7 @@ "timestamp": "2026-06-10T10:37:49.028Z", "error_message": "Missing error handling in unknown", "file": "src/controller/testsuite/test_repl_unit.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6160,7 +6160,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/steering/product.md", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 13\u219222 lines (5 removed)", + "fix": "Rewrote 13→22 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -6192,7 +6192,7 @@ "error_message": "Significant refactor of ", "file": "CLAUDE.md", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 5\u21925 lines (4 removed)", + "fix": "Rewrote 5→5 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -6224,7 +6224,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/CLAUDE.md", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 6\u21925 lines (3 removed) | Also: ### Controllers; - Struct: `type {Resource}Controller struct{}`", + "fix": "Rewrote 6→5 lines (3 removed) | Also: ### Controllers; - Struct: `type {Resource}Controller struct{}`", "tags": [ "auto-detected", "refactor", @@ -6256,7 +6256,7 @@ "error_message": "Wrong reference: signal should be stdio", "file": "src/lib/delay.c", "root_cause": "Used \"signal\" instead of \"stdio\"", - "fix": "Changed signal \u2192 stdio", + "fix": "Changed signal → stdio", "tags": [ "auto-detected", "wrong-reference", @@ -6271,7 +6271,7 @@ "timestamp": "2026-06-11T03:30:54.341Z", "error_message": "Missing error handling in XCE_TESTING", "file": "src/controller/ha.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6320,7 +6320,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/job.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 15\u219228 lines (2 removed)", + "fix": "Rewrote 15→28 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -6352,7 +6352,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-consistency-invariants/research.md", "root_cause": "3 lines replaced/restructured", - "fix": "Rewrote 7\u21927 lines (3 removed)", + "fix": "Rewrote 7→7 lines (3 removed)", "tags": [ "auto-detected", "refactor", @@ -6387,7 +6387,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/specs/ha-consistency-invariants/requirements.md", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 3\u21924 lines (2 removed)", + "fix": "Rewrote 3→4 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -6466,7 +6466,7 @@ "timestamp": "2026-06-11T07:53:43.868Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/ha-consistency-invariants/research.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6498,7 +6498,7 @@ "timestamp": "2026-06-11T08:46:20.815Z", "error_message": "Missing error handling in unknown", "file": ".kiro/steering/roadmap.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6579,7 +6579,7 @@ "error_message": "Significant refactor of ", "file": ".kiro/steering/roadmap.md", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 6\u21928 lines (4 removed)", + "fix": "Rewrote 6→8 lines (4 removed)", "tags": [ "auto-detected", "refactor", @@ -6594,7 +6594,7 @@ "timestamp": "2026-06-11T13:30:00Z", "error_message": "test_commit_engine fixture: Expected 0 Was 1 from repl_add_standby", "file": "src/controller/testsuite/test_commit_engine.c", - "root_cause": "repl_add_standby returns the slot index (>=0) on success, not 0 \u2014 second add legitimately returns 1", + "root_cause": "repl_add_standby returns the slot index (>=0) on success, not 0 — second add legitimately returns 1", "fix": "fixture asserts TEST_ASSERT_EQUAL_INT(i, repl_add_standby(...)) per standby slot", "tags": [ "controller", @@ -6611,8 +6611,8 @@ "timestamp": "2026-06-11T13:35:00Z", "error_message": "3 pre-existing controller testsuite failures masked by exit code 0: test_committed_lsn_does_not_advance_on_timeout (-110), test_repl_ack_after_promote_gate_allows_jobsub (-10013), test_rdhup_primary_dispatch_gate_opens (-10013)", "file": "src/controller/testsuite/controller_test.c", - "root_cause": "each test group calls UNITY_BEGIN/UNITY_END internally, resetting Unity counters \u2014 main()'s final UNITY_END returns 0 unless the LAST group fails, so meson reported the suite green despite 3 real failures; Unity also misattributes failure FILE names to whichever file's UNITY_BEGIN ran (e.g. reports test_repl_unit.c for a test living in test_p2_integration.c)", - "fix": "not fixed (out of task 1.1 boundary) \u2014 documented; failures exist identically on baseline without ha-commit-engine changes (verified by stash/rebuild). persist_wal_sync_append returns -ETIMEDOUT while the test expects rc==0 with sync_rc==-ETIMEDOUT; the two gate tests fail ha_has_write_quorum (standby_endpoints empty with n_peers=2)", + "root_cause": "each test group calls UNITY_BEGIN/UNITY_END internally, resetting Unity counters — main()'s final UNITY_END returns 0 unless the LAST group fails, so meson reported the suite green despite 3 real failures; Unity also misattributes failure FILE names to whichever file's UNITY_BEGIN ran (e.g. reports test_repl_unit.c for a test living in test_p2_integration.c)", + "fix": "not fixed (out of task 1.1 boundary) — documented; failures exist identically on baseline without ha-commit-engine changes (verified by stash/rebuild). persist_wal_sync_append returns -ETIMEDOUT while the test expects rc==0 with sync_rc==-ETIMEDOUT; the two gate tests fail ha_has_write_quorum (standby_endpoints empty with n_peers=2)", "tags": [ "controller", "testsuite", @@ -6631,7 +6631,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/commit.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 11\u219221 lines (2 removed) | Also: (void)timeout_ms; /* RETAIN never honors the timeo; * via commit_on_ack (Req 2.1). */", + "fix": "Rewrote 11→21 lines (2 removed) | Also: (void)timeout_ms; /* RETAIN never honors the timeo; * via commit_on_ack (Req 2.1). */", "tags": [ "auto-detected", "refactor", @@ -6646,8 +6646,8 @@ "timestamp": "2026-06-11T15:28:35.990Z", "error_message": "Missing error handling in unknown", "file": "src/controller/testsuite/test_commit_engine.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", - "fix": "Added try/catch block | Also: /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550; * Task 3.1 \u2014 membership re-evaluation (commit_on_membership_ | Also: /* Task 3.1 \u2014 membership re-evaluation */; RUN_TEST(test_catchup_standby_ack_does_not_count_toward_pend", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", + "fix": "Added try/catch block | Also: /* ═════════════════════════════════════════════════════════; * Task 3.1 — membership re-evaluation (commit_on_membership_ | Also: /* Task 3.1 — membership re-evaluation */; RUN_TEST(test_catchup_standby_ack_does_not_count_toward_pend", "tags": [ "auto-detected", "error-handling", @@ -6662,7 +6662,7 @@ "timestamp": "2026-06-11T15:55:52.451Z", "error_message": "Missing error handling in unknown", "file": "src/include/repl.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6678,7 +6678,7 @@ "timestamp": "2026-06-11T15:56:20.945Z", "error_message": "Missing error handling in unknown", "file": "src/controller/repl.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6695,7 +6695,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_commit_engine.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 10\u219215 lines (2 removed)", + "fix": "Rewrote 10→15 lines (2 removed)", "tags": [ "auto-detected", "refactor", @@ -6710,7 +6710,7 @@ "timestamp": "2026-06-11T15:57:43.900Z", "error_message": "Missing error handling in unknown", "file": "src/controller/testsuite/test_commit_engine.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6726,7 +6726,7 @@ "timestamp": "2026-06-11T16:15:06.660Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/ha-commit-engine/tasks.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6742,7 +6742,7 @@ "timestamp": "2026-06-11T16:19:30.370Z", "error_message": "Missing error handling in unknown", "file": "src/controller/ha.h", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6758,8 +6758,8 @@ "timestamp": "2026-06-11T16:19:51.195Z", "error_message": "Missing error handling in unknown", "file": "src/controller/testsuite/test_commit_engine.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", - "fix": "Added try/catch block | Also: /* Task 4.2 \u2014 wired gate: registry truth at ha_dispatch_gate; RUN_TEST(test_gate_wired_registry_catchup_standby_rejects_th", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", + "fix": "Added try/catch block | Also: /* Task 4.2 — wired gate: registry truth at ha_dispatch_gate; RUN_TEST(test_gate_wired_registry_catchup_standby_rejects_th", "tags": [ "auto-detected", "error-handling", @@ -6790,7 +6790,7 @@ "timestamp": "2026-06-11T16:23:59.453Z", "error_message": "Missing error handling in unknown", "file": "src/controller/ha.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6806,8 +6806,8 @@ "timestamp": "2026-06-11T22:39:24.704Z", "error_message": "Missing error handling in unknown", "file": "src/controller/testsuite/test_commit_engine.c", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", - "fix": "Added try/catch block | Also: /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550; * Task 5.1 \u2014 integration: ACK feed and the job-submit sync-a | Also: #include \"typemap.h\" /* wal_catchup_entry_t (task 5; #include \"wal_types.h\" /* WAL_JOB_SUBMIT (task 5.1 sy", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", + "fix": "Added try/catch block | Also: /* ═════════════════════════════════════════════════════════; * Task 5.1 — integration: ACK feed and the job-submit sync-a | Also: #include \"typemap.h\" /* wal_catchup_entry_t (task 5; #include \"wal_types.h\" /* WAL_JOB_SUBMIT (task 5.1 sy", "tags": [ "auto-detected", "error-handling", @@ -6855,7 +6855,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/repl.c", "root_cause": "5 lines replaced/restructured", - "fix": "Rewrote 13\u219227 lines (5 removed)", + "fix": "Rewrote 13→27 lines (5 removed)", "tags": [ "auto-detected", "refactor", @@ -6870,7 +6870,7 @@ "timestamp": "2026-06-11T23:32:42.365Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/ha-commit-engine/tasks.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -6919,7 +6919,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/persist.c", "root_cause": "16 lines replaced/restructured", - "fix": "Rewrote 40\u219267 lines (16 removed)", + "fix": "Rewrote 40→67 lines (16 removed)", "tags": [ "auto-detected", "refactor", @@ -6967,7 +6967,7 @@ "error_message": "Wrong reference: test_single_node_committed_lsn_unchanged should be test_single_node_committed_lsn_advances", "file": "src/controller/testsuite/test_p2_integration.c", "root_cause": "Used \"test_single_node_committed_lsn_unchanged\" instead of \"test_single_node_committed_lsn_advances\"", - "fix": "Changed test_single_node_committed_lsn_unchanged \u2192 test_single_node_committed_lsn_advances", + "fix": "Changed test_single_node_committed_lsn_unchanged → test_single_node_committed_lsn_advances", "tags": [ "auto-detected", "wrong-reference", @@ -7068,7 +7068,7 @@ "timestamp": "2026-06-12T04:13:06.326Z", "error_message": "Missing error handling in unknown", "file": ".kiro/specs/ha-commit-barrier-rollout/tasks.md", - "root_cause": "Code path had no error handling \u2014 exceptions would propagate uncaught", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", "fix": "Added try/catch block", "tags": [ "auto-detected", @@ -7101,7 +7101,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/persist.c", "root_cause": "4 lines replaced/restructured", - "fix": "Rewrote 7\u219212 lines (4 removed) | Also: return persist_wal_async(p, wal_type, &js, sizeof(; persist_t* p = cls; | Also: int rc = persist_wal_async(p, wal_type, pkt, (uint; persist_t* p = cls;", + "fix": "Rewrote 7→12 lines (4 removed) | Also: return persist_wal_async(p, wal_type, &js, sizeof(; persist_t* p = cls; | Also: int rc = persist_wal_async(p, wal_type, pkt, (uint; persist_t* p = cls;", "tags": [ "auto-detected", "refactor", @@ -7149,7 +7149,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_commit_barrier_reply.c", "root_cause": "2 lines replaced/restructured", - "fix": "Rewrote 3\u21925 lines (2 removed) | Also: static xce_signal_t g_sigkill = { .signo = SIGKIL; static xce_jobctrl_t g_jobctrl;", + "fix": "Rewrote 3→5 lines (2 removed) | Also: static xce_signal_t g_sigkill = { .signo = SIGKIL; static xce_jobctrl_t g_jobctrl;", "tags": [ "auto-detected", "refactor", @@ -7257,7 +7257,7 @@ "error_message": "Significant refactor of ", "file": "src/controller/testsuite/test_commit_barrier_reply.c", "root_cause": "7 lines replaced/restructured", - "fix": "Rewrote 12\u219256 lines (7 removed) | Also: xce_job_t jdef;; static int jobobj_ready; /* jobj module tables \u2014 o", + "fix": "Rewrote 12→56 lines (7 removed) | Also: xce_job_t jdef;; static int jobobj_ready; /* jobj module tables — o", "tags": [ "auto-detected", "refactor", @@ -7272,7 +7272,7 @@ "timestamp": "2026-06-12T11:05:00-07:00", "error_message": "va_list type confusion: WAL_JOB_RUSAGE hook payload reads job_t fields from an xce_job_rusages_t*", "file": "src/controller/job.c (caller) / src/controller/persist.c (consumer)", - "root_cause": "update_job_rusages fires AFTER_EVENT HOOK_DETAIL_JOB_RUSAGE_UPDATE with (cluster_t*, xce_job_rusages_t*) at job.c:1015-1016, but persist_job_hook_fun's default arm (persist.c:1354-1359) reads the second vararg as job_t* and dereferences j->job.state at offset 56 == sizeof(xce_job_rusages_t) \u2014 an out-of-bounds read on an exactly-sized allocation; the WAL_JOB_RUSAGE payload state field is garbage (jobid at offset 0 lines up by accident)", + "root_cause": "update_job_rusages fires AFTER_EVENT HOOK_DETAIL_JOB_RUSAGE_UPDATE with (cluster_t*, xce_job_rusages_t*) at job.c:1015-1016, but persist_job_hook_fun's default arm (persist.c:1354-1359) reads the second vararg as job_t* and dereferences j->job.state at offset 56 == sizeof(xce_job_rusages_t) — an out-of-bounds read on an exactly-sized allocation; the WAL_JOB_RUSAGE payload state field is garbage (jobid at offset 0 lines up by accident)", "fix": "NOT FIXED (discovered while writing task 3.3 receipt-ACK verdict pins; production out of boundary). Test test_update_op_rusage_receipt_reply_facts_retained uses a padded union so the pin stays in-bounds under ASAN without masking the bug; fix candidates: dedicated RUSAGE arm in persist_job_hook_fun reading xce_job_rusages_t*, or fire the hook with (c, j) like the other job hooks", "tags": [ "pre-existing", @@ -7306,6 +7306,259 @@ "related_bugs": [], "occurrences": 1, "last_seen": "2026-06-12T15:03:38.962Z" + }, + { + "id": "bug-453", + "timestamp": "2026-06-12T15:45:11.299Z", + "error_message": "Significant refactor of ", + "file": "src/controller/testsuite/test_repl_majority.c", + "root_cause": "3 lines replaced/restructured", + "fix": "Rewrote 3→10 lines (3 removed) | Also: * walAck half is engine-wired per-LSN; the configA; * byte-identical legacy behavior (config barrier o | Also: /* upgradeConfigAck with no pending ACKs → timeout; int rc_cfg = repl_wait_majority_ack(&g_mp.repl, 0,", + "tags": [ + "auto-detected", + "refactor", + "c" + ], + "related_bugs": [], + "occurrences": 3, + "last_seen": "2026-06-12T15:45:39.679Z" + }, + { + "id": "bug-454", + "timestamp": "2026-06-13T00:15:40.802Z", + "error_message": "Significant refactor of ", + "file": "src/controller/repl.c", + "root_cause": "5 lines replaced/restructured", + "fix": "Rewrote 6→11 lines (5 removed)", + "tags": [ + "auto-detected", + "refactor", + "c" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-06-13T00:15:40.802Z" + }, + { + "id": "bug-455", + "timestamp": "2026-06-13T00:16:42.277Z", + "error_message": "Significant refactor of ", + "file": "src/include/repl.h", + "root_cause": "4 lines replaced/restructured", + "fix": "Rewrote 4→7 lines (4 removed)", + "tags": [ + "auto-detected", + "refactor", + "h" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-06-13T00:16:42.277Z" + }, + { + "id": "bug-456", + "timestamp": "2026-06-13T00:17:27.276Z", + "error_message": "Significant refactor of ", + "file": "src/controller/config_repl.c", + "root_cause": "28 lines replaced/restructured", + "fix": "Rewrote 73→85 lines (28 removed) | Also: /* 7. Push to standbys (best-effort; see push_conf; int acked = push_config_to_standbys(c, out_bytes, | Also: * 7. Push to standbys (best-effort); * 8. ACK admin tool", + "tags": [ + "auto-detected", + "refactor", + "c" + ], + "related_bugs": [], + "occurrences": 3, + "last_seen": "2026-06-13T00:19:11.078Z" + }, + { + "id": "bug-457", + "timestamp": "2026-06-13T00:18:17.080Z", + "error_message": "Missing guard clause", + "file": "src/controller/config_repl.c", + "root_cause": "No early return/throw for edge case: !c->ha.standby_endpoints", + "fix": "Added guard clause: if (!c->ha.standby_endpoints)", + "tags": [ + "auto-detected", + "guard-clause", + "c" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-06-13T00:18:17.080Z" + }, + { + "id": "bug-458", + "timestamp": "2026-06-13T00:19:39.908Z", + "error_message": "Missing error handling in unknown", + "file": "src/controller/config_repl.h", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", + "fix": "Added try/catch block", + "tags": [ + "auto-detected", + "error-handling", + "h" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-06-13T00:19:39.908Z" + }, + { + "id": "bug-459", + "timestamp": "2026-06-13T00:30:00Z", + "error_message": "configAck quorum fabricated success: counted acked_config_lsn > 0 (any previous upgrade's ack), denominator n_active with no self-count, and n_active==0 short-circuited to success; upgradeConfig also ACKed the admin tool unconditionally and requested restart even when the majority wait timed out (logged-only result)", + "file": "src/controller/repl.c, src/controller/config_repl.c", + "root_cause": "legacy connectivity-based counting on the configAck track predated the commit engine's per-push arithmetic; push_config_to_standbys returned the transport push count and only logged the barrier outcome, so do_upgrade_config_with_bytes could not observe failure", + "fix": "task 5.2: configAck branch counts acked_config_lsn >= push_tag (entry_lsn carries the tag), majority 1+count > n_peers/2 over engine n_peers, n_peers<=1 immediate 0, ce==NULL -EINVAL fail-loud; push_config_to_standbys returns the barrier result (push count via acked_out); do_upgrade_config_with_bytes sends send_upgrade_error and skips success ACK + restart on non-zero; committed_config_tag advance stays success-branch-only", + "tags": [ + "ha", + "config-repl", + "quorum", + "configAck", + "commit-barrier", + "upgradeConfig" + ], + "related_bugs": [ + "bug-396" + ], + "occurrences": 1, + "last_seen": "2026-06-13T00:30:00Z" + }, + { + "id": "bug-460", + "timestamp": "2026-06-13T01:11:40.159Z", + "error_message": "Significant refactor of ", + "file": "src/controller/config_repl.c", + "root_cause": "7 lines replaced/restructured", + "fix": "Rewrote 14→19 lines (7 removed) | Also: cluster_t *c, const char *msg); reply.status = -EINVAL;", + "tags": [ + "auto-detected", + "refactor", + "c" + ], + "related_bugs": [], + "occurrences": 2, + "last_seen": "2026-06-13T01:14:42.432Z" + }, + { + "id": "bug-461", + "timestamp": "2026-06-13T01:11:49.673Z", + "error_message": "Wrong condition in logic", + "file": "src/controller/config_repl.c", + "root_cause": "Condition was: if (n == 0)", + "fix": "Changed to: if (n_out)", + "tags": [ + "auto-detected", + "logic-fix", + "c" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-06-13T01:11:49.673Z" + }, + { + "id": "bug-462", + "timestamp": "2026-06-13T01:12:50.297Z", + "error_message": "Significant refactor of ", + "file": "src/controller/config_repl.h", + "root_cause": "8 lines replaced/restructured", + "fix": "Rewrote 22→39 lines (8 removed)", + "tags": [ + "auto-detected", + "refactor", + "h" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-06-13T01:12:50.297Z" + }, + { + "id": "bug-463", + "timestamp": "2026-06-13T01:13:19.561Z", + "error_message": "Significant refactor of ", + "file": "src/controller/ha.c", + "root_cause": "6 lines replaced/restructured", + "fix": "Rewrote 10→18 lines (6 removed)", + "tags": [ + "auto-detected", + "refactor", + "c" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-06-13T01:13:19.561Z" + }, + { + "id": "bug-464", + "timestamp": "2026-06-13T01:14:15.668Z", + "error_message": "Missing error handling in unknown", + "file": "src/controller/join.c", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", + "fix": "Added try/catch block", + "tags": [ + "auto-detected", + "error-handling", + "c" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-06-13T01:14:15.668Z" + }, + { + "id": "bug-465", + "timestamp": "2026-06-13T01:30:00Z", + "error_message": "Config-ACK signal never produced in production: repl_on_config_ack had zero production callers; standby replconfigpushreplymsg silently dropped in repl_ack_decode_and_dispatch (ha.c) and myreq_handler default case (cluster.c) -> every multi-member config upgrade barrier timed out (upgrades bricked)", + "file": "src/controller/ha.c", + "root_cause": "ha-config-bootstrap task 8.1's production ingestion half was never landed; the 5.2 barrier relied on a signal nothing produced (tests injected repl_on_config_ack directly)", + "fix": "Added replconfigpushreplymsg decode branch in repl_ack_decode_and_dispatch (ha.c) and an OPCode_reply ingestion case in cluster.c myreq_handler, both calling repl_on_config_ack(&c->persist.repl, ep, versionTag); wire-path integration test added (test_config_ack_wire_path_completes_barrier)", + "tags": [ + "ha", + "config-repl", + "barrier", + "capnp", + "wire-path", + "task-5.2" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-06-13T01:30:00Z" + }, + { + "id": "bug-466", + "timestamp": "2026-06-13T01:30:00Z", + "error_message": "Unconditional 500ms config barrier wait in push_config_to_standbys reachable from iomgr epoll thread (ha.c raft_cb_become_leader, ha_dispatch_repl_action_for_standby, legacy election path; join.c controller join) -> blocks all controller I/O; the awaited ACK arrives on the blocked thread (structural deadlock)", + "file": "src/controller/config_repl.c", + "root_cause": "task 5.2 made the barrier unconditional inside push_config_to_standbys while best-effort catch-up/election pushes on iomgr-reachable paths still called the same entry", + "fix": "Split the API: exported transport-only fan-out push_config_transport_only (no barrier, iomgr-safe) and switched all 5 ha.c sites + join.c:187 to it; push_config_to_standbys (transport+barrier) kept for the vtask-context upgrade path only; doc comments updated in config_repl.h", + "tags": [ + "ha", + "iomgr", + "deadlock", + "config-repl", + "api-split", + "task-5.2" + ], + "related_bugs": [ + "bug-465" + ], + "occurrences": 1, + "last_seen": "2026-06-13T01:30:00Z" + }, + { + "id": "bug-467", + "timestamp": "2026-06-13T01:38:08.308Z", + "error_message": "Wrong condition in logic", + "file": "src/controller/ha.c", + "root_cause": "Condition was: if (rep_s.data_which == RequestReply_data_replconfigpu)", + "fix": "Changed to: if (0 /* REVIEWER RED CHECK */ && rep_s.data_which == )", + "tags": [ + "auto-detected", + "logic-fix", + "c" + ], + "related_bugs": [], + "occurrences": 2, + "last_seen": "2026-06-13T01:38:43.310Z" } ] } \ No newline at end of file diff --git a/.wolf/cerebrum.md b/.wolf/cerebrum.md index 287ca937..e2a44f2b 100644 --- a/.wolf/cerebrum.md +++ b/.wolf/cerebrum.md @@ -9,6 +9,7 @@ ## Key Learnings +- (2026-06-13, barrier-rollout 5.2) push_config_to_standbys now RETURNS the configAck barrier result (0/-ETIMEDOUT/-EINVAL), push count via acked_out; the configAck branch of repl_wait_majority_ack reads entry_lsn as the push tag and r->ce->n_peers (set-once, lock-free) as the denominator, ce==NULL -> -EINVAL fail-loud. ha.c/join.c streaming pushes discard the return BY DESIGN (log-only); only do_upgrade_config_with_bytes treats it as authoritative. - [2026-06-12] Census parsing caveat: controller_unit_test logs (tinylog) and Unity result lines BOTH go to stdout and can splice mid-line under the multi-threaded config-push tests — a distinct-Unity-line count silently drops spliced results. Robust census = union of Unity result names AND the `test_x: PASS` printf lines, cross-checked against the per-group `N Tests M Failures` summary blocks (which are splice-immune). - [2026-06-12] Config-barrier test trick: when a test needs a covering config ACK for a tag only known AFTER conf_save_config hashes the re-encoded blob (upgradeConfig path), pre-ack with acked_config_lsn = UINT64_MAX — covers any push tag under both legacy (>0) and per-push (>= tag) counting. diff --git a/.wolf/memory.md b/.wolf/memory.md index 20f16b36..65a12dbe 100644 --- a/.wolf/memory.md +++ b/.wolf/memory.md @@ -977,3 +977,37 @@ | 08:51 | Edited src/controller/testsuite/test_commit_barrier_rollout.c | 3→8 lines | ~109 | | 16:10 | Task 5.1 ha-commit-barrier-rollout: RED tests for configAck per-push arithmetic + upgrade failure path; behavior-preserving push_config_to_standbys signature change (+int *acked_out, return semantics unchanged) | src/controller/{config_repl.c,config_repl.h,ha.c,join.c}, testsuite/{test_commit_barrier_rollout.c,test_repl_majority.c} | 8 new cases (6 RED + 2 pins), isolated group 29 tests/6 fails, full suite: only those 6 fail (summary blocks), repl_majority green, prod controller builds | ~110k | | 09:31 | Edited .kiro/specs/ha-commit-barrier-rollout/tasks.md | inline fix | ~22 | +| 17:15 | Edited src/controller/repl.c | modified track() | ~202 | +| 17:15 | Edited src/controller/repl.c | added 2 condition(s) | ~323 | +| 17:16 | Edited src/controller/repl.c | quorum() → success() | ~305 | +| 17:16 | Edited src/include/repl.h | wired() → members() | ~127 | +| 17:17 | Edited src/controller/config_repl.c | modified if() | ~942 | +| 17:18 | Edited src/controller/config_repl.c | modified config_push_transport() | ~792 | +| 17:18 | Edited src/controller/config_repl.c | added 1 condition(s) | ~267 | +| 17:19 | Edited src/controller/config_repl.c | standbys() → tool() | ~73 | +| 17:19 | Edited src/controller/config_repl.h | modified acked_out() | ~312 | +| 00:30 | Task 5.2 ha-commit-barrier-rollout: configAck per-push arithmetic (acked_config_lsn>=tag, 1+count>n_peers/2, n_peers<=1 fast path, ce==NULL -EINVAL); push_config_to_standbys returns barrier result (transport split into config_push_transport helper); do_upgrade_config_with_bytes sends send_upgrade_error + skips ACK/restart on barrier failure | repl.c, repl.h, config_repl.c/.h | 29/29 group, 6/6 repl_majority, 46/46 config_repl, full suite 0 FAIL, sanitizers clean | ~60k | +| 01:00 | Adversarial review task 5.2 ha-commit-barrier-rollout: tests green (rollout 29/29, majority 6/6, config_repl 46/46, census 0 FAIL) but REJECTED — repl_on_config_ack has no production caller (config upgrades brick on n_peers>1) + push_config_to_standbys barrier now blocks iomgr thread at ha.c:570/1410 | src/controller/repl.c, config_repl.c, ha.c | REJECTED | ~60k | +| 18:03 | Edited src/controller/testsuite/test_commit_barrier_rollout.c | added 1 condition(s) | ~2006 | +| 18:04 | Edited src/controller/testsuite/test_commit_barrier_rollout.c | 3→6 lines | ~95 | +| 18:10 | Edited src/controller/ha.c | added 3 condition(s) | ~492 | +| 18:10 | Edited src/controller/ha.c | 5→8 lines | ~115 | +| 18:11 | Edited src/controller/cluster.c | added 2 condition(s) | ~441 | +| 18:11 | Edited src/controller/config_repl.c | added 1 condition(s) | ~256 | +| 18:11 | Edited src/controller/config_repl.c | added 1 condition(s) | ~35 | +| 18:12 | Edited src/controller/config_repl.h | modified n_out() | ~635 | +| 18:13 | Edited src/controller/ha.c | modified XCE_TESTING() | ~296 | +| 18:14 | Edited src/controller/join.c | push_config_to_standbys() → push_config_transport_only() | ~125 | +| 18:14 | Edited src/controller/config_repl.c | modified send_upgrade_error() | ~180 | +| 18:15 | Edited src/controller/config_repl.c | modified if() | ~110 | +| 18:15 | Edited src/controller/config_repl.c | modified ONLY() | ~172 | +| 01:35 | Task 5.2 remediation r1: wired production config-ACK ingestion (ha.c+cluster.c), split push_config_transport_only from barrier entry, switched 5 ha.c + 1 join.c sites, barrier rc into upgrade error reply; wire-path test added | ha.c, cluster.c, config_repl.c/.h, join.c, test_commit_barrier_rollout.c | rollout 30/30, repl_majority 6/6, config_repl 46/46, census 0 FAIL | ~95k | +| 18:38 | Edited src/controller/ha.c | inline fix | ~30 | +| 18:38 | Edited src/controller/ha.c | inline fix | ~22 | + +## Session: 2026-06-13 20:00 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| +| 20:45 | Edited .kiro/specs/ha-commit-barrier-rollout/tasks.md | inline fix | ~37 | +| 20:46 | Edited .kiro/specs/ha-commit-barrier-rollout/tasks.md | 1→2 lines | ~383 | diff --git a/src/controller/cluster.c b/src/controller/cluster.c index d913f729..f409c637 100644 --- a/src/controller/cluster.c +++ b/src/controller/cluster.c @@ -482,6 +482,35 @@ static int myreq_handler(vtask_t* task) } break; #endif + case OPCode_reply: + /* Config-push ACK arriving on an inbound standby endpoint (the + * join.c-registered standby_endpoints channel — these eps reply on + * the same connection the primary pushed on, so the ACK lands here + * instead of peer_ctrl_msg_handler). Feed it to the replicator so + * the configAck barrier can observe it (ha-commit-barrier-rollout + * task 5.2 remediation r1; same ingestion as + * repl_ack_decode_and_dispatch in ha.c — completes the unlanded + * half of ha-config-bootstrap task 8.1). */ + if (request.body_which == Request_body_replymsg + && request.body.replymsg + && request.body.replymsg->data_which + == RequestReply_data_replconfigpushreplymsg + && request.body.replymsg->data.replconfigpushreplymsg) { + repl_config_push_reply_t *cack = + request.body.replymsg->data.replconfigpushreplymsg; + if (cack->accepted) { + repl_on_config_ack(&c->persist.repl, ep, cack->version_tag); + TRACE("myreq_handler: config ACK from inbound standby" + " tag=%016" PRIx64, cack->version_tag); + } else { + WARN("myreq_handler: config push rejected by inbound standby" + " (tag=%016" PRIx64 ") — ignoring", cack->version_tag); + } + rc = 0; + break; + } + /* Any other reply body keeps the legacy unknown-opcode handling. */ + /* fall through */ default: rc = -E_XCE_BAD_OPCODE; ERROR("Unknown opcode: %d", request.opcode); diff --git a/src/controller/config_repl.c b/src/controller/config_repl.c index 8bdc5456..ffdcd8e4 100644 --- a/src/controller/config_repl.c +++ b/src/controller/config_repl.c @@ -754,15 +754,18 @@ int handle_repl_config_push(cluster_t *c, endpoint_t *ep, xce_request_t *req) /* ── send_upgrade_error ─────────────────────────── */ +/* status: the negative errno carried to the requester (task 5.2 r1, + * reviewer finding 3: validation failures keep -EINVAL; the barrier + * failure path forwards its own rc, e.g. -ETIMEDOUT). */ static void send_upgrade_error(endpoint_t *ep, xce_request_t *req, - cluster_t *c, const char *msg) + cluster_t *c, int status, const char *msg) { xce_reply_t reply; init_reply_by_req(&reply, req); - reply.status = -EINVAL; + reply.status = (status < 0) ? status : -EINVAL; reply.data_which = RequestReply_data_nodata; xce_reply(ep, c->myhost, &reply); - ERROR("OP_upgradeConfig rejected: %s", msg); + ERROR("OP_upgradeConfig rejected (status=%d): %s", reply.status, msg); } /* ── push_config_to_standbys ─────────────────────── */ @@ -863,21 +866,32 @@ int wait_all_with_timeout_for_test(pthread_t *threads, push_ctx_t *ctxs, } #endif -int push_config_to_standbys(cluster_t *c, const uint8_t *bytes, size_t blen, - const uint8_t sha256[32], uint64_t new_tag, - int *acked_out) +/* push_config_transport_only: transport fan-out half of + * push_config_to_standbys (exported — task 5.2 remediation r1): spawn one + * push thread per registered standby endpoint and wait for the sends. + * Best-effort streaming, NO barrier wait — the sends are non-blocking + * endpoint_enq()s, so this is safe on iomgr-reachable paths (election + * promotion, heartbeat-driven convergence, controller join). Alloc or + * thread failures degrade to fewer pushes, never to a fabricated quorum + * (the barrier wait in push_config_to_standbys owns the quorum verdict). + * Stores the registered standby count in *n_out (may be NULL); returns + * the number of standbys that ACKed the transport push. */ +int push_config_transport_only(cluster_t *c, const uint8_t *bytes, + size_t blen, const uint8_t sha256[32], + uint64_t new_tag, int *n_out) { - /* acked_out: per-standby transport push count (task 5.1 signature; - * behavior-preserving — the return value keeps the legacy push-count - * semantics until task 5.2 re-plumbs it to the barrier result). */ - if (acked_out) - *acked_out = 0; + int n; + int acked; + if (n_out) + *n_out = 0; if (!c->ha.standby_endpoints) return 0; pthread_mutex_lock(&c->ha.lock); - int n = c->ha.standby_endpoints->count; + n = c->ha.standby_endpoints->count; + if (n_out) + *n_out = n; if (n == 0) { pthread_mutex_unlock(&c->ha.lock); return 0; @@ -885,6 +899,8 @@ int push_config_to_standbys(cluster_t *c, const uint8_t *bytes, size_t blen, push_ctx_t *ctxs = calloc((size_t)n, sizeof(push_ctx_t)); if (!ctxs) { pthread_mutex_unlock(&c->ha.lock); + WARN("push_config_transport_only: ctx alloc failed (n=%d)" + " — skipping transport push", n); return 0; } for (int i = 0; i < n; i++) { @@ -900,32 +916,54 @@ int push_config_to_standbys(cluster_t *c, const uint8_t *bytes, size_t blen, pthread_t *threads = malloc((size_t)n * sizeof(pthread_t)); if (!threads) { free(ctxs); + WARN("push_config_transport_only: thread alloc failed (n=%d)" + " — skipping transport push", n); return 0; } for (int i = 0; i < n; i++) { ctxs[i].started = (pthread_create(&threads[i], NULL, push_thread, &ctxs[i]) == 0); if (!ctxs[i].started) - WARN("push_config_to_standbys: pthread_create failed for standby %d", i); + WARN("push_config_transport_only: pthread_create failed for standby %d", i); } atomic_int push_done; atomic_init(&push_done, 0); - int acked = wait_all_with_timeout(threads, ctxs, n, - CONF_PUSH_TIMEOUT_MS, &push_done); + acked = wait_all_with_timeout(threads, ctxs, n, + CONF_PUSH_TIMEOUT_MS, &push_done); free(ctxs); free(threads); + return acked; +} + +int push_config_to_standbys(cluster_t *c, const uint8_t *bytes, size_t blen, + const uint8_t sha256[32], uint64_t new_tag, + int *acked_out) +{ + /* Returns the majority-ACK barrier result (0 / -ETIMEDOUT / -EINVAL); + * the per-standby transport push count travels via acked_out (task 5.2 + * re-plumb). The transport fan-out is best-effort — push failures and + * an empty standby set fall through to the barrier wait, which owns + * the quorum policy (n_peers <= 1 → immediate success; multi-member + * with no covering acks → -ETIMEDOUT; no n_active shortcut). + * VTASK-CONTEXT ONLY (task 5.2 remediation r1): the barrier blocks up + * to CONF_PUSH_TIMEOUT_MS — iomgr-reachable callers must use + * push_config_transport_only instead (see config_repl.h). */ + int n = 0; + int acked = push_config_transport_only(c, bytes, blen, sha256, new_tag, &n); if (acked_out) *acked_out = acked; - /* Req 8.1–8.3: wait for majority-quorum OP_upgradeConfigAck. - * On quorum: advance committed_config_tag atomically (Req 8.5). */ - /* entry_lsn = 0: the configAck branch ignores it (walAck-only adapter - * parameter, ha-commit-engine task 5.1). */ - int rc = repl_wait_majority_ack(&c->persist.repl, 0, CONF_PUSH_TIMEOUT_MS, + /* Req 8.1–8.3 / barrier-rollout Req 4.1-4.3: wait for majority-quorum + * OP_upgradeConfigAck, passing the in-flight push tag for per-push + * evaluation (entry_lsn carries the tag — task 5.2). On quorum: + * advance committed_config_tag atomically (Req 8.5 / 4.3 — the + * advance stays inside this success branch only). */ + int rc = repl_wait_majority_ack(&c->persist.repl, new_tag, + CONF_PUSH_TIMEOUT_MS, (uint8_t)OPCode_upgradeConfigAck); if (rc == 0) { atomic_store_explicit(&c->ha.committed_config_tag, new_tag, @@ -933,12 +971,11 @@ int push_config_to_standbys(cluster_t *c, const uint8_t *bytes, size_t blen, INFO("Config committed on majority (tag=%016" PRIx64 ", pushed=%d/%d standbys)", new_tag, acked, n); } else { - WARN("Config push: timeout waiting for majority ACK" - " (tag=%016" PRIx64 ", pushed=%d/%d standbys)", - new_tag, acked, n); + WARN("Config push: no majority ACK (rc=%d, tag=%016" PRIx64 + ", pushed=%d/%d standbys)", rc, new_tag, acked, n); } - return acked; + return rc; } /* ── handle_upgrade_config ──────────────────────── */ @@ -956,8 +993,9 @@ int push_config_to_standbys(cluster_t *c, const uint8_t *bytes, size_t blen, * 4. Increment generation, re-encode with updated counter * 5. Save to disk atomically * 6. Update in-memory generation - * 7. Push to standbys (best-effort) - * 8. ACK admin tool + * 7. Push to standbys and wait for the majority-ACK barrier; on failure + * reply with send_upgrade_error and return (no success ACK, no restart) + * 8. ACK admin tool (success ⇔ majority-acked config) * 9. Wait for in-flight checkpoint, then signal restart */ static int do_upgrade_config_with_bytes(cluster_t *c, endpoint_t *ep, @@ -968,11 +1006,11 @@ static int do_upgrade_config_with_bytes(cluster_t *c, endpoint_t *ep, { /* 0. Validate size */ if (in_len == 0 || in_bytes == NULL || in_sha == NULL) { - send_upgrade_error(ep, req, c, "Missing bytes or sha256 field"); + send_upgrade_error(ep, req, c, -EINVAL, "Missing bytes or sha256 field"); return -EINVAL; } if (in_len > CONF_MAX_SIZE) { - send_upgrade_error(ep, req, c, "Config blob exceeds CONF_MAX_SIZE"); + send_upgrade_error(ep, req, c, -EINVAL, "Config blob exceeds CONF_MAX_SIZE"); return -EINVAL; } @@ -980,7 +1018,7 @@ static int do_upgrade_config_with_bytes(cluster_t *c, endpoint_t *ep, uint8_t computed[32]; SHA256(in_bytes, in_len, computed); if (memcmp(computed, in_sha, 32) != 0) { - send_upgrade_error(ep, req, c, + send_upgrade_error(ep, req, c, -EINVAL, "SHA-256 mismatch -- blob corrupted in transit"); return -EINVAL; } @@ -994,13 +1032,13 @@ static int do_upgrade_config_with_bytes(cluster_t *c, endpoint_t *ep, int fd = open(tmp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { - send_upgrade_error(ep, req, c, "Failed to create temp decode file"); + send_upgrade_error(ep, req, c, -EINVAL, "Failed to create temp decode file"); return -errno; } if (write(fd, in_bytes, in_len) != (ssize_t)in_len) { close(fd); unlink(tmp_path); - send_upgrade_error(ep, req, c, "Failed to write temp decode file"); + send_upgrade_error(ep, req, c, -EINVAL, "Failed to write temp decode file"); return -EIO; } close(fd); @@ -1014,7 +1052,7 @@ static int do_upgrade_config_with_bytes(cluster_t *c, endpoint_t *ep, free_ClusterConfig(new_cfg); free(new_cfg); } - send_upgrade_error(ep, req, c, "Failed to parse Cap'n Proto blob"); + send_upgrade_error(ep, req, c, -EINVAL, "Failed to parse Cap'n Proto blob"); return -EINVAL; } @@ -1022,7 +1060,7 @@ static int do_upgrade_config_with_bytes(cluster_t *c, endpoint_t *ep, if (config_contains_reserved_name(new_cfg)) { free_ClusterConfig(new_cfg); free(new_cfg); - send_upgrade_error(ep, req, c, + send_upgrade_error(ep, req, c, -EINVAL, "Config contains reserved name 'lost_and_found'"); return -EINVAL; } @@ -1046,7 +1084,7 @@ static int do_upgrade_config_with_bytes(cluster_t *c, endpoint_t *ep, uint8_t *out_bytes = (uint8_t *)malloc(out_len); if (!out_bytes) { capn_free(&out_cap); - send_upgrade_error(ep, req, c, "OOM re-encoding config"); + send_upgrade_error(ep, req, c, -EINVAL, "OOM re-encoding config"); return -ENOMEM; } capn_write_mem(&out_cap, out_bytes, out_len, 0); @@ -1058,19 +1096,37 @@ static int do_upgrade_config_with_bytes(cluster_t *c, endpoint_t *ep, rc = conf_save_config(c->myconf, out_bytes, out_len, computed); if (rc != 0) { free(out_bytes); - send_upgrade_error(ep, req, c, "Failed to save new config to disk"); + send_upgrade_error(ep, req, c, -EINVAL, "Failed to save new config to disk"); return rc; } /* 6. Update in-memory generation */ c->myconf->config_generation = new_gen; - /* 7. Push to standbys (best-effort; see push_config_to_standbys comment) */ - int acked = push_config_to_standbys(c, out_bytes, out_len, computed, - c->myconf->config_version_tag, NULL); + /* 7. Push to standbys — the majority-ACK barrier result is + * authoritative for the upgrade outcome (barrier-rollout Req 4.1/4.2): + * non-zero means the new tag never reached quorum, so the requester + * gets an explicit error and the cluster stays on the old committed + * tag (no success ACK, no restart — Req 4.3/4.4). Disk save and + * generation bump have already happened (pre-existing sequence); + * convergence of a half-pushed config remains the OP_replConfig + * anti-entropy behavior. */ + int acked = 0; + rc = push_config_to_standbys(c, out_bytes, out_len, computed, + c->myconf->config_version_tag, &acked); free(out_bytes); - /* 8. ACK the admin tool */ + if (rc != 0) { + /* Forward the barrier rc (-ETIMEDOUT / -EINVAL) as the reply + * status so the admin tool sees WHY the commit failed (task 5.2 + * r1, reviewer finding 3). */ + send_upgrade_error(ep, req, c, rc, + "Config push failed to reach majority quorum" + " -- upgrade not committed, no restart"); + return rc; + } + + /* 8. ACK the admin tool (success ⇔ majority-acked config) */ upgrade_config_reply_t urep; memset(&urep, 0, sizeof(urep)); urep.version_tag = c->myconf->config_version_tag; @@ -1118,12 +1174,12 @@ int handle_upgrade_config(cluster_t *c, endpoint_t *ep, xce_request_t *req) int rc = xce_upgrade_config_req_extract(req, &in_bytes, &in_len, &in_sha); if (rc != 0 || !in_bytes || !in_sha) { - send_upgrade_error(ep, req, c, + send_upgrade_error(ep, req, c, -EINVAL, "Failed to extract bytes from UpgradeConfigRequest"); return rc != 0 ? rc : -EINVAL; } if (in_len == 0) { - send_upgrade_error(ep, req, c, "bytes or sha256 field missing"); + send_upgrade_error(ep, req, c, -EINVAL, "bytes or sha256 field missing"); return -EINVAL; } diff --git a/src/controller/config_repl.h b/src/controller/config_repl.h index 6f850159..42342e83 100644 --- a/src/controller/config_repl.h +++ b/src/controller/config_repl.h @@ -109,14 +109,38 @@ int handle_config_request(cluster_t *c, endpoint_t *ep, xce_request_t *req); int handle_repl_config_push(cluster_t *c, endpoint_t *ep, xce_request_t *req); /* - * push_config_to_standbys: fan-out a new config blob to all connected standby - * controllers in parallel. Best-effort — standbys that miss the push will - * pull on reconnect via handle_config_request(). + * push_config_transport_only: best-effort streaming fan-out of a config blob + * to all connected standby endpoints — NO barrier wait (ha-commit-barrier- + * rollout task 5.2 remediation r1). Spawns one push thread per registered + * standby; the sends are non-blocking endpoint_enq()s, so the helper is + * iomgr-safe and is the ONLY config-push entry callers reachable from + * peer_ctrl_msg_handler / io_worker may use (election promotion, heartbeat- + * driven convergence in ha.c, controller join in join.c). Standbys that + * miss the push converge later via the OP_replConfig anti-entropy pull and + * the heartbeat decision loop. + * n_out (may be NULL): receives the registered standby count. + * Returns the number of standbys that ACKed the push at the transport level. + */ +int push_config_transport_only(cluster_t *c, const uint8_t *bytes, + size_t blen, const uint8_t sha256[32], + uint64_t new_tag, int *n_out); + +/* + * push_config_to_standbys: transport fan-out (push_config_transport_only) + * PLUS the majority-quorum config ACK barrier on new_tag (ha-commit-barrier- + * rollout task 5.2). The barrier blocks up to CONF_PUSH_TIMEOUT_MS — this + * entry is for VTASK-CONTEXT callers ONLY (the OP_upgradeConfig path, + * do_upgrade_config_with_bytes); it must never run on the iomgr epoll + * thread (no path reachable from peer_ctrl_msg_handler / io_worker — those + * callers use push_config_transport_only above). * acked_out (may be NULL): receives the number of standbys that successfully * received the push at the transport level (per-standby push count). - * Returns the number of standbys that successfully received the push. - * (ha-commit-barrier-rollout task 5.2 moves the majority-ACK barrier result - * into the return value; the push count is then only available via acked_out.) + * Returns the barrier result: 0 once 1 (self) + standbys with + * acked_config_lsn >= new_tag form a majority of configured members + * (n_peers <= 1 → immediate 0); -ETIMEDOUT when the quorum never forms + * within CONF_PUSH_TIMEOUT_MS; -EINVAL if the commit engine is not wired. + * On 0 committed_config_tag has been advanced to new_tag; on failure it is + * untouched. The upgrade path treats the result as authoritative. * Only meaningful when CONF_ENABLE_UPGRADE_CONFIG is set, but always declared * so callers can reference it without an #if guard. */ diff --git a/src/controller/ha.c b/src/controller/ha.c index 4912f862..2f772e93 100644 --- a/src/controller/ha.c +++ b/src/controller/ha.c @@ -68,15 +68,23 @@ typedef struct { cluster_t *c; } committing_timeout_ctx_t; -/* cluster_init_phase2 / push_config_to_standbys / ha_send_ctrlhello_to_all_standbys: - * defined in cluster.c / config_repl.c; only referenced in production builds. - * (XCE_TESTING uses hook shims and does not link cluster.c.) */ +/* cluster_init_phase2 / push_config_transport_only / + * ha_send_ctrlhello_to_all_standbys: defined in cluster.c / config_repl.c; + * only referenced in production builds. + * (XCE_TESTING uses hook shims and does not link cluster.c.) + * ha.c uses push_config_transport_only ONLY (task 5.2 remediation r1): + * every ha.c push site is reachable from peer_ctrl_msg_handler / io_worker + * (vote replies, heartbeat replies), where the push_config_to_standbys + * barrier wait would block the single iomgr epoll thread — and the config + * ACK it waits for would arrive on that same blocked thread (structural + * deadlock). Convergence is owned by the OP_replConfig anti-entropy pull + * and the heartbeat decision loop, not by these best-effort pushes. */ #ifndef XCE_TESTING extern int cluster_init_phase2(cluster_t *c); extern void ha_send_ctrlhello_to_all_standbys(cluster_t *c); -extern int push_config_to_standbys(cluster_t *c, const uint8_t *bytes, size_t blen, - const uint8_t sha256[32], uint64_t new_tag, - int *acked_out); +extern int push_config_transport_only(cluster_t *c, const uint8_t *bytes, + size_t blen, const uint8_t sha256[32], + uint64_t new_tag, int *n_out); #endif static void on_vote_connected(endpoint_t *ep); @@ -567,12 +575,12 @@ static void raft_cb_become_leader(void *ctx, uint64_t term) extern int conf_read_raw(const char *, uint8_t **, size_t *); if (c->myconf && c->myconf->conffile && conf_read_raw(c->myconf->conffile, &cfg_bytes, &cfg_len) == 0) { - push_config_to_standbys(c, cfg_bytes, cfg_len, + push_config_transport_only(c, cfg_bytes, cfg_len, c->myconf->config_sha256, c->myconf->config_version_tag, NULL); free(cfg_bytes); } else { - push_config_to_standbys(c, NULL, 0, NULL, + push_config_transport_only(c, NULL, 0, NULL, c->myconf ? c->myconf->config_version_tag : 0, NULL); } @@ -1362,7 +1370,7 @@ void ha_check_majority_convergence(ha_state_t *ha, cluster_t *c) * (stored by ha_process_standby_state on the same heartbeat reply), calls * ha_evaluate_repl_action, and acts on the result: * - * HA_REPL_PUSH_CONFIG → push_config_to_standbys(c, …) [or test hook] (Task 3.3) + * HA_REPL_PUSH_CONFIG → push_config_transport_only(c, …) [or test hook] (Task 3.3) * HA_REPL_SEND_SNAPSHOT→ stub (Task 3.5) * HA_REPL_STREAM_WAL → ha_stream_wal_to_standby(c, ha, idx) (Task 3.4) * HA_REPL_CONVERGED → no-op @@ -1407,12 +1415,12 @@ int ha_dispatch_repl_action_for_standby(ha_state_t *ha, size_t cfg_len = 0; if (c->myconf && c->myconf->conffile && conf_read_raw(c->myconf->conffile, &cfg_bytes, &cfg_len) == 0) { - push_config_to_standbys(c, cfg_bytes, cfg_len, + push_config_transport_only(c, cfg_bytes, cfg_len, c->myconf->config_sha256, c->myconf->config_version_tag, NULL); free(cfg_bytes); } else { - push_config_to_standbys(c, NULL, 0, NULL, + push_config_transport_only(c, NULL, 0, NULL, c->myconf ? c->myconf->config_version_tag : 0, NULL); } @@ -2078,7 +2086,7 @@ int handle_request_vote(ha_state_t *ha, /* ── Test-only hook shims (XCE_TESTING only) ────────────────────────────── * Type definitions (election_timeout_ctx_t, committing_timeout_ctx_t), - * extern declarations (cluster_init_phase2, push_config_to_standbys, + * extern declarations (cluster_init_phase2, push_config_transport_only, * ha_send_ctrlhello_to_all_standbys), and static forward declarations * (committing_timeout_fn, ha_heartbeat_is_fresh) have been moved to the * forward-declarations block near the top of this file so that the raft @@ -2101,9 +2109,9 @@ int (*ha_phase2_hook)(void *c) = NULL; int (*ha_send_ctrlhello_hook)(void *c) = NULL; /* - * ha_push_config_hook: test-only seam for push_config_to_standbys. + * ha_push_config_hook: test-only seam for push_config_transport_only. * When non-NULL, the COMMITTING path calls this INSTEAD OF the real function - * (config_repl.c's push_config_to_standbys needs a real cluster). Req 7.1. + * (config_repl.c's push_config_transport_only needs a real cluster). Req 7.1. */ int (*ha_push_config_hook)(void *c) = NULL; @@ -2118,7 +2126,7 @@ int (*ha_nop_req_hook)(endpoint_t *ep) = NULL; * ha_repl_push_config_for_standby_hook: test-only seam for the per-standby * config push triggered by the heartbeat-reply decision loop (Task 3.3). * When non-NULL, ha_dispatch_repl_action_for_standby() calls this INSTEAD OF - * push_config_to_standbys() when the decision is HA_REPL_PUSH_CONFIG. + * push_config_transport_only() when the decision is HA_REPL_PUSH_CONFIG. * Requirements: 1.4 (ha-repl-hardening) */ int (*ha_repl_push_config_for_standby_hook)(void *c, int peer_idx) = NULL; @@ -2283,7 +2291,7 @@ apply_role: ha_push_config_hook(c); #else if (c != NULL) - push_config_to_standbys(c, NULL, 0, NULL, 0, NULL); + push_config_transport_only(c, NULL, 0, NULL, 0, NULL); #endif /* Send CtrlHelloRequest to all standbys to trigger WAL catch-up (Req 7.2) */ @@ -2649,7 +2657,10 @@ done: * * - OPCode_replCatchUpDone → ha_repl_ack_received * - OPCode_reply with stateReportMsg → ha_process_standby_state (Task 3.1) - * - OPCode_nop or OPCode_reply without stateReport → silently ignored + * - OPCode_reply with walackreplymsg → repl_on_ack (Task 4.1) + * - OPCode_reply with replconfigpushreplymsg → repl_on_config_ack + * (barrier-rollout task 5.2 r1 / ha-config-bootstrap task 8.1) + * - OPCode_nop or OPCode_reply with any other body → silently ignored * - anything else → WARN + drop */ static int repl_ack_decode_and_dispatch(endpoint_t *ep, vote_ep_ctx_t *vctx) @@ -2775,6 +2786,32 @@ static int repl_ack_decode_and_dispatch(endpoint_t *ep, vote_ep_ctx_t *vctx) WARN("repl_ack_decode_and_dispatch: WAL NACK from standby" " lsn=%lu success=0 — ignoring", (unsigned long)acked_lsn); } + } else if (rep_s.data_which == RequestReply_data_replconfigpushreplymsg) { + /* Config-push ACK from standby (ha-commit-barrier-rollout task + * 5.2 remediation r1; completes the unlanded half of + * ha-config-bootstrap task 8.1): the standby replies to + * OP_replConfigPush with replconfigpushreplymsg carrying + * {accepted, versionTag} (config_repl.c:744-752). Decode the + * acked tag and apply it to the replicator so the configAck + * barrier (repl_wait_majority_ack) can observe the covering + * ACK — without this branch every multi-member config upgrade + * would time out. read_ReplConfigPushReply reads scalars only + * — safe to call before capn_free. */ + struct ReplConfigPushReply rcp_raw; + memset(&rcp_raw, 0, sizeof(rcp_raw)); + read_ReplConfigPushReply(&rcp_raw, rep_s.data.replconfigpushreplymsg); + uint64_t acked_tag = (uint64_t)rcp_raw.versionTag; + int accepted = rcp_raw.accepted ? 1 : 0; + capn_free(&capn_ctx); + + if (accepted && c) { + repl_on_config_ack(&c->persist.repl, ep, acked_tag); + TRACE("repl_ack_decode_and_dispatch: config ACK from standby" + " tag=%016" PRIx64 " accepted=1", acked_tag); + } else if (!accepted) { + WARN("repl_ack_decode_and_dispatch: config push rejected by" + " standby (tag=%016" PRIx64 ") — ignoring", acked_tag); + } } else { capn_free(&capn_ctx); } diff --git a/src/controller/join.c b/src/controller/join.c index ee08f7cb..6c83b541 100644 --- a/src/controller/join.c +++ b/src/controller/join.c @@ -182,9 +182,12 @@ static int handle_controller_join(cluster_t* c, endpoint_t* ep, c->ha.standby_endpoints->count); /* Immediately push current config to the newly-joined standby so it - * doesn't have to wait for the next scheduled push. */ + * doesn't have to wait for the next scheduled push. Transport-only + * (no barrier wait — task 5.2 remediation r1): this is a best-effort + * catch-up stream; convergence is owned by the OP_replConfig + * anti-entropy pull and the heartbeat decision loop. */ if (ha_is_primary(&c->ha)) - push_config_to_standbys(c, NULL, 0, NULL, 0, NULL); + push_config_transport_only(c, NULL, 0, NULL, 0, NULL); } return 0; diff --git a/src/controller/repl.c b/src/controller/repl.c index 898e2ef5..06352d77 100644 --- a/src/controller/repl.c +++ b/src/controller/repl.c @@ -407,11 +407,16 @@ void repl_on_config_ack(replicator_t *r, repl_ep_t ep, uint64_t acked_config_lsn * anything ever" counting with self-count and the zero-active * success short-circuit is retired). Returns the engine code * unmodified: 0 / -ETIMEDOUT / -XCE_ERR_NOT_READY / -EINVAL. - * configAck: legacy standby counting (hardened by barrier-rollout task 5): - * quorum = (n_active + 2) / 2 over active (non-catch-up) - * standbys; acked_total = standbys with acked_config_lsn > 0, - * NO self-count (config must be confirmed by standbys). - * Returns 0 on quorum, -ETIMEDOUT on timeout. + * configAck: per-push arithmetic mirroring the engine's rules on the + * config track (barrier-rollout task 5.2, design §Config + * Barrier): entry_lsn carries the in-flight config push tag; + * count = active (non-catch-up) standbys with + * acked_config_lsn >= push_tag (a previous upgrade's ack no + * longer counts); majority = 1 (self) + count > n_peers / 2 + * over configured members (engine n_peers, set-once at + * wiring); n_peers <= 1 → immediate success. Returns 0 on + * quorum, -ETIMEDOUT on timeout, -EINVAL when the engine is + * not wired (fail-loud, mirroring the walAck branch). * ============================================================ */ int repl_wait_majority_ack(replicator_t *r, uint64_t entry_lsn, @@ -441,7 +446,33 @@ int repl_wait_majority_ack(replicator_t *r, uint64_t entry_lsn, return commit_submit(r->ce, entry_lsn, COMMIT_MODE_WAIT, timeout_ms); } - /* ── configAck track (legacy standby counting; task 5 hardens it) ── */ + /* ── configAck track (per-push arithmetic; barrier-rollout task 5.2) ── + * entry_lsn carries the in-flight config push tag (repl.h: the branch + * reuses the walAck adapter parameter — no second parameter). */ + + uint64_t push_tag = entry_lsn; + int n_peers; + + if (r->ce == NULL) { + /* The engine is wired at cluster_init_phase2 before any config + * push reaches this wait; an unwired engine is a harness defect, + * not a quorum — fail loud rather than fabricate a result + * (mirrors the walAck branch's commit_submit -EINVAL). */ + WARN("repl_wait_majority_ack: configAck wait without a wired" + " commit engine (push_tag=%016" PRIx64 ")", push_tag); + return -EINVAL; + } + + /* ce and ce->n_peers are set-once before replication traffic + * (repl_set_commit_engine contract) — safe to read without locks. */ + n_peers = r->ce->n_peers; + + if (n_peers <= 1) { + /* Single-member cluster: the local save is the quorum. */ + TRACE("repl_wait_majority_ack: configAck self-quorum" + " (n_peers=%d, push_tag=%016" PRIx64 ")", n_peers, push_tag); + return 0; + } struct timespec deadline; clock_gettime(CLOCK_REALTIME, &deadline); @@ -458,8 +489,7 @@ int repl_wait_majority_ack(replicator_t *r, uint64_t entry_lsn, while (1) { mylock_lock(&r->lock); - int n_active = 0; - int acked_count = 0; + int covered = 0; for (int i = 0; i < MAX_STANDBYS; i++) { standby_conn_t *s = &r->standbys[i]; @@ -467,26 +497,18 @@ int repl_wait_majority_ack(replicator_t *r, uint64_t entry_lsn, continue; if (atomic_load_explicit(&s->in_catch_up, memory_order_acquire)) continue; - n_active++; if (atomic_load_explicit(&s->acked_config_lsn, - memory_order_acquire) > 0) - acked_count++; + memory_order_acquire) >= push_tag) + covered++; } mylock_unlock(&r->lock); - /* Single-node shortcircuit: no active standbys → local write is - * quorum (configAck only, Req 8.5 — walAck never reaches this - * loop; its single-member case is the engine's self-quorum). */ - if (n_active == 0) { - rc = 0; - break; - } - - int quorum = (n_active + 2) / 2; - int acked_total = acked_count; /* no self-count for configAck */ - - if (acked_total >= quorum) { + /* Majority over configured members, self counted: the primary's + * own save plus standbys covering the in-flight push tag. No + * n_active shortcut — a multi-member cluster with zero covering + * acks must time out, never fabricate success (Req 4.2). */ + if (1 + covered > n_peers / 2) { rc = 0; break; } diff --git a/src/controller/testsuite/test_commit_barrier_rollout.c b/src/controller/testsuite/test_commit_barrier_rollout.c index 784f6711..4a754638 100644 --- a/src/controller/testsuite/test_commit_barrier_rollout.c +++ b/src/controller/testsuite/test_commit_barrier_rollout.c @@ -1492,6 +1492,175 @@ static void test_upgrade_config_majority_ack_success_unchanged(void) printf("test_upgrade_config_majority_ack_success_unchanged: PASS\n"); } +/* ════════════════════════════════════════════════════════════════════════ + * Task 5.2 remediation r1 — wire-path config ACK ingestion (Req 4.1, 4.2). + * + * The barrier consumes acked_config_lsn, which only repl_on_config_ack + * advances — so the standby's OP_replConfigPush reply + * (replconfigpushreplymsg, config_repl.c:744-752) must be decoded on the + * primary and fed to repl_on_config_ack. This case drives the ACK over + * the WIRE: it builds the standby's capnp reply bytes and pushes them + * through peer_ctrl_msg_handler → repl_ack_decode_and_dispatch (the + * production decode entry for promoted peer endpoints), asserting the + * decoded tag lands in the replicator slot and completes a pending + * barrier. This also completes the unlanded half of ha-config-bootstrap + * task 8.1 (production ingestion of the config-push ACK), which until + * now was only exercised by direct repl_on_config_ack injection. + * ════════════════════════════════════════════════════════════════════════ */ + +/* ha.c test seams (test_ha_connection_reuse.c precedent) */ +extern int peer_ctrl_msg_handler_for_test(endpoint_t *ep, io_packet_t *pkt); +extern endpoint_t *vote_ep_ctx_build_for_test(endpoint_t *ep, + ha_state_t *ha, + ha_elect_ctx_t *ctx, + int peer_idx, + uint64_t my_term, + uint64_t my_tag, + uint64_t my_lsn, + const char *my_name); +extern void vote_ep_ctx_set_cluster_for_test(endpoint_t *ep, cluster_t *c); + +/* Build the standby's config-push ACK exactly as xce_reply() puts it on + * the wire (config_repl.c:744-752): Request{opcode=reply, + * body.replymsg=RequestReply{data.replconfigpushreplymsg= + * ReplConfigPushReply{accepted, versionTag}}}. The handler takes + * ownership (free_packet + free). */ +static io_packet_t *cb5_build_config_ack_pkt(int accepted, uint64_t tag) +{ + struct capn cs; + struct capn_segment *seg; + struct ReplConfigPushReply ack_capn; + ReplConfigPushReply_ptr ack_ptr; + struct RequestReply rr_capn; + RequestReply_ptr rr_ptr; + struct Request rq_capn; + Request_ptr rq_ptr; + io_packet_t *pkt; + + capn_init_malloc(&cs); + seg = capn_root(&cs).seg; + + memset(&ack_capn, 0, sizeof(ack_capn)); + ack_capn.accepted = accepted ? 1 : 0; + ack_capn.versionTag = tag; + ack_ptr = new_ReplConfigPushReply(seg); + write_ReplConfigPushReply(&ack_capn, ack_ptr); + + memset(&rr_capn, 0, sizeof(rr_capn)); + rr_capn.seq = 1; + rr_capn.opcode = OPCode_replConfigPush; + rr_capn.status = 0; + rr_capn.data_which = RequestReply_data_replconfigpushreplymsg; + rr_capn.data.replconfigpushreplymsg = ack_ptr; + rr_ptr = new_RequestReply(seg); + write_RequestReply(&rr_capn, rr_ptr); + + memset(&rq_capn, 0, sizeof(rq_capn)); + rq_capn.opcode = OPCode_reply; + rq_capn.seq = 1; + rq_capn.body_which = Request_body_replymsg; + rq_capn.body.replymsg = rr_ptr; + rq_ptr = new_Request(seg); + write_Request(&rq_capn, rq_ptr); + capn_setp(capn_root(&cs), 0, rq_ptr.p); + + pkt = calloc(1, sizeof(*pkt)); + TEST_ASSERT_NOT_NULL_MESSAGE(pkt, "config ACK pkt alloc"); + pkt->sz = capn_size(&cs); + TEST_ASSERT_TRUE_MESSAGE(alloc_packet(pkt) >= 0, "config ACK pkt data alloc"); + capn_write_mem(&cs, (uint8_t *)pkt->data, (size_t)pkt->sz, 0); + capn_free(&cs); + + return pkt; +} + +typedef struct { + endpoint_t *ep; + io_packet_t *pkt; + unsigned delay_ms; +} cb5_wire_ack_args_t; + +/* Deliver the wire ACK to the promoted-peer message handler after a delay, + * while the main thread is parked inside the barrier wait. */ +static void *cb5_wire_ack_thread(void *arg) +{ + cb5_wire_ack_args_t *a = (cb5_wire_ack_args_t *)arg; + + usleep(a->delay_ms * 1000); + peer_ctrl_msg_handler_for_test(a->ep, a->pkt); + return NULL; +} + +/* statics survive Unity longjmp */ +static endpoint_t g_cb5_wire_ep; +static cb5_wire_ack_args_t g_cb5_wire_args; + +/* A config ACK delivered as WIRE BYTES through the production decode entry + * (peer_ctrl_msg_handler → repl_ack_decode_and_dispatch) must reach + * repl_on_config_ack: the replicator slot's acked_config_lsn advances to + * the decoded tag and a pending configAck barrier completes (Req 4.1). + * RED (remediation r1): repl_ack_decode_and_dispatch has no + * replconfigpushreplymsg branch — the reply is silently dropped, the + * barrier times out, rc=-ETIMEDOUT. */ +static void test_config_ack_wire_path_completes_barrier(void) +{ + const uint64_t push_tag = 0xC0FFEE; + pthread_t th; + int rc; + uint64_t acked = 0; + int found = 0; + + /* Arrange: cb5 cluster (wired engine n_peers=3, two never-acking + * standbys) + a third, promoted-peer endpoint whose vote_ep_ctx routes + * peer_ctrl_msg_handler to repl_ack_decode_and_dispatch (role PRIMARY). */ + cb5_setup(); + memset(&g_cb5_wire_ep, 0, sizeof(g_cb5_wire_ep)); + g_cb5_wire_ep.fd = -1; + vote_ep_ctx_build_for_test(&g_cb5_wire_ep, &g_cb5_c.ha, NULL, 0, + 1, 0, 0, "ctrl-cb5"); + vote_ep_ctx_set_cluster_for_test(&g_cb5_wire_ep, &g_cb5_c); + atomic_store_explicit(&g_cb5_c.ha.role, (int32_t)CTRL_PRIMARY, + memory_order_release); + TEST_ASSERT_TRUE(repl_add_standby(&g_cb5_c.persist.repl, + &g_cb5_wire_ep, "cb5_wire", 7502) >= 0); + + /* Act: park in the barrier (self + 1 covering = 2 > 3/2 quorum), then + * deliver the standby's ACK as raw capnp bytes through the handler. */ + g_cb5_wire_args.ep = &g_cb5_wire_ep; + g_cb5_wire_args.pkt = cb5_build_config_ack_pkt(1, push_tag); + g_cb5_wire_args.delay_ms = 50; + TEST_ASSERT_EQUAL_INT(0, pthread_create(&th, NULL, cb5_wire_ack_thread, + &g_cb5_wire_args)); + rc = repl_wait_majority_ack(&g_cb5_c.persist.repl, push_tag, 1000, + (uint8_t)OPCode_upgradeConfigAck); + pthread_join(th, NULL); + + /* Assert: barrier completed off the wire-decoded ACK … */ + TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, + "a wire-delivered config ACK must complete a pending barrier" + " (production ingestion path — Req 4.1)"); + + /* … because repl_on_config_ack advanced the slot to the decoded tag */ + for (int i = 0; i < MAX_STANDBYS; i++) { + if (g_cb5_c.persist.repl.standbys[i].ep == (void *)&g_cb5_wire_ep) { + acked = atomic_load_explicit( + &g_cb5_c.persist.repl.standbys[i].acked_config_lsn, + memory_order_acquire); + found = 1; + break; + } + } + TEST_ASSERT_TRUE_MESSAGE(found, "wire ep must be registered in the replicator"); + TEST_ASSERT_EQUAL_UINT64_MESSAGE(push_tag, acked, + "the decoded versionTag must land in acked_config_lsn via" + " repl_on_config_ack"); + + free(g_cb5_wire_ep.data); /* vctx from vote_ep_ctx_build_for_test */ + g_cb5_wire_ep.data = NULL; + cb5_teardown(); + printf("test_config_ack_wire_path_completes_barrier: PASS\n"); +} + /* ── Test group entry point ──────────────────────────────────────────────── */ void test_commit_barrier_rollout(void) @@ -1531,5 +1700,8 @@ void test_commit_barrier_rollout(void) RUN_TEST(test_push_config_majority_acks_returns_zero_and_commits); RUN_TEST(test_upgrade_config_barrier_failure_error_reply_no_restart); RUN_TEST(test_upgrade_config_majority_ack_success_unchanged); + /* Task 5.2 remediation r1 — production wire-path ACK ingestion + * (completes the unlanded half of ha-config-bootstrap task 8.1) */ + RUN_TEST(test_config_ack_wire_path_completes_barrier); UNITY_END(); } diff --git a/src/include/repl.h b/src/include/repl.h index 4c6abebf..f15c320f 100644 --- a/src/include/repl.h +++ b/src/include/repl.h @@ -80,10 +80,13 @@ void repl_on_config_ack(replicator_t *r, repl_ep_t ep, uint64_t acked_config_lsn /* Sync replication (stub in P2-1; wired in P2-3) */ int repl_wait_ack(replicator_t *r, uint64_t target_lsn, uint32_t timeout_ms); -/* Majority-quorum ACK wait (task 7.3; ha-commit-engine task 5 adapter). - * entry_lsn: the WAL entry the caller waits on. The walAck branch delegates - * to the commit engine for per-LSN evaluation when wired (task 5.2); the - * legacy counting body ignores it. configAck callers pass 0 (ignored). */ +/* Majority-quorum ACK wait (task 7.3; ha-commit-engine task 5 adapter; + * configAck track hardened by ha-commit-barrier-rollout task 5.2). + * entry_lsn: walAck — the WAL entry the caller waits on, delegated to the + * commit engine for per-LSN evaluation; configAck — the in-flight config + * push tag, counted as acked_config_lsn >= tag with majority + * 1 (self) + count > n_peers / 2 over configured members (no second + * parameter — the configAck branch reuses this one). */ int repl_wait_majority_ack(replicator_t *r, uint64_t entry_lsn, uint32_t timeout_ms, uint8_t ack_op);