--- id: "bug-990" status: "fixed" status_updated: "2026-09-24" status_note: "" component: "controller" severity: "unknown" timestamp: "2026-06-22T13:24:07.476480" last_seen: "2026-09-19T09:00:00Z" occurrences: 1 files: ["src/controller/repl.c (live send repl_broadcast persist.c:747) + src/controller/ha.c (ha_apply_wal_catchup_entry term!=0 legacy direct-apply)"] tags: ["ha-term-safety", "task-5.2", "live-send-retirement", "term-0", "repl_broadcast", "committed-prefix-divergence", "bug-851", "BLOCKED"] related: ["bug-851"] legacy: true --- # bug-990: ha-term-safety 5.2 live failover: standbys persist per-entry WAL term=0 while primary persists the… ## Description ha-term-safety 5.2 live failover: standbys persist per-entry WAL term=0 while primary persists the real term (e.g. primary LSN1..5 term=1, ctrl-2/ctrl-3 term=0); a clean failover's committed prefix is byte-identical in payload (pcrc) but NOT in term, so R5.1 'byte-identical WAL including terms' cannot pass. walcompare passes only via legacy-0 -> WAL_LEGACY_TERM tolerance, which masks it on a fresh cluster. ## Environment _Not recorded (migrated from buglog.json)._ ## Reproduction _Not recorded (migrated from buglog.json)._ ## Root cause The live replication SEND path was deferred from tasks 3.2/3.3 and still uses the legacy term-0 repl_broadcast; the standby applies via the legacy direct-apply path, NOT through raft_on_append_entries_request, so it never receives/persists the leader's term. The raft library send loop (on_send_append_entries / raft_send_append_entries) has ZERO live callers. on_persist_entry stamps the real term on the PRIMARY only. ## Fix FIXED AND VERIFIED LIVE -- root-caused and fixed together with bug-851 in ha-term-safety task 3.4: repl_broadcast (persist.c:747, the live replicator) built its wire entry without setting entry.term, so standbys persisted term=0 for every live-replicated entry even though the primary's own WAL header carried the real term. Fix carries entry.term=hdr->term/prev_log_term through the live send+intake path (repl_broadcast, persist_wal_async, ha_stream_wal_to_standby, ha_apply_wal_catchup_entry all updated), retiring the term-0 legacy direct-apply branch. 175/175 unit tests, reviewer-approved. Live-confirmed on a real fs ($HOME/tmp/xce-ha-deploy, ext4, 2026-06-22): 'ctrl-2/ctrl-3 persisting the leader's REAL term... walcompare OK over the committed prefix.' [Original entry] NOT FIXED (out of task 5.2 testsuite-only boundary). Remaining integration work: retire the term-0 repl_broadcast live SEND and drive raft_send_append_entries/on_send_append_entries on the live primary so standbys persist the leader's term via raft_on_append_entries_request. Built clean_failover_preservation.sh (XCE_FAILOVER_STRICT_TERM=1) as the ready-to-run assertion this retirement must satisfy. ## Verification _Not recorded (migrated from buglog.json)._ ## References - Related: bug-851