2026-06-02. Replaces the cf1f80c "generation = per-lifetime root version" ordering key, which the 3-agent
red-team found has residual data-loss holes (docs/design/redteam-synthesis-commitrank-and-flip.md A.1-A.6). This
redesign WILL be red-teamed before implementation. Persisted from the redesign Plan agent.
D3 (durable root version) REJECTED: fixes only A.2, not A.1/A.3/A.4/A.5; root version is bumped per-publication
but idempotent arms publish nothing + increments bypass it. D2 (visibility-LSN token) REJECTED: assigning a
token at the CAS from the next_lsn counter (also advanced at append, out-of-CAS-order) just moves the skew; a
reserved LSN punches watermark holes. But D2 surfaces the right invariant: the key must live in ONE durable
monotone domain shared by ranked AND unranked records. D1 RECOMMENDED: a purpose-built AtomicU64 commit_seq,
advanced once per state-changing commit, stamped on EVERY state-changing record (incl. increments), ordered by
(commit_seq, lsn), seeded on open from the durable max → globally monotone across restarts, decoupled from the
volatile root version. Strictly stronger than cf1f80c (monotone per-term AND across terms AND across restarts);
generalizes the single-session reconcile_lww path, doesn't regress it.
commit_seq: AtomicU64 on PersistentARTrieChar (beside committed_watermark).
Seeding (A.2 fix): in every open/recovery WAL scan (mmap_ctor.rs:300, io_uring twin, RecoveryManager),
track max_commit_seq = max over ALL recovered CommitRank.generation (over the FULL scan, BEFORE the
checkpoint-skip filter — a post-checkpoint op must out-rank a pre-checkpoint same-term op still in a retained
segment); seed commit_seq = AtomicU64::new(max_commit_seq). enable_lockfree MUST NOT reset it (it's trie-owned,
seeded once at open, survives enable_lockfree — the precise A.2 fix).
Claim-before-CAS, discard-on-loss (the monotonicity rule — load-bearing): next_commit_seq() =
commit_seq.fetch_add(1, AcqRel)+1, claimed BEFORE the visibility CAS each loop iteration; on CAS-loss the claim is
DISCARDED (leaves a harmless gap — we only ever COMPARE commit_seq, never require contiguity). Theorem
(CommitSeqMonotone): if X ≺_CAS Y on the same term, commit_seq(X) < commit_seq(Y) — because the only way Y
gets a lower winning claim than X is if Y claimed-and-won before X claimed ($\Rightarrow$ Y ≺_CAS X, contradiction). Claiming
AFTER the CAS (no gaps) reopens a CAS-vs-fetch_add inversion — rejected; gaps are the correct price.
A.6 (crash between data-append and rank-append) — made BENIGN, NOT eliminated (honest): under Order-A you
CANNOT carry the CAS-assigned key in a single durable-before-visible append (the key is unknown until the CAS = the
visibility point; a single append after the CAS is Order-B = visible-before-durable, rejected). So KEEP the post-CAS
CommitRank append; the window yields an UN-ACKED op (watermark stalls at the data LSN — mark_committed runs only
after BOTH appends). A v3 data record with no rank at the frontier uses generation_of=lsn — safe because it has no
same-term out-orderer (it's the last append). This is the single weakest seam (risk §8.1); the red-team must
scrutinize the "no same-term competitor above the frontier" argument under a torn multi-op batch.
next_commit_seq() wired into EVERY durable producer (replacing root-version reads), claim-before-CAS:
insert_cas_durable winning :363 + idempotent AlreadyExists :383 (A.5 fix: real commit_seq, not live
lockfree_root.load().version() re-walk).remove_cas_durable winning :540 + idempotent AlreadyAbsent :567 (A.5 fix).insert_cas_with_value_durable:1659, upsert_cas_durable:1757.try_increment_cas_durable:1547 (A.3 HEADLINE): now appends a CommitRank (was rank-free) — increments ranked
exactly like every other op; no more cross-domain LSN-vs-root-version mis-sort.Paths: (1)+(2) ctors mmap_ctor.rs:403/597 + io_uring_ctor.rs:227 → replay_records_lww→reconcile_lww
(already rank-honoring — auto-fixed once §3 stamps commit_seq). (3) recover_from_archives:1138→
rebuild_from_wal_segments:1443 (raw LSN — A.1): collect segment records into one Vec<(Lsn,WalRecord)> (already
LSN-sorted) → route through replay_records_lww. (4) RecoveryManager::redo_phase:756 + IncrementalRecovery:932
(raw LSN — A.1): extract tx-gating into a shared committed_records_after_tx_filter → route through reconcile_lww.
IncrementalRecovery STREAMS (can't hold all) → buffer per-checkpoint-window (a checkpoint bounds the reorder
window) OR fail-closed on v3 lock-free WALs (documented restriction, risk §8.3). New reconcile signature:
reconcile_lww(records, loaded_from_disk, checkpoint_lsn, wal_version, tx_states).
Shared analysis_phase-style scan builds tx_states: HashMap<u64,{Committed|Aborted|Incomplete}>; reconcile_lww
SKIPS data records of non-Committed tx (a current_tx cursor like redo_phase:769). Closes A.4 (aborted/incomplete
document-tx replayed on the ctor path). A committed document-tx batch gets ONE CommitRank at CommitTx (batch
entries touch distinct terms; intra-batch order irrelevant). Removes the fragile "masked because overlay rejects
commit_document" dependence.
C1′: its R-2 bail-rank g_read becomes a commit_seq claimed at the read-snapshot (same counter) → strictly <
any superseder's commit_seq in the DURABLE domain (fixes the synthesis note "C1′ inherits A.2"). One counter, one
rank record, one reconcile; the NoPhantomCasWrite + DurableGlobalOrder TLAs compose.
cf1f80c: single-session insert/remove → commit_seq order $\equiv$ root-version order, so reconcile_lww + the OD4 s019
regression PASS unchanged (strengthens, doesn't regress).
WAL: REUSE CommitRank=15 (rename generation→commit_seq in the v3 path; wire layout byte-identical). Header
VERSION 2→3 (header.rs:38), MIN_SUPPORTED 1. Backward: v1→lsn fallback, v2→root-version legacy comparator
(selected by the wal_version param — a v2 WAL's ranks are root-versions, must NOT be reinterpreted as commit_seq);
no migration. Forward: v2 binary refuses v3 fail-closed (GAP_LEDGER/UNSAFE_BOUNDARY). No new record type (auditable
zero structural delta).
TLA — extend LockFreeOverlayDurableReplay.tla → DurableGlobalOrder with FIVE negative controls (each MUST
fire its invariant; register in verify-formal-correspondence.sh:341):
Restart (resets rootVersion, NOT nextGen=commit_seq); _UnsafeRestart.cfg orders by rootVersion
→ MUST violate ReplayEqualsCommittedVisible on the cross-restart collision.Increment kind + per-term value; _UnsafeRankFreeIncrement.cfg models rank-free increment → MUST
violate new ReplayEqualsCommittedValue.CONSTANT RECOVERY_PATH; AllPathsAgree; _UnsafeRawLsnPaths.cfg (archive/redo/incremental order by raw
LSN) → MUST violate on s019.NoUncommittedTxReplay; _UnsafeTxIgnored.cfg (reconcile ignores tx) → MUST violate.CommitSeqMonotone (the §2 theorem, gaps modeled).
Extended soak (the blind spots cf1f80c's 50/50 missed) — each $\ge$50$\times$ green, Immediate+GroupCommit, real-disk:
(1) archive-rebuild (recover_from_archives), (2) cross-restart (session1 insert+remove → reopen → session2 insert
→ crash → reopen → PRESENT), (3) increment-mixed-with-insert/upsert/remove on one key (value equality), (4)
aborted document-tx (OwnedTree), (5) idempotent-arm race. Plus a deterministic cross-restart regression
(fails-pre/passes-post). Stay-green: concurrent_durable_writers_all_survive_reopen, the existing soak, OD4,
recovery_replay_completeness_correspondence, atomicity specs, full gate exit 0, 0 unsafe.\ge$2534 + verify-formal-correspondence exit 0 + unsafe exit 0)commit_seq field + seeding (no behavior change; key still root-version). Rollback: delete field.commit_seq, claim-before-CAS-discard-on-loss, INCL. increment-ranked (A.3) +
idempotent-arm fix (A.5); header 2→3. Rollback: revert key source + header. Gate: single-session soak+OD4 green;
cross-restart/increment soaks now PASS.wal_version+tx_states (A.4) + unify paths 3+4 (A.1). Gate: archive+aborted-tx soaks PASS,
AllPathsAgree.\ge$50$\times$ + deterministic cross-restart regression. Unblocks the flip.
DG0-DG3 revert by code; DG4-DG5 verification-only; the one one-way step = header 2→3 (fail-closed, opt-in pre-flip).IncrementalRecovery ⟂ global reordering (bound by checkpoint or fail-closed on never-checkpoint+v3).src/persistent_artrie_char/lockfree_cas.rs (commit_seq+next_commit_seq; rewrite key source in all producers
:363,383,540,567,1659,1757 + A.3 :1547; claim-before-CAS; 5 soaks)src/persistent_artrie_core/recovery.rs (reconcile_lww:253 +wal_version+tx_states, v3-vs-legacy, A.4
tx-gating; unify redo_phase:756+IncrementalRecovery:932+rebuild_from_wal_segments:1443)src/persistent_artrie_char/mutation_core.rs (replay_records_lww:252 params; apply_core_recovered_operation_no_wal:286 shared applier)src/persistent_artrie_char/mmap_ctor.rs (seed commit_seq:300-321; route recover_from_archives:1138;
io_uring_ctor.rs:227 twin)formal-verification/tla+/LockFreeOverlayDurableReplay.tla (DurableGlobalOrder + 5 _Unsafe*.cfg;
scripts/verify-formal-correspondence.sh:341); wal/header.rs:38 (2→3); wal/codec.rs:227 (rename, layout same)Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |