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 additional ops **onto an already-folded base state** — the B4
235/// checkpoint-consumption primitive: a truncated device reconstructs
236/// `fold(full log)` as `fold_onto(checkpoint.state, retained tail)`.
237///
238/// Uses the same per-key winner selection as [`fold`] (grow-only earliest
239/// `(hlc, op_id)` wins; registry latest wins; event streams keyed by
240/// `op_id`), so `fold_onto(fold(prefix), suffix) == fold(prefix ∪ suffix)`
241/// exactly — the equivalence that makes compaction safe, and the invariant
242/// the lib-level B4 tests pin per surface. Re-delivering an op already in
243/// the base is idempotent (equal `(hlc, op_id)` never displaces the slot).
244///
245/// Same input contract as [`fold`]: verify (via
246/// [`crate::oplog::verify_log`] / [`crate::checkpoint::verify_anchored`])
247/// before folding untrusted input. One caveat unique to invalid input: the
248/// base keeps only `FoldedRecord`s, so a forged op colliding with a
249/// *base* record's `op_id` cannot use the full-record content tiebreak
250/// [`fold`] applies within one op-set — verification is what makes the
251/// answer mean something.
252pub fn fold_onto(base: &SyncState, ops: &[OpRecord]) -> SyncState {
253    // Idempotence: a retransmitted op (same op_id) folds once. On an id
254    // collision with DIFFERENT content (invalid input — verify_log rejects
255    // it) the tiebreak must not depend on arrival order, so the
256    // lexicographically-smaller canonical serialization wins.
257    let canonical_record = |op: &OpRecord| -> String {
258        canonical_json(&serde_json::to_value(op).expect("OpRecord serializes"))
259    };
260    let mut unique: BTreeMap<&str, &OpRecord> = BTreeMap::new();
261    for op in ops {
262        unique
263            .entry(&op.op_id)
264            .and_modify(|existing| {
265                if *existing != op && canonical_record(op) < canonical_record(existing) {
266                    *existing = op;
267                }
268            })
269            .or_insert(op);
270    }
271
272    let mut state = base.clone();
273
274    // Leased-tier pre-pass (B5): establish the FINAL per-agent `fencing_epoch`
275    // (max over the base and every new intent op) BEFORE the main loop, and
276    // evict base *pending* records that the raised fence makes stale — terminals
277    // (committed/failed) are immune and kept. Deciding pending-fencing against
278    // the final fence (not an intermediate one built up mid-loop) is what keeps
279    // the leased fold order-independent and base-composable
280    // (`fold_onto(checkpoint, tail) == fold(full)`).
281    {
282        let mut agent_max: BTreeMap<String, u64> = BTreeMap::new();
283        for op in unique.values() {
284            if op.surface.fold_tier() == FoldTier::Leased {
285                let slot = agent_max
286                    .entry(crate::lease::intent_agent(&op.payload).to_string())
287                    .or_insert(0);
288                *slot = (*slot).max(crate::lease::intent_epoch(&op.payload));
289            }
290        }
291        for (agent, max_epoch) in agent_max {
292            let entry = state.intents.entry(agent).or_default();
293            if max_epoch > entry.fencing_epoch {
294                entry.fencing_epoch = max_epoch;
295                entry.runs.retain(|_, r| intent_is_terminal(r)); // keep terminals, fence pendings
296            }
297        }
298    }
299
300    for op in unique.values() {
301        let record = FoldedRecord {
302            op_id: op.op_id.clone(),
303            hlc: op.hlc.clone(),
304            payload: op.payload.clone(),
305        };
306        match op.surface.fold_tier() {
307            FoldTier::GrowOnly => {
308                let slot = state
309                    .logs
310                    .entry(op.surface.tag())
311                    .or_default()
312                    .entry(op.fold_key());
313                slot.and_modify(|existing| {
314                    // Immutable-entity union: earliest (hlc, op_id) wins.
315                    // (Unreachable for event-stream surfaces — their fold_key
316                    // IS the op_id, so a collision is the same op.)
317                    if (&record.hlc, &record.op_id) < (&existing.hlc, &existing.op_id) {
318                        *existing = record.clone();
319                    }
320                })
321                .or_insert(record);
322            }
323            FoldTier::Registry => {
324                let slot = state
325                    .registries
326                    .entry(op.surface.tag())
327                    .or_default()
328                    .entry(op.fold_key());
329                slot.and_modify(|existing| {
330                    // LWW: latest (hlc, op_id) wins.
331                    if (&record.hlc, &record.op_id) > (&existing.hlc, &existing.op_id) {
332                        *existing = record.clone();
333                    }
334                })
335                .or_insert(record);
336            }
337            FoldTier::Leased => {
338                // `fencing_epoch` is already final for this agent (pre-pass).
339                let agent = crate::lease::intent_agent(&op.payload).to_string();
340                let epoch = crate::lease::intent_epoch(&op.payload);
341                let key = op.fold_key();
342                let is_terminal = crate::lease::intent_status_rank(&op.payload) == 1;
343                let is_committed = crate::lease::intent_is_committed(&op.payload);
344                let entry = state.intents.entry(agent).or_default();
345
346                // (A) The idempotency ORACLE: grow-only, fence-INDEPENDENT,
347                //     keep-all. A commit is a permanent fact — recorded whatever
348                //     its epoch, never cleared by the fence. Highest
349                //     `(epoch, hlc, op_id)` committed record wins a collision.
350                if is_committed {
351                    let better = entry.committed_runs.get(&key).is_none_or(|existing| {
352                        intent_priority(&record) > intent_priority(existing)
353                    });
354                    if better {
355                        entry.committed_runs.insert(key.clone(), record.clone());
356                    }
357                }
358
359                // (B) The "who holds now" view: terminals are immune, pendings
360                //     are fenced to `fencing_epoch`. Eligible = terminal (always)
361                //     OR a live pending at the fence; below-fence pendings drop.
362                //     The winner is `max` under `intent_priority`, so a terminal
363                //     never reverts to a pending (terminal-immunity) and a
364                //     stale-epoch pending never wins.
365                let eligible = is_terminal || epoch == entry.fencing_epoch;
366                if eligible {
367                    let better = entry.runs.get(&key).is_none_or(|existing| {
368                        intent_priority(&record) > intent_priority(existing)
369                    });
370                    if better {
371                        entry.runs.insert(key, record);
372                    }
373                }
374            }
375        }
376    }
377    state
378}
379
380/// Deterministic content hash of a folded state — the proposal's built-in
381/// divergence invariant: "Same frontier ⇒ same snapshot hash,
382/// deterministically. A mismatch is a fold bug or a non-determinism leak."
383/// B4's checkpoint hash is this value at a frontier.
384pub fn state_hash(state: &SyncState) -> String {
385    let value = serde_json::to_value(state).expect("SyncState serializes");
386    let mut hasher = Sha256::new();
387    hasher.update(canonical_json(&value).as_bytes());
388    let digest = hasher.finalize();
389    let hex: String = digest.iter().take(16).map(|b| format!("{b:02x}")).collect();
390    format!("state-{hex}")
391}
392
393/// Encode an [`Hlc`] as a single `u64` version that preserves the
394/// `(wall_ms, counter)` order — the bridge onto `car_state::crdt`'s
395/// `(version, replica)` total order. 44 bits of wall-clock milliseconds
396/// (good past year 2500) and 20 bits of counter; a counter ≥ 2^20 within one
397/// millisecond is outside the HLC's operating range (B3's clock guarantees
398/// far less) and would break the order-preservation, so it is debug-asserted.
399pub fn hlc_version(hlc: &Hlc) -> u64 {
400    debug_assert!(
401        hlc.counter < (1 << 20),
402        "HLC counter exceeds encoding range"
403    );
404    debug_assert!(
405        hlc.wall_ms < (1 << 44),
406        "HLC wall_ms exceeds encoding range (the << 20 would drop high bits in release)"
407    );
408    (hlc.wall_ms << 20) | (u64::from(hlc.counter) & 0xF_FFFF)
409}
410
411/// Project a folded registry surface onto the shipped
412/// [`car_state::crdt::LwwMap`], so the oplog fold composes with (and is
413/// testably equivalent to) `crdt_merge`/`crdt_export` where the domains
414/// overlap: `fold(union of ops)` ≡ `merge_maps(per-device exports)`.
415pub fn registry_as_lww(state: &SyncState, surface_tag: &str) -> LwwMap {
416    state
417        .registries
418        .get(surface_tag)
419        .map(|records| {
420            records
421                .iter()
422                .map(|(key, rec)| {
423                    (
424                        key.clone(),
425                        LwwRegister::new(
426                            rec.payload.clone(),
427                            hlc_version(&rec.hlc),
428                            rec.hlc.device_id.clone(),
429                        ),
430                    )
431                })
432                .collect()
433        })
434        .unwrap_or_default()
435}
436
437#[cfg(test)]
438mod tests {
439    use super::*;
440    use crate::lease::{Intent, IntentStatus};
441    use crate::oplog::{DeviceLog, Scope, Surface};
442    use serde_json::json;
443
444    /// Append a leased execution-intent op through a device log.
445    fn intent_op(
446        dev: &mut DeviceLog,
447        agent: &str,
448        run: &str,
449        epoch: u64,
450        status: IntentStatus,
451    ) -> OpRecord {
452        dev.append(
453            Scope::Personal,
454            Surface::Intent,
455            Intent::new(agent, run, epoch, status).payload(),
456        )
457    }
458
459    #[test]
460    fn grow_only_unions_by_stable_key() {
461        let mut a = DeviceLog::new("a");
462        let mut b = DeviceLog::new("b");
463        let ops = vec![
464            a.append(
465                Scope::Personal,
466                Surface::Knowledge,
467                json!({"id": "f1", "v": 1}),
468            ),
469            b.append(
470                Scope::Personal,
471                Surface::Knowledge,
472                json!({"id": "f2", "v": 2}),
473            ),
474        ];
475        let state = fold(&ops);
476        let knowledge = &state.logs[&Surface::Knowledge.tag()];
477        assert_eq!(knowledge.len(), 2);
478        assert_eq!(knowledge["id:f1"].payload["v"], json!(1));
479        assert_eq!(knowledge["id:f2"].payload["v"], json!(2));
480    }
481
482    #[test]
483    fn grow_only_key_collision_resolves_to_earliest_deterministically() {
484        // Two devices emit different content under one stable id — an
485        // anomaly for the immutable tier, resolved first-writer-wins.
486        let mut a = DeviceLog::new("a");
487        let mut b = DeviceLog::new("b");
488        let oa = a.append(
489            Scope::Personal,
490            Surface::Knowledge,
491            json!({"id": "f", "v": "a"}),
492        );
493        b.observe(&oa.hlc); // b writes causally later
494        let ob = b.append(
495            Scope::Personal,
496            Surface::Knowledge,
497            json!({"id": "f", "v": "b"}),
498        );
499        let fwd = fold(&[oa.clone(), ob.clone()]);
500        let rev = fold(&[ob, oa]);
501        assert_eq!(fwd, rev);
502        assert_eq!(
503            fwd.logs[&Surface::Knowledge.tag()]["id:f"].payload["v"],
504            json!("a")
505        );
506    }
507
508    #[test]
509    fn registry_is_lww_per_record_not_per_file() {
510        let mut a = DeviceLog::new("a");
511        let mut b = DeviceLog::new("b");
512        // Concurrent edits to DIFFERENT agents both survive.
513        let oa = a.append(
514            Scope::Personal,
515            Surface::Declagent,
516            json!({"id": "x", "owner": "a"}),
517        );
518        let ob = b.append(
519            Scope::Personal,
520            Surface::Declagent,
521            json!({"id": "y", "owner": "b"}),
522        );
523        // Concurrent edits to the SAME agent resolve by HLC.
524        b.observe(&oa.hlc);
525        let ob2 = b.append(
526            Scope::Personal,
527            Surface::Declagent,
528            json!({"id": "x", "owner": "b"}),
529        );
530        let state = fold(&[oa, ob, ob2]);
531        let reg = &state.registries[&Surface::Declagent.tag()];
532        assert_eq!(reg.len(), 2, "both records survive");
533        assert_eq!(
534            reg["id:x"].payload["owner"],
535            json!("b"),
536            "later HLC wins the shared record"
537        );
538        assert_eq!(reg["id:y"].payload["owner"], json!("b"));
539    }
540
541    #[test]
542    fn registry_concurrent_tie_breaks_on_device_deterministically() {
543        // Same lamport stamp on two devices (true concurrency): the HLC's
544        // device_id component breaks the tie, both fold orders agree.
545        let mut a = DeviceLog::new("a");
546        let mut b = DeviceLog::new("b");
547        let oa = a.append(
548            Scope::Personal,
549            Surface::Declagent,
550            json!({"id": "x", "owner": "a"}),
551        );
552        let ob = b.append(
553            Scope::Personal,
554            Surface::Declagent,
555            json!({"id": "x", "owner": "b"}),
556        );
557        assert_eq!(oa.hlc.wall_ms, ob.hlc.wall_ms);
558        let fwd = fold(&[oa.clone(), ob.clone()]);
559        let rev = fold(&[ob, oa]);
560        assert_eq!(fwd, rev);
561        // "b" > "a" in the device tiebreak — matches crdt's replica tiebreak.
562        assert_eq!(
563            fwd.registries[&Surface::Declagent.tag()]["id:x"].payload["owner"],
564            json!("b")
565        );
566    }
567
568    #[test]
569    fn state_hash_detects_divergence_and_agrees_on_convergence() {
570        let mut a = DeviceLog::new("a");
571        let o1 = a.append(Scope::Personal, Surface::Knowledge, json!({"id": "f1"}));
572        let o2 = a.append(Scope::Personal, Surface::Knowledge, json!({"id": "f2"}));
573        let h_full = state_hash(&fold(&[o1.clone(), o2.clone()]));
574        let h_full_again = state_hash(&fold(&[o2.clone(), o1.clone()]));
575        assert_eq!(h_full, h_full_again, "same op-set → same hash");
576        let h_partial = state_hash(&fold(&[o1]));
577        assert_ne!(h_full, h_partial, "different op-set → different hash");
578        assert!(h_full.starts_with("state-"));
579    }
580
581    #[test]
582    fn hlc_version_preserves_order() {
583        let stamps = [
584            Hlc {
585                wall_ms: 1,
586                counter: 0,
587                device_id: "a".into(),
588            },
589            Hlc {
590                wall_ms: 1,
591                counter: 1,
592                device_id: "a".into(),
593            },
594            Hlc {
595                wall_ms: 2,
596                counter: 0,
597                device_id: "a".into(),
598            },
599        ];
600        for w in stamps.windows(2) {
601            assert!(hlc_version(&w[0]) < hlc_version(&w[1]));
602        }
603    }
604
605    #[test]
606    fn registry_as_lww_matches_crdt_merge_including_export_shape() {
607        // The equivalence the proposal leans on: per-device exports merged
608        // with the shipped crdt primitives == the fold of the op union.
609        let mut a = DeviceLog::new("dev-a");
610        let mut b = DeviceLog::new("dev-b");
611        let oa1 = a.append(
612            Scope::Personal,
613            Surface::Registry {
614                kind: "agents".into(),
615            },
616            json!({"id": "r1", "v": "a"}),
617        );
618        let oa2 = a.append(
619            Scope::Personal,
620            Surface::Registry {
621                kind: "agents".into(),
622            },
623            json!({"id": "r2", "v": "a"}),
624        );
625        b.observe(&oa1.hlc);
626        b.observe(&oa2.hlc);
627        let ob1 = b.append(
628            Scope::Personal,
629            Surface::Registry {
630                kind: "agents".into(),
631            },
632            json!({"id": "r1", "v": "b"}),
633        );
634
635        let tag = Surface::Registry {
636            kind: "agents".into(),
637        }
638        .tag();
639        let union = registry_as_lww(&fold(&[oa1.clone(), oa2.clone(), ob1.clone()]), &tag);
640        let export_a = registry_as_lww(&fold(&[oa1, oa2]), &tag);
641        let export_b = registry_as_lww(&fold(&[ob1]), &tag);
642
643        assert_eq!(car_state::crdt::merge_maps(&export_a, &export_b), union);
644        assert_eq!(car_state::crdt::merge_many(&[export_b, export_a]), union);
645        let plain = car_state::crdt::materialize(&union);
646        assert_eq!(plain["id:r1"]["v"], json!("b"));
647        assert_eq!(plain["id:r2"]["v"], json!("a"));
648    }
649
650    #[test]
651    fn log_entries_are_hlc_ordered() {
652        let mut a = DeviceLog::new("a");
653        let mut b = DeviceLog::new("b");
654        let o1 = a.append(
655            Scope::Personal,
656            Surface::Conversation,
657            json!({"t": "first"}),
658        );
659        b.observe(&o1.hlc);
660        let o2 = b.append(
661            Scope::Personal,
662            Surface::Conversation,
663            json!({"t": "second"}),
664        );
665        a.observe(&o2.hlc); // a's next write causally follows b's
666        let o3 = a.append(
667            Scope::Personal,
668            Surface::Conversation,
669            json!({"t": "third"}),
670        );
671        // Deliver out of order; the view is canonical.
672        let state = fold(&[o3, o1, o2]);
673        let texts: Vec<&Value> = state
674            .log_entries(&Surface::Conversation.tag())
675            .iter()
676            .map(|r| &r.payload["t"])
677            .collect();
678        assert_eq!(
679            texts,
680            vec![&json!("first"), &json!("second"), &json!("third")]
681        );
682    }
683
684    #[test]
685    fn routing_observations_fold_as_a_multiset() {
686        // The demonstrated kernel-review defect: "agent x succeeded" twice is
687        // TWO observations. Under content-keyed dedup the second collapsed
688        // into the first (1 entry, EMA 0.65); the proposal requires the
689        // merged MULTISET (2 entries, EMA 0.755).
690        let mut dev = DeviceLog::new("dev-a");
691        let ops = vec![
692            dev.append(Scope::Personal, Surface::Routing, json!({"sample": 1.0})),
693            dev.append(Scope::Personal, Surface::Routing, json!({"sample": 1.0})),
694        ];
695        let state = fold(&ops);
696        assert_eq!(
697            state.log_entries(&Surface::Routing.tag()).len(),
698            2,
699            "two byte-identical observations are two events"
700        );
701        let ema =
702            |s: f64, rec: &FoldedRecord| 0.7 * s + 0.3 * rec.payload["sample"].as_f64().unwrap();
703        let value = state.replay(&Surface::Routing.tag(), 0.5_f64, ema);
704        assert!(
705            (value - 0.755).abs() < 1e-12,
706            "EMA over both events: got {value}"
707        );
708    }
709
710    #[test]
711    fn logical_entity_surfaces_dedup_identical_content() {
712        // Content-keyed dedup is scoped to logical-ENTITY surfaces (knowledge,
713        // skills, …): the same fact emitted identically by two devices is ONE
714        // entity. (Conversation is NOT one of these — it's an event stream
715        // keyed by op_id — see the conversation module's CRIT-2 tests.)
716        let mut a = DeviceLog::new("a");
717        let mut b = DeviceLog::new("b");
718        let fact = json!({"kind": "note", "body": "the sky is blue"});
719        let oa = a.append(Scope::Personal, Surface::Knowledge, fact.clone());
720        let ob = b.append(Scope::Personal, Surface::Knowledge, fact);
721        let state = fold(&[oa, ob]);
722        assert_eq!(state.log_entries(&Surface::Knowledge.tag()).len(), 1);
723    }
724
725    #[test]
726    fn forged_colliding_op_id_dedups_order_independently() {
727        // Invalid input (verify_log rejects it), but the fold must stay
728        // order-independent: two records claiming the SAME op_id with
729        // DIFFERENT content tiebreak on content, not arrival order.
730        let mut dev = DeviceLog::new("d1");
731        let genuine = dev.append(
732            Scope::Personal,
733            Surface::Knowledge,
734            json!({"id": "f", "v": 1}),
735        );
736        let mut forged = genuine.clone();
737        forged.payload = json!({"id": "f", "v": 2}); // op_id NOT recomputed
738        assert!(crate::oplog::verify_log(&[forged.clone()]).is_err());
739
740        let ab = fold(&[genuine.clone(), forged.clone()]);
741        let ba = fold(&[forged, genuine]);
742        assert_eq!(
743            ab, ba,
744            "colliding-id dedup must not depend on arrival order"
745        );
746        assert_eq!(state_hash(&ab), state_hash(&ba));
747    }
748
749    #[test]
750    fn fold_onto_prefix_fold_equals_full_fold() {
751        // The B4 primitive: fold(prefix) then fold_onto(., suffix) must be
752        // byte-identical to fold(prefix ∪ suffix) — for every fold rule at
753        // once, including a grow-only collision and an LWW overwrite that
754        // CROSS the split point.
755        let mut a = DeviceLog::new("a");
756        let mut b = DeviceLog::new("b");
757        let prefix = vec![
758            a.append(
759                Scope::Personal,
760                Surface::Knowledge,
761                json!({"id": "f", "v": "old"}),
762            ),
763            a.append(
764                Scope::Personal,
765                Surface::Declagent,
766                json!({"id": "x", "owner": "a"}),
767            ),
768            a.append(Scope::Personal, Surface::Routing, json!({"sample": 1.0})),
769        ];
770        for op in &prefix {
771            b.observe(&op.hlc);
772        }
773        let suffix = vec![
774            // Grow-only collision across the split: earliest wins → "old".
775            b.append(
776                Scope::Personal,
777                Surface::Knowledge,
778                json!({"id": "f", "v": "new"}),
779            ),
780            // LWW across the split: latest wins → owner "b".
781            b.append(
782                Scope::Personal,
783                Surface::Declagent,
784                json!({"id": "x", "owner": "b"}),
785            ),
786            // Event stream across the split: both observations survive.
787            b.append(Scope::Personal, Surface::Routing, json!({"sample": 0.0})),
788        ];
789        let mut full = prefix.clone();
790        full.extend(suffix.iter().cloned());
791
792        let via_base = fold_onto(&fold(&prefix), &suffix);
793        assert_eq!(via_base, fold(&full));
794        assert_eq!(state_hash(&via_base), state_hash(&fold(&full)));
795        assert_eq!(
796            via_base.logs[&Surface::Knowledge.tag()]["id:f"].payload["v"],
797            json!("old")
798        );
799        assert_eq!(
800            via_base.registries[&Surface::Declagent.tag()]["id:x"].payload["owner"],
801            json!("b")
802        );
803        assert_eq!(via_base.log_entries(&Surface::Routing.tag()).len(), 2);
804
805        // Idempotent re-delivery: folding an op already in the base changes
806        // nothing.
807        assert_eq!(fold_onto(&via_base, &prefix), via_base);
808    }
809
810    #[test]
811    fn empty_fold_is_empty_and_stable() {
812        let state = fold(&[]);
813        assert_eq!(state, SyncState::default());
814        assert_eq!(state_hash(&state), state_hash(&fold(&[])));
815        assert!(state.log_entries("conversation").is_empty());
816        assert!(registry_as_lww(&state, "declagent").is_empty());
817        assert!(state.intent("milo", "R").is_none());
818        assert!(state.fencing_epoch("milo").is_none());
819    }
820
821    // ------------------------------------------------------------------
822    // B5: leased execution-intent fencing as a deterministic fold property.
823    // ------------------------------------------------------------------
824
825    #[test]
826    fn intent_fold_fences_stale_epoch_order_independently() {
827        // Failover: dev-a held epoch 1, dev-b stole epoch 2. Both fire the
828        // SAME run R — a=zombie, b=legit holder. The fold must pick epoch 2
829        // (b) in ANY delivery order and fence a's epoch-1 writes, leaving one
830        // ledger record — no double-commit.
831        let mut a = DeviceLog::new("dev-a");
832        let mut b = DeviceLog::new("dev-b");
833        let ops = vec![
834            intent_op(&mut a, "milo", "R", 1, IntentStatus::Pending),
835            intent_op(&mut a, "milo", "R", 1, IntentStatus::Committed),
836            intent_op(&mut b, "milo", "R", 2, IntentStatus::Pending),
837            intent_op(&mut b, "milo", "R", 2, IntentStatus::Committed),
838        ];
839        let b_commit = ops[3].clone();
840
841        let baseline = fold(&ops);
842        assert_eq!(baseline.fencing_epoch("milo"), Some(2));
843        assert_eq!(
844            baseline.intents["milo"].runs.len(),
845            1,
846            "single record — no double-commit"
847        );
848        let winner = baseline.intent("milo", "R").expect("R survives");
849        let decoded = Intent::from_payload(&winner.payload).unwrap();
850        assert_eq!(
851            (decoded.epoch, decoded.status),
852            (2, IntentStatus::Committed)
853        );
854        assert_eq!(
855            winner.op_id, b_commit.op_id,
856            "the current holder's commit wins"
857        );
858
859        // Order-independence: several explicit permutations agree exactly.
860        for order in [
861            vec![
862                ops[3].clone(),
863                ops[2].clone(),
864                ops[1].clone(),
865                ops[0].clone(),
866            ],
867            vec![
868                ops[2].clone(),
869                ops[0].clone(),
870                ops[3].clone(),
871                ops[1].clone(),
872            ],
873            vec![
874                ops[1].clone(),
875                ops[3].clone(),
876                ops[0].clone(),
877                ops[2].clone(),
878            ],
879        ] {
880            assert_eq!(fold(&order), baseline);
881            assert_eq!(state_hash(&fold(&order)), state_hash(&baseline));
882        }
883    }
884
885    #[test]
886    fn intent_per_agent_pending_fencing_with_committed_immunity() {
887        // Per-AGENT fencing applies to PENDINGS: dev-a (epoch 1) has an
888        // unshared PENDING run S that dev-b (epoch 2) never touched → S's
889        // pending is fenced (a stale holder's intent-to-do is silenced). But a
890        // COMMITTED run is terminal-immune — a commit is a fact, not fenced —
891        // so the zombie's unshared committed run K survives (the C1 fix: an
892        // unrelated higher-epoch run must not evict it).
893        let mut a = DeviceLog::new("dev-a");
894        let mut b = DeviceLog::new("dev-b");
895        let ops = vec![
896            intent_op(&mut a, "milo", "S", 1, IntentStatus::Pending), // unshared zombie pending
897            intent_op(&mut a, "milo", "K", 1, IntentStatus::Committed), // unshared zombie commit
898            intent_op(&mut b, "milo", "T", 2, IntentStatus::Committed), // new holder, unrelated run
899        ];
900        let state = fold(&ops);
901        assert_eq!(state.fencing_epoch("milo"), Some(2));
902        // The unshared PENDING is fenced; it never committed.
903        assert!(
904            state.intent("milo", "S").is_none(),
905            "unshared zombie pending is fenced"
906        );
907        assert!(
908            state.committed_run("milo", "S").is_none(),
909            "S never committed"
910        );
911        // The unshared COMMITTED run survives the unrelated epoch bump (C1).
912        assert!(
913            state.committed_run("milo", "K").is_some(),
914            "committed run survives an unrelated epoch bump (idempotency oracle)"
915        );
916        assert!(
917            state.intent("milo", "K").is_some(),
918            "committed is terminal-immune in runs too"
919        );
920        assert!(state.committed_run("milo", "T").is_some());
921        // A different agent is a different fencing group.
922        let mut c = DeviceLog::new("dev-c");
923        let mixed = {
924            let mut v = ops.clone();
925            v.push(intent_op(&mut c, "other", "U", 1, IntentStatus::Committed));
926            v
927        };
928        assert!(
929            fold(&mixed).committed_run("other", "U").is_some(),
930            "fencing does not cross agents"
931        );
932    }
933
934    #[test]
935    fn intent_fold_onto_equals_full_fold_across_an_epoch_bump() {
936        // The compaction-safety equivalence for the leased tier across an epoch
937        // bump: a checkpoint captured the agent at epoch 1 (COMMITTED run R). A
938        // later, higher-epoch tail op (run S @ 2) raises the fence — and R,
939        // being committed, is terminal-immune and SURVIVES (the C1/C3 fix; a
940        // commit is a durable fact, not evicted by an unrelated bump). The
941        // fold_onto == fold equivalence still holds exactly.
942        let mut a = DeviceLog::new("dev-a");
943        let mut b = DeviceLog::new("dev-b");
944        let prefix = vec![intent_op(&mut a, "milo", "R", 1, IntentStatus::Committed)];
945        for op in &prefix {
946            b.observe(&op.hlc);
947        }
948        let tail = vec![intent_op(&mut b, "milo", "S", 2, IntentStatus::Committed)];
949        let full = {
950            let mut v = prefix.clone();
951            v.extend(tail.iter().cloned());
952            v
953        };
954
955        let base = fold(&prefix); // the "checkpoint" state: epoch 1, R committed
956        assert_eq!(base.fencing_epoch("milo"), Some(1));
957        assert!(base.committed_run("milo", "R").is_some());
958
959        let via_base = fold_onto(&base, &tail);
960        assert_eq!(
961            via_base,
962            fold(&full),
963            "fold_onto == fold across the epoch bump"
964        );
965        assert_eq!(state_hash(&via_base), state_hash(&fold(&full)));
966        assert_eq!(via_base.fencing_epoch("milo"), Some(2));
967        // R committed@1 SURVIVES the bump in both views (terminal-immune / oracle).
968        assert!(
969            via_base.committed_run("milo", "R").is_some(),
970            "committed R survives the epoch bump in the idempotency oracle"
971        );
972        assert!(
973            via_base.intent("milo", "R").is_some(),
974            "committed R is terminal-immune in runs"
975        );
976        assert!(via_base.committed_run("milo", "S").is_some());
977        // Idempotent re-delivery of the tail changes nothing.
978        assert_eq!(fold_onto(&via_base, &tail), via_base);
979    }
980
981    #[test]
982    fn intent_fencing_beats_a_later_hlc() {
983        // Safety is by EPOCH, not wall clock: a zombie op with a LATER hlc but
984        // a LOWER epoch still loses to the higher-epoch op — no wall-clock race.
985        let mut cloud = DeviceLog::new("cloud");
986        let mut zombie = DeviceLog::new("laptop");
987        let c = intent_op(&mut cloud, "milo", "R", 2, IntentStatus::Committed);
988        zombie.observe(&c.hlc); // the zombie's later write stamps a HIGHER hlc
989        let z = intent_op(&mut zombie, "milo", "R", 1, IntentStatus::Committed);
990        assert!(z.hlc > c.hlc, "the zombie op is later in HLC");
991
992        let state = fold(&[c.clone(), z]);
993        let winner = state.intent("milo", "R").unwrap();
994        assert_eq!(winner.op_id, c.op_id, "higher epoch wins despite lower HLC");
995        assert_eq!(state.fencing_epoch("milo"), Some(2));
996    }
997
998    #[test]
999    fn idempotent_run_under_failover_uses_the_same_deterministic_run_id() {
1000        // B7 tie-in: two sites computing the same scheduled occurrence derive
1001        // the SAME run_id, so a failed-over holder and a zombie collapse to ONE
1002        // ledger record; epoch fencing then picks the legit (epoch-2) winner.
1003        let run_id = car_proto::deterministic_run_id("milo", "3am digest", "2026-07-02T03:00");
1004        assert_eq!(
1005            run_id,
1006            car_proto::deterministic_run_id("milo", "3am digest", "2026-07-02T03:00"),
1007            "same occurrence → same run_id"
1008        );
1009        let mut zombie = DeviceLog::new("laptop");
1010        let mut cloud = DeviceLog::new("cloud");
1011        let z = intent_op(&mut zombie, "milo", &run_id, 1, IntentStatus::Committed);
1012        let c = intent_op(&mut cloud, "milo", &run_id, 2, IntentStatus::Committed);
1013
1014        let state = fold(&[z, c.clone()]);
1015        assert_eq!(
1016            state.intents["milo"].runs.len(),
1017            1,
1018            "exactly one execution record"
1019        );
1020        assert_eq!(
1021            state.intent("milo", &run_id).unwrap().op_id,
1022            c.op_id,
1023            "the epoch-2 holder's run wins; the zombie is a no-op"
1024        );
1025    }
1026
1027    #[test]
1028    fn intent_fold_is_order_independent_over_every_permutation() {
1029        // Brute-force the redesigned leased fold (pre-pass + terminal-immunity
1030        // + committed oracle): a 5-op set mixing committed/pending across two
1031        // epochs and three runs must fold IDENTICALLY in all 120 orders.
1032        fn permutations<T: Clone>(items: &[T]) -> Vec<Vec<T>> {
1033            fn heap<T: Clone>(k: usize, arr: &mut Vec<T>, out: &mut Vec<Vec<T>>) {
1034                if k == 1 {
1035                    out.push(arr.clone());
1036                    return;
1037                }
1038                for i in 0..k {
1039                    heap(k - 1, arr, out);
1040                    if k.is_multiple_of(2) {
1041                        arr.swap(i, k - 1);
1042                    } else {
1043                        arr.swap(0, k - 1);
1044                    }
1045                }
1046            }
1047            let mut arr = items.to_vec();
1048            let mut out = Vec::new();
1049            heap(arr.len(), &mut arr, &mut out);
1050            out
1051        }
1052
1053        let mut a = DeviceLog::new("a");
1054        let mut b = DeviceLog::new("b");
1055        let ops = vec![
1056            intent_op(&mut a, "milo", "R", 1, IntentStatus::Committed), // terminal-immune across bump
1057            intent_op(&mut a, "milo", "S", 1, IntentStatus::Pending),   // unshared pending → fenced
1058            intent_op(&mut a, "milo", "T", 1, IntentStatus::Committed), // unshared committed → survives
1059            intent_op(&mut b, "milo", "R", 2, IntentStatus::Pending),   // C3: must not revert R
1060            intent_op(&mut b, "milo", "S", 2, IntentStatus::Committed), // S commits at the higher epoch
1061        ];
1062        let baseline = fold(&ops);
1063        // Expected steady state.
1064        assert_eq!(baseline.fencing_epoch("milo"), Some(2));
1065        let mut committed = baseline.committed_run_ids("milo");
1066        committed.sort();
1067        assert_eq!(
1068            committed,
1069            vec!["R", "S", "T"],
1070            "the oracle keeps every committed run"
1071        );
1072        assert!(baseline
1073            .intent("milo", "S")
1074            .map(|r| r.op_id.clone())
1075            .is_some_and(|_| {
1076                Intent::from_payload(&baseline.intent("milo", "S").unwrap().payload)
1077                    .unwrap()
1078                    .status
1079                    == IntentStatus::Committed
1080            }));
1081        assert_eq!(
1082            Intent::from_payload(&baseline.intent("milo", "R").unwrap().payload)
1083                .unwrap()
1084                .status,
1085            IntentStatus::Committed,
1086            "R is not reverted to pending"
1087        );
1088
1089        for perm in permutations(&ops) {
1090            assert_eq!(
1091                fold(&perm),
1092                baseline,
1093                "leased fold must be order-independent"
1094            );
1095            assert_eq!(state_hash(&fold(&perm)), state_hash(&baseline));
1096        }
1097    }
1098
1099    #[test]
1100    fn c1_committed_run_survives_an_unrelated_higher_epoch_run() {
1101        // C1 REPRO: run R commits at epoch 1; later an UNRELATED run T lands at
1102        // epoch 2 for the same agent (no concurrency). The old fold cleared
1103        // runs on the bump, so intent(R) → None → the idempotency check said
1104        // "not run" → double-execution. FIX: the fence-INDEPENDENT
1105        // committed_run oracle answers correctly regardless of the bump.
1106        let mut a = DeviceLog::new("dev-a");
1107        let mut b = DeviceLog::new("dev-b");
1108        let r_commit = intent_op(&mut a, "milo", "R", 1, IntentStatus::Committed);
1109        b.observe(&r_commit.hlc);
1110        let t_pending = intent_op(&mut b, "milo", "T", 2, IntentStatus::Pending);
1111
1112        let state = fold(&[r_commit.clone(), t_pending]);
1113        assert_eq!(
1114            state.fencing_epoch("milo"),
1115            Some(2),
1116            "the unrelated run bumped the fence"
1117        );
1118        // The oracle still says R committed — the correct idempotency answer.
1119        assert_eq!(
1120            state.committed_run("milo", "R").unwrap().op_id,
1121            r_commit.op_id,
1122            "committed_run(R) survives the unrelated epoch bump (C1 fixed)"
1123        );
1124        assert_eq!(state.committed_run_ids("milo"), vec!["R"]);
1125    }
1126
1127    #[test]
1128    fn c3_committed_then_pending_across_a_bump_stays_committed() {
1129        // C3 REPRO: R commits at epoch 1; a failed-over holder writes R PENDING
1130        // at epoch 2 (before checking). The old Greater arm unconditionally
1131        // cleared, reverting R to pending → looked un-run → double-execute.
1132        // FIX: terminal-immunity — the fold keeps committed for R in BOTH views,
1133        // in any delivery order.
1134        let mut orig = DeviceLog::new("orig");
1135        let mut failover = DeviceLog::new("failover");
1136        let committed = intent_op(&mut orig, "milo", "R", 1, IntentStatus::Committed);
1137        failover.observe(&committed.hlc);
1138        let late_pending = intent_op(&mut failover, "milo", "R", 2, IntentStatus::Pending);
1139        assert!(
1140            late_pending.hlc > committed.hlc,
1141            "the pending is even later in HLC"
1142        );
1143
1144        for order in [
1145            vec![committed.clone(), late_pending.clone()],
1146            vec![late_pending.clone(), committed.clone()],
1147        ] {
1148            let state = fold(&order);
1149            // Oracle: committed, unconditionally.
1150            assert_eq!(
1151                state.committed_run("milo", "R").unwrap().op_id,
1152                committed.op_id,
1153                "committed stays committed across the bump (oracle)"
1154            );
1155            // who-holds view: terminal-immune, still committed (not reverted).
1156            let decoded =
1157                Intent::from_payload(&state.intent("milo", "R").unwrap().payload).unwrap();
1158            assert_eq!(
1159                decoded.status,
1160                IntentStatus::Committed,
1161                "runs view is not reverted to pending"
1162            );
1163        }
1164    }
1165}