Skip to main content

car_sync/
fold.rs

1//! The deterministic fold: `fold(ops) → materialized state`.
2//!
3//! The proposal's convergence contract, verbatim: "each daemon **folds** the
4//! full op-set into local state deterministically. Because the fold is
5//! commutative, associative, and idempotent over the op-set (CRDT
6//! properties), two laptops writing simultaneously converge the moment they
7//! exchange ops."
8//!
9//! Fold rules per surface tier (the proposal's table):
10//! - **Grow-only** (conversations, knowledge, skills, trajectories, runs,
11//!   routing observations): union by [`crate::oplog::OpRecord::fold_key`] —
12//!   the stable entity key for logical-entity surfaces, the `op_id` for
13//!   event-stream surfaces (routing), which fold as a MULTISET: the proposal
14//!   replays "the merged **multiset** of observations", so two
15//!   byte-identical observations are two events and both survive.
16//!   Entities are immutable in this tier (a change is a new op — e.g. a
17//!   `Supersedes` fact), so on a key collision with *different* content the
18//!   earliest `(hlc, op_id)` writer wins, deterministically.
19//! - **Registry** (declagents, the file registries): LWW-register per
20//!   record keyed by id, ordered by HLC — *not per file*. Latest
21//!   `(hlc, op_id)` wins; concurrent edits to different records both
22//!   survive.
23//! - **Routing**: the fold materializes the hlc-ordered observation stream;
24//!   the EMA replay is the caller-injected [`SyncState::replay`] ("sync the
25//!   observations, not the result" — same observations + same canonical
26//!   order ⇒ bit-identical result on every device).
27//! - **Leased** (`Intent`, B5): LWW-per-run_id (monotone status) with
28//!   **per-agent epoch fencing** — a stale-epoch intent from a failed-over
29//!   lease holder loses at the fold, order-independently. See [`crate::lease`]
30//!   and the [`fold_onto`] `FoldTier::Leased` arm.
31//!
32//! Determinism discipline: all state is `BTreeMap`-backed and nothing here
33//! reads a clock — the proposal calls out "a non-determinism leak
34//! (wall-clock or HashMap iteration order sneaking into a fold)" as the bug
35//! class [`state_hash`] exists to catch.
36
37pub use crate::oplog::FoldTier;
38use crate::oplog::{canonical_json, Hlc, OpRecord};
39use car_state::crdt::{LwwMap, LwwRegister};
40use serde::{Deserialize, Serialize};
41use serde_json::Value;
42use sha2::{Digest, Sha256};
43use std::collections::BTreeMap;
44
45/// One folded entity: the winning op's payload plus the stamp/id it won with.
46#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
47pub struct FoldedRecord {
48    pub op_id: String,
49    pub hlc: Hlc,
50    pub payload: Value,
51}
52
53/// One agent's leased execution-intent ledger — the [`FoldTier::Leased`]
54/// tier's per-agent folded state (B5).
55///
56/// This slice delivers **deterministic ledger convergence plus a durable
57/// idempotency oracle** — it does NOT by itself make execution exactly-once.
58/// The exactly-once *execution* gate is B6's dispatch fence (a linearizable
59/// "am I still epoch N?" check plus the durable non-fenced idempotency read
60/// **before** the external side effect); the fold decides who wins the
61/// *ledger*, not whether the effect happens.
62///
63/// Two distinct views live here, and confusing them causes double-execution:
64///
65/// - **`committed_runs` is the idempotency oracle** — a **fence-INDEPENDENT,
66///   keep-all** map `run_id → committed record`. Once a run commits, it stays
67///   here forever (a commit is a fact; no epoch bump erases it), so
68///   [`SyncState::committed_run`] is the correct "did this run already
69///   execute?" lookup. It is carried in the checkpoint and never trimmed by
70///   retention/compaction (see [`crate::compact`]).
71/// - **`runs` is the "who holds now" view** — per-agent epoch **fencing**
72///   applies to *pending* intents (a stale zombie holder's pending is fenced),
73///   while committed/failed records are **terminal-immune** (never reverted to
74///   pending, never cleared by a fence raise). Read via [`SyncState::intent`].
75///   Do **NOT** use `runs`/`intent()` as the idempotency oracle — a pending
76///   fenced by a later epoch is absent here yet the run may have committed; ask
77///   `committed_runs` / [`SyncState::committed_run`].
78///
79/// **Fencing is per AGENT, not per run** (the proposal's spec, deliberately):
80/// a zombie's *unique* post-failover **pending** — one the new holder never
81/// re-ran — is fenced too (per-run fencing would let it through). `fencing_epoch`
82/// is the agent's max-seen lease epoch. `committed_runs` is what makes that
83/// safe for idempotency: even after prior-epoch pendings drop from `runs`, the
84/// committed fact survives keep-all.
85#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
86pub struct IntentAgent {
87    /// The agent's fencing epoch — the max lease epoch any of its intents
88    /// carried. **Pending** intents below it are fenced (terminals are immune).
89    pub fencing_epoch: u64,
90    /// run_id key (`id:<run_id>`) → the "who holds now" winner (terminal-immune;
91    /// pendings fenced to `fencing_epoch`). NOT the idempotency oracle.
92    pub runs: BTreeMap<String, FoldedRecord>,
93    /// run_id key → the committed record. **Fence-independent, keep-all** — the
94    /// durable idempotency oracle that survives epoch bumps AND compaction.
95    /// Grow-only (highest `(epoch, hlc, op_id)` committed record wins on a
96    /// collision); never cleared by fencing. `#[serde(default)]` so a state
97    /// serialized before this field parses.
98    #[serde(default)]
99    pub committed_runs: BTreeMap<String, FoldedRecord>,
100}
101
102/// The materialized read model a full op-set folds to. On-disk files
103/// (`conversations/*.jsonl`, `declagents.json`, …) are projections of this
104/// (the proposal's "files are projections" reframe); B4's checkpoint is a
105/// serialized `SyncState` at a frontier.
106#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
107pub struct SyncState {
108    /// Grow-only tier: surface tag → stable key → record (union;
109    /// first-writer-wins on a key collision).
110    pub logs: BTreeMap<String, BTreeMap<String, FoldedRecord>>,
111    /// Registry tier: surface tag → record id → LWW winner.
112    pub registries: BTreeMap<String, BTreeMap<String, FoldedRecord>>,
113    /// Leased execution-intent tier (B5): agent_id → its fenced intent
114    /// ledger. Folded from [`crate::oplog::Surface::Intent`] ops with **epoch
115    /// fencing** applied deterministically. `#[serde(default)]` so a pre-B5
116    /// serialized state still parses.
117    #[serde(default)]
118    pub intents: BTreeMap<String, IntentAgent>,
119}
120
121impl SyncState {
122    /// A grow-only surface's entries in canonical `(hlc, op_id)` order — the
123    /// deterministic total order every device agrees on (used by the routing
124    /// replay, and the order B2's transcript materialization will consume).
125    pub fn log_entries(&self, surface_tag: &str) -> Vec<&FoldedRecord> {
126        let mut entries: Vec<&FoldedRecord> = self
127            .logs
128            .get(surface_tag)
129            .map(|m| m.values().collect())
130            .unwrap_or_default();
131        entries.sort_by(|a, b| (&a.hlc, &a.op_id).cmp(&(&b.hlc, &b.op_id)));
132        entries
133    }
134
135    /// Replay an order-sensitive fold (e.g. the routing EMA) over a surface's
136    /// canonically-ordered entries: `fold(routing) =
137    /// observations.sorted_by(hlc).fold(empty_store, apply_ema)`. The apply
138    /// function is injected — execution (and the EMA itself) stays out of
139    /// this crate, like the other pure cores.
140    pub fn replay<T, F>(&self, surface_tag: &str, init: T, apply: F) -> T
141    where
142        F: FnMut(T, &FoldedRecord) -> T,
143    {
144        self.log_entries(surface_tag).into_iter().fold(init, apply)
145    }
146
147    /// The **"who holds now"** leased intent for a run (terminal-immune,
148    /// pending-fenced) — NOT the idempotency oracle. `None` when the agent has
149    /// no such run, or the run is a *pending* fenced by a later, higher-epoch
150    /// holder. A committed run is terminal-immune and stays visible here.
151    ///
152    /// **For "did this run already execute?" use [`SyncState::committed_run`]**
153    /// — `intent()` can return `None`/pending for a run that actually committed
154    /// under a prior epoch, which would cause a double-execution if trusted as
155    /// the idempotency check.
156    pub fn intent(&self, agent_id: &str, run_id: &str) -> Option<&FoldedRecord> {
157        self.intents
158            .get(agent_id)?
159            .runs
160            .get(&format!("id:{run_id}"))
161    }
162
163    /// The idempotency oracle (B5): the committed record for a run, if it has
164    /// **ever** committed for this agent. **Fence-independent and keep-all** —
165    /// unaffected by epoch bumps and by compaction — so this is the correct
166    /// "did `run_id` already run?" lookup before dispatching a side effect.
167    /// `None` iff no committed intent for `(agent_id, run_id)` exists.
168    pub fn committed_run(&self, agent_id: &str, run_id: &str) -> Option<&FoldedRecord> {
169        self.intents
170            .get(agent_id)?
171            .committed_runs
172            .get(&format!("id:{run_id}"))
173    }
174
175    /// Every run_id this agent has committed (the keep-all oracle's keys, with
176    /// the `id:` prefix stripped) — for a failover executor scanning "what has
177    /// already run".
178    pub fn committed_run_ids(&self, agent_id: &str) -> Vec<&str> {
179        self.intents
180            .get(agent_id)
181            .map(|a| {
182                a.committed_runs
183                    .keys()
184                    .filter_map(|k| k.strip_prefix("id:"))
185                    .collect()
186            })
187            .unwrap_or_default()
188    }
189
190    /// The agent's current fencing epoch — the max lease epoch its intents
191    /// carry — or `None` if it has none. Pending intents below this are fenced.
192    pub fn fencing_epoch(&self, agent_id: &str) -> Option<u64> {
193        self.intents.get(agent_id).map(|a| a.fencing_epoch)
194    }
195}
196
197/// Is this folded intent record terminal (committed/failed)? Terminals are
198/// immune to fencing; only pending intents are fenced.
199fn intent_is_terminal(rec: &FoldedRecord) -> bool {
200    crate::lease::intent_status_rank(&rec.payload) == 1
201}
202
203/// Total priority for the leased tier's winner selection: terminal-flag (a
204/// terminal always outranks a pending — terminal-immunity), then `epoch`
205/// (fencing / latest-holder), then `(hlc, op_id)`. `max` under this key is
206/// order-independent.
207fn intent_priority(rec: &FoldedRecord) -> (u8, u64, &Hlc, &str) {
208    (
209        crate::lease::intent_status_rank(&rec.payload),
210        crate::lease::intent_epoch(&rec.payload),
211        &rec.hlc,
212        rec.op_id.as_str(),
213    )
214}
215
216/// Fold an op-set into its materialized state. Order-independent (per-key
217/// winner selection under a total order), idempotent (ops dedup on `op_id`
218/// first), and pure.
219///
220/// # Input contract: verify before folding untrusted input
221///
222/// `fold` does NOT verify ids or chains — that is the explicit, separate
223/// [`crate::oplog::verify_log`] pass, and any caller feeding ops from a
224/// remote/untrusted source (the B3 relay pull path) MUST run it first.
225/// `fold` stays order-independent even on invalid input (two records forging
226/// the *same* claimed `op_id` with *different* content dedup by a
227/// content-deterministic tiebreak, not arrival order), but which forged
228/// record wins is meaningless — verification is what makes the answer mean
229/// something.
230pub fn fold(ops: &[OpRecord]) -> SyncState {
231    fold_onto(&SyncState::default(), ops)
232}
233
234/// Fold the op-set **as it stood at a past per-device seq frontier** — the
235/// state a branch would have forked from, without forking anything.
236///
237/// An op is included when its device appears in `frontier` and its `seq` is at
238/// or below that device's bound. A device absent from the frontier contributes
239/// nothing: the frontier describes what a reader had *seen*, and a device it
240/// had never heard from is correctly invisible rather than silently whole.
241///
242/// # What this is for
243///
244/// Counterfactual replay of the *state* suffix — "what did memory look like at
245/// F?", and the ability to assemble a context two ways and diff them. That is
246/// the evaluation primitive CAR is missing, and the reason it matters is
247/// recorded in CLAUDE.md: StateBench's per-track numbers carry a **±15pp noise
248/// floor**, so a per-track delta is uninterpretable on its own, and the only
249/// trustworthy check is to dump the assembled context both ways and compare. A
250/// re-run reintroduces stochastic and environmental variation unrelated to the
251/// edit; folding at a fixed frontier holds everything constant except the edit.
252/// See `docs/proposals/shepherd-substrate-adoption.md` (item 2) and
253/// `docs/proposals/oplog-branch-semantics.md` (Finding 2).
254///
255/// # What this is NOT
256///
257/// Not a branch. Nothing is written, nothing forks, and two callers folding at
258/// the same frontier get the same answer without coordinating. Writing a
259/// sibling chain that can later merge or discard needs a wire-format change, a
260/// digest change, branch-aware fold/checkpoint/compaction/relay/session, a
261/// five-surface FFI change, and a fleet-wide version floor — and the branch
262/// spec argues that side should stay gated, partly because a branch cannot hold
263/// an execution lease and so can speculate about state but not about tool
264/// calls. This function is the whole of the read side, and it needs none of it.
265///
266/// # Cost and equivalence
267///
268/// A filter plus the existing fold: no wire change, no digest change, no
269/// migration, and no new trust assumption. `fold_at(ops, &frontier_of(ops))`
270/// equals `fold(ops)`, and folding at a frontier is exactly folding the
271/// corresponding prefix — both pinned by tests.
272///
273/// Same input contract as [`fold`]: verify untrusted input with
274/// [`crate::oplog::verify_log`] first. Filtering by seq does not make an
275/// unverified log meaningful; it only bounds which of its records are read.
276pub fn fold_at(ops: &[OpRecord], frontier: &crate::relay::Frontier) -> SyncState {
277    let visible: Vec<OpRecord> = ops
278        .iter()
279        .filter(|op| {
280            frontier
281                .get(&op.device_id)
282                .is_some_and(|&bound| op.seq <= bound)
283        })
284        .cloned()
285        .collect();
286    fold(&visible)
287}
288
289/// Fold additional ops **onto an already-folded base state** — the B4
290/// checkpoint-consumption primitive: a truncated device reconstructs
291/// `fold(full log)` as `fold_onto(checkpoint.state, retained tail)`.
292///
293/// Uses the same per-key winner selection as [`fold`] (grow-only earliest
294/// `(hlc, op_id)` wins; registry latest wins; event streams keyed by
295/// `op_id`), so `fold_onto(fold(prefix), suffix) == fold(prefix ∪ suffix)`
296/// exactly — the equivalence that makes compaction safe, and the invariant
297/// the lib-level B4 tests pin per surface. Re-delivering an op already in
298/// the base is idempotent (equal `(hlc, op_id)` never displaces the slot).
299///
300/// Same input contract as [`fold`]: verify (via
301/// [`crate::oplog::verify_log`] / [`crate::checkpoint::verify_anchored`])
302/// before folding untrusted input. One caveat unique to invalid input: the
303/// base keeps only `FoldedRecord`s, so a forged op colliding with a
304/// *base* record's `op_id` cannot use the full-record content tiebreak
305/// [`fold`] applies within one op-set — verification is what makes the
306/// answer mean something.
307pub fn fold_onto(base: &SyncState, ops: &[OpRecord]) -> SyncState {
308    // Idempotence: a retransmitted op (same op_id) folds once. On an id
309    // collision with DIFFERENT content (invalid input — verify_log rejects
310    // it) the tiebreak must not depend on arrival order, so the
311    // lexicographically-smaller canonical serialization wins.
312    let canonical_record = |op: &OpRecord| -> String {
313        canonical_json(&serde_json::to_value(op).expect("OpRecord serializes"))
314    };
315    let mut unique: BTreeMap<&str, &OpRecord> = BTreeMap::new();
316    for op in ops {
317        unique
318            .entry(&op.op_id)
319            .and_modify(|existing| {
320                if *existing != op && canonical_record(op) < canonical_record(existing) {
321                    *existing = op;
322                }
323            })
324            .or_insert(op);
325    }
326
327    let mut state = base.clone();
328
329    // Leased-tier pre-pass (B5): establish the FINAL per-agent `fencing_epoch`
330    // (max over the base and every new intent op) BEFORE the main loop, and
331    // evict base *pending* records that the raised fence makes stale — terminals
332    // (committed/failed) are immune and kept. Deciding pending-fencing against
333    // the final fence (not an intermediate one built up mid-loop) is what keeps
334    // the leased fold order-independent and base-composable
335    // (`fold_onto(checkpoint, tail) == fold(full)`).
336    {
337        let mut agent_max: BTreeMap<String, u64> = BTreeMap::new();
338        for op in unique.values() {
339            if op.surface.fold_tier() == FoldTier::Leased {
340                let slot = agent_max
341                    .entry(crate::lease::intent_agent(&op.payload).to_string())
342                    .or_insert(0);
343                *slot = (*slot).max(crate::lease::intent_epoch(&op.payload));
344            }
345        }
346        for (agent, max_epoch) in agent_max {
347            let entry = state.intents.entry(agent).or_default();
348            if max_epoch > entry.fencing_epoch {
349                entry.fencing_epoch = max_epoch;
350                entry.runs.retain(|_, r| intent_is_terminal(r)); // keep terminals, fence pendings
351            }
352        }
353    }
354
355    for op in unique.values() {
356        let record = FoldedRecord {
357            op_id: op.op_id.clone(),
358            hlc: op.hlc.clone(),
359            payload: op.payload.clone(),
360        };
361        match op.surface.fold_tier() {
362            FoldTier::GrowOnly => {
363                let slot = state
364                    .logs
365                    .entry(op.surface.tag())
366                    .or_default()
367                    .entry(op.fold_key());
368                slot.and_modify(|existing| {
369                    // Immutable-entity union: earliest (hlc, op_id) wins.
370                    // (Unreachable for event-stream surfaces — their fold_key
371                    // IS the op_id, so a collision is the same op.)
372                    if (&record.hlc, &record.op_id) < (&existing.hlc, &existing.op_id) {
373                        *existing = record.clone();
374                    }
375                })
376                .or_insert(record);
377            }
378            FoldTier::Registry => {
379                let slot = state
380                    .registries
381                    .entry(op.surface.tag())
382                    .or_default()
383                    .entry(op.fold_key());
384                slot.and_modify(|existing| {
385                    // LWW: latest (hlc, op_id) wins.
386                    if (&record.hlc, &record.op_id) > (&existing.hlc, &existing.op_id) {
387                        *existing = record.clone();
388                    }
389                })
390                .or_insert(record);
391            }
392            FoldTier::Leased => {
393                // `fencing_epoch` is already final for this agent (pre-pass).
394                let agent = crate::lease::intent_agent(&op.payload).to_string();
395                let epoch = crate::lease::intent_epoch(&op.payload);
396                let key = op.fold_key();
397                let is_terminal = crate::lease::intent_status_rank(&op.payload) == 1;
398                let is_committed = crate::lease::intent_is_committed(&op.payload);
399                let entry = state.intents.entry(agent).or_default();
400
401                // (A) The idempotency ORACLE: grow-only, fence-INDEPENDENT,
402                //     keep-all. A commit is a permanent fact — recorded whatever
403                //     its epoch, never cleared by the fence. Highest
404                //     `(epoch, hlc, op_id)` committed record wins a collision.
405                if is_committed {
406                    let better = entry.committed_runs.get(&key).is_none_or(|existing| {
407                        intent_priority(&record) > intent_priority(existing)
408                    });
409                    if better {
410                        entry.committed_runs.insert(key.clone(), record.clone());
411                    }
412                }
413
414                // (B) The "who holds now" view: terminals are immune, pendings
415                //     are fenced to `fencing_epoch`. Eligible = terminal (always)
416                //     OR a live pending at the fence; below-fence pendings drop.
417                //     The winner is `max` under `intent_priority`, so a terminal
418                //     never reverts to a pending (terminal-immunity) and a
419                //     stale-epoch pending never wins.
420                let eligible = is_terminal || epoch == entry.fencing_epoch;
421                if eligible {
422                    let better = entry.runs.get(&key).is_none_or(|existing| {
423                        intent_priority(&record) > intent_priority(existing)
424                    });
425                    if better {
426                        entry.runs.insert(key, record);
427                    }
428                }
429            }
430        }
431    }
432    state
433}
434
435/// Deterministic content hash of a folded state — the proposal's built-in
436/// divergence invariant: "Same frontier ⇒ same snapshot hash,
437/// deterministically. A mismatch is a fold bug or a non-determinism leak."
438/// B4's checkpoint hash is this value at a frontier.
439pub fn state_hash(state: &SyncState) -> String {
440    let value = serde_json::to_value(state).expect("SyncState serializes");
441    let mut hasher = Sha256::new();
442    hasher.update(canonical_json(&value).as_bytes());
443    let digest = hasher.finalize();
444    let hex: String = digest.iter().take(16).map(|b| format!("{b:02x}")).collect();
445    format!("state-{hex}")
446}
447
448/// Encode an [`Hlc`] as a single `u64` version that preserves the
449/// `(wall_ms, counter)` order — the bridge onto `car_state::crdt`'s
450/// `(version, replica)` total order. 44 bits of wall-clock milliseconds
451/// (good past year 2500) and 20 bits of counter; a counter ≥ 2^20 within one
452/// millisecond is outside the HLC's operating range (B3's clock guarantees
453/// far less) and would break the order-preservation, so it is debug-asserted.
454pub fn hlc_version(hlc: &Hlc) -> u64 {
455    debug_assert!(
456        hlc.counter < (1 << 20),
457        "HLC counter exceeds encoding range"
458    );
459    debug_assert!(
460        hlc.wall_ms < (1 << 44),
461        "HLC wall_ms exceeds encoding range (the << 20 would drop high bits in release)"
462    );
463    (hlc.wall_ms << 20) | (u64::from(hlc.counter) & 0xF_FFFF)
464}
465
466/// Project a folded registry surface onto the shipped
467/// [`car_state::crdt::LwwMap`], so the oplog fold composes with (and is
468/// testably equivalent to) `crdt_merge`/`crdt_export` where the domains
469/// overlap: `fold(union of ops)` ≡ `merge_maps(per-device exports)`.
470pub fn registry_as_lww(state: &SyncState, surface_tag: &str) -> LwwMap {
471    state
472        .registries
473        .get(surface_tag)
474        .map(|records| {
475            records
476                .iter()
477                .map(|(key, rec)| {
478                    (
479                        key.clone(),
480                        LwwRegister::new(
481                            rec.payload.clone(),
482                            hlc_version(&rec.hlc),
483                            rec.hlc.device_id.clone(),
484                        ),
485                    )
486                })
487                .collect()
488        })
489        .unwrap_or_default()
490}
491
492#[cfg(test)]
493mod tests {
494    use super::*;
495    use crate::lease::{Intent, IntentStatus};
496    use crate::oplog::{DeviceLog, Scope, Surface};
497    use serde_json::json;
498
499    /// Append a leased execution-intent op through a device log.
500    fn intent_op(
501        dev: &mut DeviceLog,
502        agent: &str,
503        run: &str,
504        epoch: u64,
505        status: IntentStatus,
506    ) -> OpRecord {
507        dev.append(
508            Scope::Personal,
509            Surface::Intent,
510            Intent::new(agent, run, epoch, status).payload(),
511        )
512    }
513
514    #[test]
515    fn fold_at_the_full_frontier_equals_fold() {
516        // The claim the doc makes about cost: fold_at is fold plus a filter, so
517        // at the log's own frontier it must be indistinguishable — including
518        // the state_hash, which is what a caller diffs.
519        let mut a = DeviceLog::new("a");
520        let mut b = DeviceLog::new("b");
521        let ops = vec![
522            a.append(
523                Scope::Personal,
524                Surface::Knowledge,
525                json!({"id": "f1", "v": 1}),
526            ),
527            b.append(
528                Scope::Personal,
529                Surface::Knowledge,
530                json!({"id": "f2", "v": 2}),
531            ),
532            a.append(
533                Scope::Personal,
534                Surface::Knowledge,
535                json!({"id": "f3", "v": 3}),
536            ),
537        ];
538        let full = crate::relay::frontier_of(&ops);
539        assert_eq!(state_hash(&fold_at(&ops, &full)), state_hash(&fold(&ops)));
540    }
541
542    #[test]
543    fn fold_at_a_past_frontier_equals_folding_the_prefix() {
544        // The equivalence that makes this a REPLAY rather than an approximation:
545        // folding at a frontier gives exactly the state that existed then. Two
546        // devices interleave, so a naive "first N ops" would differ from a
547        // per-device seq bound and this would catch it.
548        let mut a = DeviceLog::new("a");
549        let mut b = DeviceLog::new("b");
550        let a1 = a.append(
551            Scope::Personal,
552            Surface::Knowledge,
553            json!({"id": "f1", "v": 1}),
554        );
555        let b1 = b.append(
556            Scope::Personal,
557            Surface::Knowledge,
558            json!({"id": "f2", "v": 2}),
559        );
560        let a2 = a.append(
561            Scope::Personal,
562            Surface::Knowledge,
563            json!({"id": "f3", "v": 3}),
564        );
565        let b2 = b.append(
566            Scope::Personal,
567            Surface::Knowledge,
568            json!({"id": "f4", "v": 4}),
569        );
570        let all = vec![a1.clone(), b1.clone(), a2, b2];
571
572        // As of "a at seq 0, b at seq 0" — one op from each device.
573        let past = crate::relay::frontier_of(&[a1.clone(), b1.clone()]);
574        let prefix = vec![a1, b1];
575        assert_eq!(
576            state_hash(&fold_at(&all, &past)),
577            state_hash(&fold(&prefix)),
578            "folding at a frontier must equal folding that prefix"
579        );
580        let knowledge = &fold_at(&all, &past).logs[&Surface::Knowledge.tag()];
581        assert_eq!(
582            knowledge.len(),
583            2,
584            "later ops must be invisible: {knowledge:?}"
585        );
586    }
587
588    #[test]
589    fn fold_at_is_order_independent_like_fold() {
590        // fold's central property is order independence; the filter must not
591        // quietly reintroduce an arrival-order dependence.
592        let mut a = DeviceLog::new("a");
593        let mut b = DeviceLog::new("b");
594        let ops = vec![
595            a.append(
596                Scope::Personal,
597                Surface::Knowledge,
598                json!({"id": "f1", "v": 1}),
599            ),
600            b.append(
601                Scope::Personal,
602                Surface::Knowledge,
603                json!({"id": "f2", "v": 2}),
604            ),
605            a.append(
606                Scope::Personal,
607                Surface::Knowledge,
608                json!({"id": "f3", "v": 3}),
609            ),
610        ];
611        let f = crate::relay::frontier_of(&ops);
612        let mut shuffled = ops.clone();
613        shuffled.reverse();
614        assert_eq!(
615            state_hash(&fold_at(&ops, &f)),
616            state_hash(&fold_at(&shuffled, &f))
617        );
618    }
619
620    #[test]
621    fn fold_at_omits_devices_absent_from_the_frontier() {
622        // A frontier describes what a reader had SEEN. A device it never heard
623        // from must contribute nothing — treating "absent" as "unbounded" would
624        // silently fold in a whole device's history and make the answer larger
625        // than the moment being replayed.
626        let mut a = DeviceLog::new("a");
627        let mut b = DeviceLog::new("b");
628        let a1 = a.append(
629            Scope::Personal,
630            Surface::Knowledge,
631            json!({"id": "f1", "v": 1}),
632        );
633        let b1 = b.append(
634            Scope::Personal,
635            Surface::Knowledge,
636            json!({"id": "f2", "v": 2}),
637        );
638
639        let only_a = crate::relay::frontier_of(std::slice::from_ref(&a1));
640        let state = fold_at(&[a1, b1], &only_a);
641        let knowledge = &state.logs[&Surface::Knowledge.tag()];
642        assert_eq!(knowledge.len(), 1, "device b was never seen: {knowledge:?}");
643        assert!(knowledge.contains_key("id:f1"));
644    }
645
646    #[test]
647    fn fold_at_an_empty_frontier_is_the_empty_state() {
648        let mut a = DeviceLog::new("a");
649        let ops = vec![a.append(
650            Scope::Personal,
651            Surface::Knowledge,
652            json!({"id": "f1", "v": 1}),
653        )];
654        let empty = crate::relay::Frontier::new();
655        assert_eq!(
656            state_hash(&fold_at(&ops, &empty)),
657            state_hash(&SyncState::default())
658        );
659    }
660
661    #[test]
662    fn grow_only_unions_by_stable_key() {
663        let mut a = DeviceLog::new("a");
664        let mut b = DeviceLog::new("b");
665        let ops = vec![
666            a.append(
667                Scope::Personal,
668                Surface::Knowledge,
669                json!({"id": "f1", "v": 1}),
670            ),
671            b.append(
672                Scope::Personal,
673                Surface::Knowledge,
674                json!({"id": "f2", "v": 2}),
675            ),
676        ];
677        let state = fold(&ops);
678        let knowledge = &state.logs[&Surface::Knowledge.tag()];
679        assert_eq!(knowledge.len(), 2);
680        assert_eq!(knowledge["id:f1"].payload["v"], json!(1));
681        assert_eq!(knowledge["id:f2"].payload["v"], json!(2));
682    }
683
684    #[test]
685    fn grow_only_key_collision_resolves_to_earliest_deterministically() {
686        // Two devices emit different content under one stable id — an
687        // anomaly for the immutable tier, resolved first-writer-wins.
688        let mut a = DeviceLog::new("a");
689        let mut b = DeviceLog::new("b");
690        let oa = a.append(
691            Scope::Personal,
692            Surface::Knowledge,
693            json!({"id": "f", "v": "a"}),
694        );
695        b.observe(&oa.hlc); // b writes causally later
696        let ob = b.append(
697            Scope::Personal,
698            Surface::Knowledge,
699            json!({"id": "f", "v": "b"}),
700        );
701        let fwd = fold(&[oa.clone(), ob.clone()]);
702        let rev = fold(&[ob, oa]);
703        assert_eq!(fwd, rev);
704        assert_eq!(
705            fwd.logs[&Surface::Knowledge.tag()]["id:f"].payload["v"],
706            json!("a")
707        );
708    }
709
710    #[test]
711    fn registry_is_lww_per_record_not_per_file() {
712        let mut a = DeviceLog::new("a");
713        let mut b = DeviceLog::new("b");
714        // Concurrent edits to DIFFERENT agents both survive.
715        let oa = a.append(
716            Scope::Personal,
717            Surface::Declagent,
718            json!({"id": "x", "owner": "a"}),
719        );
720        let ob = b.append(
721            Scope::Personal,
722            Surface::Declagent,
723            json!({"id": "y", "owner": "b"}),
724        );
725        // Concurrent edits to the SAME agent resolve by HLC.
726        b.observe(&oa.hlc);
727        let ob2 = b.append(
728            Scope::Personal,
729            Surface::Declagent,
730            json!({"id": "x", "owner": "b"}),
731        );
732        let state = fold(&[oa, ob, ob2]);
733        let reg = &state.registries[&Surface::Declagent.tag()];
734        assert_eq!(reg.len(), 2, "both records survive");
735        assert_eq!(
736            reg["id:x"].payload["owner"],
737            json!("b"),
738            "later HLC wins the shared record"
739        );
740        assert_eq!(reg["id:y"].payload["owner"], json!("b"));
741    }
742
743    #[test]
744    fn registry_concurrent_tie_breaks_on_device_deterministically() {
745        // Same lamport stamp on two devices (true concurrency): the HLC's
746        // device_id component breaks the tie, both fold orders agree.
747        let mut a = DeviceLog::new("a");
748        let mut b = DeviceLog::new("b");
749        let oa = a.append(
750            Scope::Personal,
751            Surface::Declagent,
752            json!({"id": "x", "owner": "a"}),
753        );
754        let ob = b.append(
755            Scope::Personal,
756            Surface::Declagent,
757            json!({"id": "x", "owner": "b"}),
758        );
759        assert_eq!(oa.hlc.wall_ms, ob.hlc.wall_ms);
760        let fwd = fold(&[oa.clone(), ob.clone()]);
761        let rev = fold(&[ob, oa]);
762        assert_eq!(fwd, rev);
763        // "b" > "a" in the device tiebreak — matches crdt's replica tiebreak.
764        assert_eq!(
765            fwd.registries[&Surface::Declagent.tag()]["id:x"].payload["owner"],
766            json!("b")
767        );
768    }
769
770    #[test]
771    fn state_hash_detects_divergence_and_agrees_on_convergence() {
772        let mut a = DeviceLog::new("a");
773        let o1 = a.append(Scope::Personal, Surface::Knowledge, json!({"id": "f1"}));
774        let o2 = a.append(Scope::Personal, Surface::Knowledge, json!({"id": "f2"}));
775        let h_full = state_hash(&fold(&[o1.clone(), o2.clone()]));
776        let h_full_again = state_hash(&fold(&[o2.clone(), o1.clone()]));
777        assert_eq!(h_full, h_full_again, "same op-set → same hash");
778        let h_partial = state_hash(&fold(&[o1]));
779        assert_ne!(h_full, h_partial, "different op-set → different hash");
780        assert!(h_full.starts_with("state-"));
781    }
782
783    #[test]
784    fn hlc_version_preserves_order() {
785        let stamps = [
786            Hlc {
787                wall_ms: 1,
788                counter: 0,
789                device_id: "a".into(),
790            },
791            Hlc {
792                wall_ms: 1,
793                counter: 1,
794                device_id: "a".into(),
795            },
796            Hlc {
797                wall_ms: 2,
798                counter: 0,
799                device_id: "a".into(),
800            },
801        ];
802        for w in stamps.windows(2) {
803            assert!(hlc_version(&w[0]) < hlc_version(&w[1]));
804        }
805    }
806
807    #[test]
808    fn registry_as_lww_matches_crdt_merge_including_export_shape() {
809        // The equivalence the proposal leans on: per-device exports merged
810        // with the shipped crdt primitives == the fold of the op union.
811        let mut a = DeviceLog::new("dev-a");
812        let mut b = DeviceLog::new("dev-b");
813        let oa1 = a.append(
814            Scope::Personal,
815            Surface::Registry {
816                kind: "agents".into(),
817            },
818            json!({"id": "r1", "v": "a"}),
819        );
820        let oa2 = a.append(
821            Scope::Personal,
822            Surface::Registry {
823                kind: "agents".into(),
824            },
825            json!({"id": "r2", "v": "a"}),
826        );
827        b.observe(&oa1.hlc);
828        b.observe(&oa2.hlc);
829        let ob1 = b.append(
830            Scope::Personal,
831            Surface::Registry {
832                kind: "agents".into(),
833            },
834            json!({"id": "r1", "v": "b"}),
835        );
836
837        let tag = Surface::Registry {
838            kind: "agents".into(),
839        }
840        .tag();
841        let union = registry_as_lww(&fold(&[oa1.clone(), oa2.clone(), ob1.clone()]), &tag);
842        let export_a = registry_as_lww(&fold(&[oa1, oa2]), &tag);
843        let export_b = registry_as_lww(&fold(&[ob1]), &tag);
844
845        assert_eq!(car_state::crdt::merge_maps(&export_a, &export_b), union);
846        assert_eq!(car_state::crdt::merge_many(&[export_b, export_a]), union);
847        let plain = car_state::crdt::materialize(&union);
848        assert_eq!(plain["id:r1"]["v"], json!("b"));
849        assert_eq!(plain["id:r2"]["v"], json!("a"));
850    }
851
852    #[test]
853    fn log_entries_are_hlc_ordered() {
854        let mut a = DeviceLog::new("a");
855        let mut b = DeviceLog::new("b");
856        let o1 = a.append(
857            Scope::Personal,
858            Surface::Conversation,
859            json!({"t": "first"}),
860        );
861        b.observe(&o1.hlc);
862        let o2 = b.append(
863            Scope::Personal,
864            Surface::Conversation,
865            json!({"t": "second"}),
866        );
867        a.observe(&o2.hlc); // a's next write causally follows b's
868        let o3 = a.append(
869            Scope::Personal,
870            Surface::Conversation,
871            json!({"t": "third"}),
872        );
873        // Deliver out of order; the view is canonical.
874        let state = fold(&[o3, o1, o2]);
875        let texts: Vec<&Value> = state
876            .log_entries(&Surface::Conversation.tag())
877            .iter()
878            .map(|r| &r.payload["t"])
879            .collect();
880        assert_eq!(
881            texts,
882            vec![&json!("first"), &json!("second"), &json!("third")]
883        );
884    }
885
886    #[test]
887    fn routing_observations_fold_as_a_multiset() {
888        // The demonstrated kernel-review defect: "agent x succeeded" twice is
889        // TWO observations. Under content-keyed dedup the second collapsed
890        // into the first (1 entry, EMA 0.65); the proposal requires the
891        // merged MULTISET (2 entries, EMA 0.755).
892        let mut dev = DeviceLog::new("dev-a");
893        let ops = vec![
894            dev.append(Scope::Personal, Surface::Routing, json!({"sample": 1.0})),
895            dev.append(Scope::Personal, Surface::Routing, json!({"sample": 1.0})),
896        ];
897        let state = fold(&ops);
898        assert_eq!(
899            state.log_entries(&Surface::Routing.tag()).len(),
900            2,
901            "two byte-identical observations are two events"
902        );
903        let ema =
904            |s: f64, rec: &FoldedRecord| 0.7 * s + 0.3 * rec.payload["sample"].as_f64().unwrap();
905        let value = state.replay(&Surface::Routing.tag(), 0.5_f64, ema);
906        assert!(
907            (value - 0.755).abs() < 1e-12,
908            "EMA over both events: got {value}"
909        );
910    }
911
912    #[test]
913    fn logical_entity_surfaces_dedup_identical_content() {
914        // Content-keyed dedup is scoped to logical-ENTITY surfaces (knowledge,
915        // skills, …): the same fact emitted identically by two devices is ONE
916        // entity. (Conversation is NOT one of these — it's an event stream
917        // keyed by op_id — see the conversation module's CRIT-2 tests.)
918        let mut a = DeviceLog::new("a");
919        let mut b = DeviceLog::new("b");
920        let fact = json!({"kind": "note", "body": "the sky is blue"});
921        let oa = a.append(Scope::Personal, Surface::Knowledge, fact.clone());
922        let ob = b.append(Scope::Personal, Surface::Knowledge, fact);
923        let state = fold(&[oa, ob]);
924        assert_eq!(state.log_entries(&Surface::Knowledge.tag()).len(), 1);
925    }
926
927    #[test]
928    fn forged_colliding_op_id_dedups_order_independently() {
929        // Invalid input (verify_log rejects it), but the fold must stay
930        // order-independent: two records claiming the SAME op_id with
931        // DIFFERENT content tiebreak on content, not arrival order.
932        let mut dev = DeviceLog::new("d1");
933        let genuine = dev.append(
934            Scope::Personal,
935            Surface::Knowledge,
936            json!({"id": "f", "v": 1}),
937        );
938        let mut forged = genuine.clone();
939        forged.payload = json!({"id": "f", "v": 2}); // op_id NOT recomputed
940        assert!(crate::oplog::verify_log(&[forged.clone()]).is_err());
941
942        let ab = fold(&[genuine.clone(), forged.clone()]);
943        let ba = fold(&[forged, genuine]);
944        assert_eq!(
945            ab, ba,
946            "colliding-id dedup must not depend on arrival order"
947        );
948        assert_eq!(state_hash(&ab), state_hash(&ba));
949    }
950
951    #[test]
952    fn fold_onto_prefix_fold_equals_full_fold() {
953        // The B4 primitive: fold(prefix) then fold_onto(., suffix) must be
954        // byte-identical to fold(prefix ∪ suffix) — for every fold rule at
955        // once, including a grow-only collision and an LWW overwrite that
956        // CROSS the split point.
957        let mut a = DeviceLog::new("a");
958        let mut b = DeviceLog::new("b");
959        let prefix = vec![
960            a.append(
961                Scope::Personal,
962                Surface::Knowledge,
963                json!({"id": "f", "v": "old"}),
964            ),
965            a.append(
966                Scope::Personal,
967                Surface::Declagent,
968                json!({"id": "x", "owner": "a"}),
969            ),
970            a.append(Scope::Personal, Surface::Routing, json!({"sample": 1.0})),
971        ];
972        for op in &prefix {
973            b.observe(&op.hlc);
974        }
975        let suffix = vec![
976            // Grow-only collision across the split: earliest wins → "old".
977            b.append(
978                Scope::Personal,
979                Surface::Knowledge,
980                json!({"id": "f", "v": "new"}),
981            ),
982            // LWW across the split: latest wins → owner "b".
983            b.append(
984                Scope::Personal,
985                Surface::Declagent,
986                json!({"id": "x", "owner": "b"}),
987            ),
988            // Event stream across the split: both observations survive.
989            b.append(Scope::Personal, Surface::Routing, json!({"sample": 0.0})),
990        ];
991        let mut full = prefix.clone();
992        full.extend(suffix.iter().cloned());
993
994        let via_base = fold_onto(&fold(&prefix), &suffix);
995        assert_eq!(via_base, fold(&full));
996        assert_eq!(state_hash(&via_base), state_hash(&fold(&full)));
997        assert_eq!(
998            via_base.logs[&Surface::Knowledge.tag()]["id:f"].payload["v"],
999            json!("old")
1000        );
1001        assert_eq!(
1002            via_base.registries[&Surface::Declagent.tag()]["id:x"].payload["owner"],
1003            json!("b")
1004        );
1005        assert_eq!(via_base.log_entries(&Surface::Routing.tag()).len(), 2);
1006
1007        // Idempotent re-delivery: folding an op already in the base changes
1008        // nothing.
1009        assert_eq!(fold_onto(&via_base, &prefix), via_base);
1010    }
1011
1012    #[test]
1013    fn empty_fold_is_empty_and_stable() {
1014        let state = fold(&[]);
1015        assert_eq!(state, SyncState::default());
1016        assert_eq!(state_hash(&state), state_hash(&fold(&[])));
1017        assert!(state.log_entries("conversation").is_empty());
1018        assert!(registry_as_lww(&state, "declagent").is_empty());
1019        assert!(state.intent("milo", "R").is_none());
1020        assert!(state.fencing_epoch("milo").is_none());
1021    }
1022
1023    // ------------------------------------------------------------------
1024    // B5: leased execution-intent fencing as a deterministic fold property.
1025    // ------------------------------------------------------------------
1026
1027    #[test]
1028    fn intent_fold_fences_stale_epoch_order_independently() {
1029        // Failover: dev-a held epoch 1, dev-b stole epoch 2. Both fire the
1030        // SAME run R — a=zombie, b=legit holder. The fold must pick epoch 2
1031        // (b) in ANY delivery order and fence a's epoch-1 writes, leaving one
1032        // ledger record — no double-commit.
1033        let mut a = DeviceLog::new("dev-a");
1034        let mut b = DeviceLog::new("dev-b");
1035        let ops = vec![
1036            intent_op(&mut a, "milo", "R", 1, IntentStatus::Pending),
1037            intent_op(&mut a, "milo", "R", 1, IntentStatus::Committed),
1038            intent_op(&mut b, "milo", "R", 2, IntentStatus::Pending),
1039            intent_op(&mut b, "milo", "R", 2, IntentStatus::Committed),
1040        ];
1041        let b_commit = ops[3].clone();
1042
1043        let baseline = fold(&ops);
1044        assert_eq!(baseline.fencing_epoch("milo"), Some(2));
1045        assert_eq!(
1046            baseline.intents["milo"].runs.len(),
1047            1,
1048            "single record — no double-commit"
1049        );
1050        let winner = baseline.intent("milo", "R").expect("R survives");
1051        let decoded = Intent::from_payload(&winner.payload).unwrap();
1052        assert_eq!(
1053            (decoded.epoch, decoded.status),
1054            (2, IntentStatus::Committed)
1055        );
1056        assert_eq!(
1057            winner.op_id, b_commit.op_id,
1058            "the current holder's commit wins"
1059        );
1060
1061        // Order-independence: several explicit permutations agree exactly.
1062        for order in [
1063            vec![
1064                ops[3].clone(),
1065                ops[2].clone(),
1066                ops[1].clone(),
1067                ops[0].clone(),
1068            ],
1069            vec![
1070                ops[2].clone(),
1071                ops[0].clone(),
1072                ops[3].clone(),
1073                ops[1].clone(),
1074            ],
1075            vec![
1076                ops[1].clone(),
1077                ops[3].clone(),
1078                ops[0].clone(),
1079                ops[2].clone(),
1080            ],
1081        ] {
1082            assert_eq!(fold(&order), baseline);
1083            assert_eq!(state_hash(&fold(&order)), state_hash(&baseline));
1084        }
1085    }
1086
1087    #[test]
1088    fn intent_per_agent_pending_fencing_with_committed_immunity() {
1089        // Per-AGENT fencing applies to PENDINGS: dev-a (epoch 1) has an
1090        // unshared PENDING run S that dev-b (epoch 2) never touched → S's
1091        // pending is fenced (a stale holder's intent-to-do is silenced). But a
1092        // COMMITTED run is terminal-immune — a commit is a fact, not fenced —
1093        // so the zombie's unshared committed run K survives (the C1 fix: an
1094        // unrelated higher-epoch run must not evict it).
1095        let mut a = DeviceLog::new("dev-a");
1096        let mut b = DeviceLog::new("dev-b");
1097        let ops = vec![
1098            intent_op(&mut a, "milo", "S", 1, IntentStatus::Pending), // unshared zombie pending
1099            intent_op(&mut a, "milo", "K", 1, IntentStatus::Committed), // unshared zombie commit
1100            intent_op(&mut b, "milo", "T", 2, IntentStatus::Committed), // new holder, unrelated run
1101        ];
1102        let state = fold(&ops);
1103        assert_eq!(state.fencing_epoch("milo"), Some(2));
1104        // The unshared PENDING is fenced; it never committed.
1105        assert!(
1106            state.intent("milo", "S").is_none(),
1107            "unshared zombie pending is fenced"
1108        );
1109        assert!(
1110            state.committed_run("milo", "S").is_none(),
1111            "S never committed"
1112        );
1113        // The unshared COMMITTED run survives the unrelated epoch bump (C1).
1114        assert!(
1115            state.committed_run("milo", "K").is_some(),
1116            "committed run survives an unrelated epoch bump (idempotency oracle)"
1117        );
1118        assert!(
1119            state.intent("milo", "K").is_some(),
1120            "committed is terminal-immune in runs too"
1121        );
1122        assert!(state.committed_run("milo", "T").is_some());
1123        // A different agent is a different fencing group.
1124        let mut c = DeviceLog::new("dev-c");
1125        let mixed = {
1126            let mut v = ops.clone();
1127            v.push(intent_op(&mut c, "other", "U", 1, IntentStatus::Committed));
1128            v
1129        };
1130        assert!(
1131            fold(&mixed).committed_run("other", "U").is_some(),
1132            "fencing does not cross agents"
1133        );
1134    }
1135
1136    #[test]
1137    fn intent_fold_onto_equals_full_fold_across_an_epoch_bump() {
1138        // The compaction-safety equivalence for the leased tier across an epoch
1139        // bump: a checkpoint captured the agent at epoch 1 (COMMITTED run R). A
1140        // later, higher-epoch tail op (run S @ 2) raises the fence — and R,
1141        // being committed, is terminal-immune and SURVIVES (the C1/C3 fix; a
1142        // commit is a durable fact, not evicted by an unrelated bump). The
1143        // fold_onto == fold equivalence still holds exactly.
1144        let mut a = DeviceLog::new("dev-a");
1145        let mut b = DeviceLog::new("dev-b");
1146        let prefix = vec![intent_op(&mut a, "milo", "R", 1, IntentStatus::Committed)];
1147        for op in &prefix {
1148            b.observe(&op.hlc);
1149        }
1150        let tail = vec![intent_op(&mut b, "milo", "S", 2, IntentStatus::Committed)];
1151        let full = {
1152            let mut v = prefix.clone();
1153            v.extend(tail.iter().cloned());
1154            v
1155        };
1156
1157        let base = fold(&prefix); // the "checkpoint" state: epoch 1, R committed
1158        assert_eq!(base.fencing_epoch("milo"), Some(1));
1159        assert!(base.committed_run("milo", "R").is_some());
1160
1161        let via_base = fold_onto(&base, &tail);
1162        assert_eq!(
1163            via_base,
1164            fold(&full),
1165            "fold_onto == fold across the epoch bump"
1166        );
1167        assert_eq!(state_hash(&via_base), state_hash(&fold(&full)));
1168        assert_eq!(via_base.fencing_epoch("milo"), Some(2));
1169        // R committed@1 SURVIVES the bump in both views (terminal-immune / oracle).
1170        assert!(
1171            via_base.committed_run("milo", "R").is_some(),
1172            "committed R survives the epoch bump in the idempotency oracle"
1173        );
1174        assert!(
1175            via_base.intent("milo", "R").is_some(),
1176            "committed R is terminal-immune in runs"
1177        );
1178        assert!(via_base.committed_run("milo", "S").is_some());
1179        // Idempotent re-delivery of the tail changes nothing.
1180        assert_eq!(fold_onto(&via_base, &tail), via_base);
1181    }
1182
1183    #[test]
1184    fn intent_fencing_beats_a_later_hlc() {
1185        // Safety is by EPOCH, not wall clock: a zombie op with a LATER hlc but
1186        // a LOWER epoch still loses to the higher-epoch op — no wall-clock race.
1187        let mut cloud = DeviceLog::new("cloud");
1188        let mut zombie = DeviceLog::new("laptop");
1189        let c = intent_op(&mut cloud, "milo", "R", 2, IntentStatus::Committed);
1190        zombie.observe(&c.hlc); // the zombie's later write stamps a HIGHER hlc
1191        let z = intent_op(&mut zombie, "milo", "R", 1, IntentStatus::Committed);
1192        assert!(z.hlc > c.hlc, "the zombie op is later in HLC");
1193
1194        let state = fold(&[c.clone(), z]);
1195        let winner = state.intent("milo", "R").unwrap();
1196        assert_eq!(winner.op_id, c.op_id, "higher epoch wins despite lower HLC");
1197        assert_eq!(state.fencing_epoch("milo"), Some(2));
1198    }
1199
1200    #[test]
1201    fn idempotent_run_under_failover_uses_the_same_deterministic_run_id() {
1202        // B7 tie-in: two sites computing the same scheduled occurrence derive
1203        // the SAME run_id, so a failed-over holder and a zombie collapse to ONE
1204        // ledger record; epoch fencing then picks the legit (epoch-2) winner.
1205        let run_id = car_proto::deterministic_run_id("milo", "3am digest", "2026-07-02T03:00");
1206        assert_eq!(
1207            run_id,
1208            car_proto::deterministic_run_id("milo", "3am digest", "2026-07-02T03:00"),
1209            "same occurrence → same run_id"
1210        );
1211        let mut zombie = DeviceLog::new("laptop");
1212        let mut cloud = DeviceLog::new("cloud");
1213        let z = intent_op(&mut zombie, "milo", &run_id, 1, IntentStatus::Committed);
1214        let c = intent_op(&mut cloud, "milo", &run_id, 2, IntentStatus::Committed);
1215
1216        let state = fold(&[z, c.clone()]);
1217        assert_eq!(
1218            state.intents["milo"].runs.len(),
1219            1,
1220            "exactly one execution record"
1221        );
1222        assert_eq!(
1223            state.intent("milo", &run_id).unwrap().op_id,
1224            c.op_id,
1225            "the epoch-2 holder's run wins; the zombie is a no-op"
1226        );
1227    }
1228
1229    #[test]
1230    fn intent_fold_is_order_independent_over_every_permutation() {
1231        // Brute-force the redesigned leased fold (pre-pass + terminal-immunity
1232        // + committed oracle): a 5-op set mixing committed/pending across two
1233        // epochs and three runs must fold IDENTICALLY in all 120 orders.
1234        fn permutations<T: Clone>(items: &[T]) -> Vec<Vec<T>> {
1235            fn heap<T: Clone>(k: usize, arr: &mut Vec<T>, out: &mut Vec<Vec<T>>) {
1236                if k == 1 {
1237                    out.push(arr.clone());
1238                    return;
1239                }
1240                for i in 0..k {
1241                    heap(k - 1, arr, out);
1242                    if k.is_multiple_of(2) {
1243                        arr.swap(i, k - 1);
1244                    } else {
1245                        arr.swap(0, k - 1);
1246                    }
1247                }
1248            }
1249            let mut arr = items.to_vec();
1250            let mut out = Vec::new();
1251            heap(arr.len(), &mut arr, &mut out);
1252            out
1253        }
1254
1255        let mut a = DeviceLog::new("a");
1256        let mut b = DeviceLog::new("b");
1257        let ops = vec![
1258            intent_op(&mut a, "milo", "R", 1, IntentStatus::Committed), // terminal-immune across bump
1259            intent_op(&mut a, "milo", "S", 1, IntentStatus::Pending),   // unshared pending → fenced
1260            intent_op(&mut a, "milo", "T", 1, IntentStatus::Committed), // unshared committed → survives
1261            intent_op(&mut b, "milo", "R", 2, IntentStatus::Pending),   // C3: must not revert R
1262            intent_op(&mut b, "milo", "S", 2, IntentStatus::Committed), // S commits at the higher epoch
1263        ];
1264        let baseline = fold(&ops);
1265        // Expected steady state.
1266        assert_eq!(baseline.fencing_epoch("milo"), Some(2));
1267        let mut committed = baseline.committed_run_ids("milo");
1268        committed.sort();
1269        assert_eq!(
1270            committed,
1271            vec!["R", "S", "T"],
1272            "the oracle keeps every committed run"
1273        );
1274        assert!(baseline
1275            .intent("milo", "S")
1276            .map(|r| r.op_id.clone())
1277            .is_some_and(|_| {
1278                Intent::from_payload(&baseline.intent("milo", "S").unwrap().payload)
1279                    .unwrap()
1280                    .status
1281                    == IntentStatus::Committed
1282            }));
1283        assert_eq!(
1284            Intent::from_payload(&baseline.intent("milo", "R").unwrap().payload)
1285                .unwrap()
1286                .status,
1287            IntentStatus::Committed,
1288            "R is not reverted to pending"
1289        );
1290
1291        for perm in permutations(&ops) {
1292            assert_eq!(
1293                fold(&perm),
1294                baseline,
1295                "leased fold must be order-independent"
1296            );
1297            assert_eq!(state_hash(&fold(&perm)), state_hash(&baseline));
1298        }
1299    }
1300
1301    #[test]
1302    fn c1_committed_run_survives_an_unrelated_higher_epoch_run() {
1303        // C1 REPRO: run R commits at epoch 1; later an UNRELATED run T lands at
1304        // epoch 2 for the same agent (no concurrency). The old fold cleared
1305        // runs on the bump, so intent(R) → None → the idempotency check said
1306        // "not run" → double-execution. FIX: the fence-INDEPENDENT
1307        // committed_run oracle answers correctly regardless of the bump.
1308        let mut a = DeviceLog::new("dev-a");
1309        let mut b = DeviceLog::new("dev-b");
1310        let r_commit = intent_op(&mut a, "milo", "R", 1, IntentStatus::Committed);
1311        b.observe(&r_commit.hlc);
1312        let t_pending = intent_op(&mut b, "milo", "T", 2, IntentStatus::Pending);
1313
1314        let state = fold(&[r_commit.clone(), t_pending]);
1315        assert_eq!(
1316            state.fencing_epoch("milo"),
1317            Some(2),
1318            "the unrelated run bumped the fence"
1319        );
1320        // The oracle still says R committed — the correct idempotency answer.
1321        assert_eq!(
1322            state.committed_run("milo", "R").unwrap().op_id,
1323            r_commit.op_id,
1324            "committed_run(R) survives the unrelated epoch bump (C1 fixed)"
1325        );
1326        assert_eq!(state.committed_run_ids("milo"), vec!["R"]);
1327    }
1328
1329    #[test]
1330    fn c3_committed_then_pending_across_a_bump_stays_committed() {
1331        // C3 REPRO: R commits at epoch 1; a failed-over holder writes R PENDING
1332        // at epoch 2 (before checking). The old Greater arm unconditionally
1333        // cleared, reverting R to pending → looked un-run → double-execute.
1334        // FIX: terminal-immunity — the fold keeps committed for R in BOTH views,
1335        // in any delivery order.
1336        let mut orig = DeviceLog::new("orig");
1337        let mut failover = DeviceLog::new("failover");
1338        let committed = intent_op(&mut orig, "milo", "R", 1, IntentStatus::Committed);
1339        failover.observe(&committed.hlc);
1340        let late_pending = intent_op(&mut failover, "milo", "R", 2, IntentStatus::Pending);
1341        assert!(
1342            late_pending.hlc > committed.hlc,
1343            "the pending is even later in HLC"
1344        );
1345
1346        for order in [
1347            vec![committed.clone(), late_pending.clone()],
1348            vec![late_pending.clone(), committed.clone()],
1349        ] {
1350            let state = fold(&order);
1351            // Oracle: committed, unconditionally.
1352            assert_eq!(
1353                state.committed_run("milo", "R").unwrap().op_id,
1354                committed.op_id,
1355                "committed stays committed across the bump (oracle)"
1356            );
1357            // who-holds view: terminal-immune, still committed (not reverted).
1358            let decoded =
1359                Intent::from_payload(&state.intent("milo", "R").unwrap().payload).unwrap();
1360            assert_eq!(
1361                decoded.status,
1362                IntentStatus::Committed,
1363                "runs view is not reverted to pending"
1364            );
1365        }
1366    }
1367}