Skip to main content

lean_ctx/core/ocla/
capsule.rs

1use std::collections::{HashMap, HashSet};
2use std::sync::atomic::{AtomicU64, Ordering};
3use std::sync::{Arc, OnceLock, RwLock};
4use std::time::{Instant, SystemTime, UNIX_EPOCH};
5
6use serde::{Deserialize, Serialize};
7
8use super::types::{OclaError, OclaResult};
9
10static NEXT_FORK_ID: AtomicU64 = AtomicU64::new(1);
11static NEXT_SNAPSHOT_ID: AtomicU64 = AtomicU64::new(1);
12static GLOBAL_CAPSULE_STORE: OnceLock<CapsuleStore> = OnceLock::new();
13
14#[must_use]
15pub fn global_capsule_store() -> &'static CapsuleStore {
16    GLOBAL_CAPSULE_STORE.get_or_init(CapsuleStore::new)
17}
18
19#[derive(Clone, Debug, Eq, PartialEq)]
20pub struct Delta {
21    pub offset: usize,
22    pub data: Vec<u8>,
23}
24
25/// A point-in-time snapshot of a capsule for rollback.
26#[derive(Clone, Debug, Serialize, Deserialize)]
27pub struct CapsuleSnapshot {
28    pub snapshot_id: String,
29    pub capsule_ref: String,
30    pub content: Vec<u8>,
31    pub delta_count: usize,
32    pub created_at_ms: u64,
33}
34
35/// A compressed change set for transferring a capsule between agents.
36#[derive(Clone, Debug, Serialize, Deserialize)]
37pub struct HandoffDelta {
38    pub from_ref: String,
39    pub to_ref: String,
40    pub operations: Vec<DeltaOp>,
41    pub compressed_size: usize,
42    pub original_size: usize,
43}
44
45/// A single operation in a [`HandoffDelta`].
46#[derive(Clone, Debug, Serialize, Deserialize)]
47pub enum DeltaOp {
48    Keep { offset: usize, len: usize },
49    Insert { offset: usize, data: Vec<u8> },
50    Delete { offset: usize, len: usize },
51}
52
53#[derive(Clone, Debug)]
54pub struct CapsuleEntry {
55    pub parent_ref: Option<String>,
56    pub data: Vec<u8>,
57    pub deltas: Vec<Delta>,
58    pub budget_tokens: u64,
59    pub created_at: Instant,
60}
61
62#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
63pub struct CapsuleStats {
64    pub total_entries: usize,
65    pub total_bytes: usize,
66    pub max_depth: usize,
67}
68
69#[derive(Clone, Debug, Default)]
70struct CapsuleStoreState {
71    entries: HashMap<String, CapsuleEntry>,
72    snapshots: Vec<CapsuleSnapshot>,
73}
74
75#[derive(Clone, Debug, Default)]
76pub struct CapsuleStore {
77    state: Arc<RwLock<CapsuleStoreState>>,
78}
79
80impl CapsuleStore {
81    #[must_use]
82    pub fn new() -> Self {
83        Self::default()
84    }
85
86    pub fn register(&self, data: &[u8]) -> String {
87        let capsule_ref = format!("capsule:{}", blake3::hash(data).to_hex());
88        let entry = CapsuleEntry {
89            parent_ref: None,
90            data: data.to_vec(),
91            deltas: Vec::new(),
92            budget_tokens: 0,
93            created_at: Instant::now(),
94        };
95        if let Ok(mut state) = self.state.write() {
96            state.entries.insert(capsule_ref.clone(), entry);
97        }
98        capsule_ref
99    }
100
101    pub fn fork(&self, parent_ref: &str, budget_tokens: u64) -> OclaResult<String> {
102        let mut state = self
103            .state
104            .write()
105            .map_err(|_| invalid("capsule store lock poisoned"))?;
106        if !state.entries.contains_key(parent_ref) {
107            return Err(invalid(format!("unknown parent capsule: {parent_ref}")));
108        }
109
110        let fork_id = NEXT_FORK_ID.fetch_add(1, Ordering::Relaxed);
111        let identity = format!("{parent_ref}\0{budget_tokens}\0{fork_id}");
112        let capsule_ref = format!("capsule:{}", blake3::hash(identity.as_bytes()).to_hex());
113        state.entries.insert(
114            capsule_ref.clone(),
115            CapsuleEntry {
116                parent_ref: Some(parent_ref.to_string()),
117                data: Vec::new(),
118                deltas: Vec::new(),
119                budget_tokens,
120                created_at: Instant::now(),
121            },
122        );
123        Ok(capsule_ref)
124    }
125
126    pub fn resolve(&self, capsule_ref: &str) -> OclaResult<Vec<u8>> {
127        let state = self
128            .state
129            .read()
130            .map_err(|_| invalid("capsule store lock poisoned"))?;
131        resolve_entries(&state.entries, capsule_ref)
132    }
133
134    #[cfg(test)]
135    pub(crate) fn budget_tokens(&self, capsule_ref: &str) -> OclaResult<u64> {
136        let state = self
137            .state
138            .read()
139            .map_err(|_| invalid("capsule store lock poisoned"))?;
140        state
141            .entries
142            .get(capsule_ref)
143            .map(|entry| entry.budget_tokens)
144            .ok_or_else(|| invalid(format!("unknown capsule: {capsule_ref}")))
145    }
146
147    pub fn apply_delta(&self, capsule_ref: &str, delta: Delta) -> OclaResult<()> {
148        let mut state = self
149            .state
150            .write()
151            .map_err(|_| invalid("capsule store lock poisoned"))?;
152        let current = resolve_entries(&state.entries, capsule_ref)?;
153        let entry = state
154            .entries
155            .get_mut(capsule_ref)
156            .ok_or_else(|| invalid(format!("unknown capsule: {capsule_ref}")))?;
157        if entry.parent_ref.is_none() {
158            return Err(invalid("deltas can only be applied to forked capsules"));
159        }
160        if delta.offset > current.len() {
161            return Err(invalid("capsule delta starts beyond materialized content"));
162        }
163        entry.deltas.push(delta);
164        Ok(())
165    }
166
167    pub fn merge_back(&self, child_ref: &str) -> OclaResult<()> {
168        let mut state = self
169            .state
170            .write()
171            .map_err(|_| invalid("capsule store lock poisoned"))?;
172        resolve_entries(&state.entries, child_ref)?;
173        let (parent_ref, deltas) = {
174            let child = state
175                .entries
176                .get(child_ref)
177                .ok_or_else(|| invalid(format!("unknown capsule: {child_ref}")))?;
178            (
179                child
180                    .parent_ref
181                    .clone()
182                    .ok_or_else(|| invalid("root capsules cannot merge back"))?,
183                child.deltas.clone(),
184            )
185        };
186        let parent = state
187            .entries
188            .get_mut(&parent_ref)
189            .ok_or_else(|| invalid(format!("unknown parent capsule: {parent_ref}")))?;
190        parent.deltas.extend(deltas);
191        state
192            .entries
193            .get_mut(child_ref)
194            .ok_or_else(|| invalid(format!("unknown capsule: {child_ref}")))?
195            .deltas
196            .clear();
197        Ok(())
198    }
199
200    /// Creates a snapshot of the current capsule state for potential rollback.
201    pub fn snapshot(&self, capsule_ref: &str) -> OclaResult<CapsuleSnapshot> {
202        let mut state = self
203            .state
204            .write()
205            .map_err(|_| invalid("capsule store lock poisoned"))?;
206        let content = resolve_entries(&state.entries, capsule_ref)?;
207        let delta_count = state
208            .entries
209            .get(capsule_ref)
210            .ok_or_else(|| invalid(format!("unknown capsule: {capsule_ref}")))?
211            .deltas
212            .len();
213        let snapshot_number = NEXT_SNAPSHOT_ID.fetch_add(1, Ordering::Relaxed);
214        let snapshot_id = format!(
215            "snapshot:{}",
216            blake3::hash(format!("{capsule_ref}\\0{snapshot_number}").as_bytes()).to_hex()
217        );
218        let snapshot = CapsuleSnapshot {
219            snapshot_id,
220            capsule_ref: capsule_ref.to_string(),
221            content,
222            delta_count,
223            created_at_ms: unix_time_ms(),
224        };
225        state.snapshots.push(snapshot.clone());
226        Ok(snapshot)
227    }
228
229    /// Rolls back a capsule to a previous snapshot.
230    pub fn rollback(&self, capsule_ref: &str, snapshot_id: &str) -> OclaResult<()> {
231        let mut state = self
232            .state
233            .write()
234            .map_err(|_| invalid("capsule store lock poisoned"))?;
235        let snapshot = state
236            .snapshots
237            .iter()
238            .find(|snapshot| {
239                snapshot.snapshot_id == snapshot_id && snapshot.capsule_ref == capsule_ref
240            })
241            .cloned()
242            .ok_or_else(|| invalid(format!("unknown capsule snapshot: {snapshot_id}")))?;
243        let entry = state
244            .entries
245            .get_mut(capsule_ref)
246            .ok_or_else(|| invalid(format!("unknown capsule: {capsule_ref}")))?;
247        entry.parent_ref = None;
248        entry.data = snapshot.content;
249        entry.deltas.clear();
250        Ok(())
251    }
252
253    /// Computes a compressed delta between two capsule versions.
254    pub fn compute_handoff_delta(&self, from_ref: &str, to_ref: &str) -> OclaResult<HandoffDelta> {
255        let state = self
256            .state
257            .read()
258            .map_err(|_| invalid("capsule store lock poisoned"))?;
259        let from = resolve_entries(&state.entries, from_ref)?;
260        let to = resolve_entries(&state.entries, to_ref)?;
261        let operations = compute_delta_operations(&from, &to);
262        Ok(HandoffDelta {
263            from_ref: from_ref.to_string(),
264            to_ref: to_ref.to_string(),
265            compressed_size: handoff_delta_size(&operations),
266            original_size: to.len(),
267            operations,
268        })
269    }
270
271    /// Applies a handoff delta to create a new capsule version.
272    pub fn apply_handoff_delta(&self, base_ref: &str, delta: &HandoffDelta) -> OclaResult<String> {
273        if delta.from_ref != base_ref {
274            return Err(invalid("handoff delta base reference does not match"));
275        }
276        let mut state = self
277            .state
278            .write()
279            .map_err(|_| invalid("capsule store lock poisoned"))?;
280        let base = resolve_entries(&state.entries, base_ref)?;
281        let content = apply_handoff_operations(&base, &delta.operations)?;
282        let handoff_id = NEXT_FORK_ID.fetch_add(1, Ordering::Relaxed);
283        let identity = format!(
284            "handoff\\0{base_ref}\\0{}\\0{handoff_id}",
285            blake3::hash(&content)
286        );
287        let capsule_ref = format!("capsule:{}", blake3::hash(identity.as_bytes()).to_hex());
288        state.entries.insert(
289            capsule_ref.clone(),
290            CapsuleEntry {
291                parent_ref: None,
292                data: content,
293                deltas: Vec::new(),
294                budget_tokens: 0,
295                created_at: Instant::now(),
296            },
297        );
298        Ok(capsule_ref)
299    }
300
301    #[must_use]
302    pub fn stats(&self) -> CapsuleStats {
303        let Ok(state) = self.state.read() else {
304            return CapsuleStats::default();
305        };
306        let total_bytes = state.entries.values().fold(0_usize, |total, entry| {
307            total.saturating_add(entry.data.len()).saturating_add(
308                entry
309                    .deltas
310                    .iter()
311                    .map(|delta| delta.data.len())
312                    .sum::<usize>(),
313            )
314        });
315        let max_depth = state
316            .entries
317            .keys()
318            .map(|capsule_ref| depth_of(&state.entries, capsule_ref))
319            .max()
320            .unwrap_or(0);
321        CapsuleStats {
322            total_entries: state.entries.len(),
323            total_bytes,
324            max_depth,
325        }
326    }
327}
328
329fn resolve_entries(
330    entries: &HashMap<String, CapsuleEntry>,
331    capsule_ref: &str,
332) -> OclaResult<Vec<u8>> {
333    let mut current = capsule_ref;
334    let mut visited = HashSet::new();
335    let mut layers = Vec::new();
336    loop {
337        if !visited.insert(current) {
338            return Err(invalid("capsule parent cycle detected"));
339        }
340        let entry = entries
341            .get(current)
342            .ok_or_else(|| invalid(format!("unknown capsule: {capsule_ref}")))?;
343        layers.push(entry.deltas.clone());
344        if let Some(parent_ref) = entry.parent_ref.as_deref() {
345            current = parent_ref;
346        } else {
347            let mut data = entry.data.clone();
348            for layer in layers.iter().rev() {
349                for delta in layer {
350                    apply_patch(&mut data, delta)?;
351                }
352            }
353            return Ok(data);
354        }
355    }
356}
357
358fn apply_patch(data: &mut Vec<u8>, delta: &Delta) -> OclaResult<()> {
359    let end = delta
360        .offset
361        .checked_add(delta.data.len())
362        .ok_or_else(|| invalid("capsule delta range overflow"))?;
363    if delta.offset > data.len() {
364        return Err(invalid("capsule delta starts beyond materialized content"));
365    }
366    if end > data.len() {
367        data.resize(end, 0);
368    }
369    data[delta.offset..end].copy_from_slice(&delta.data);
370    Ok(())
371}
372
373fn compute_delta_operations(from: &[u8], to: &[u8]) -> Vec<DeltaOp> {
374    if from == to {
375        return Vec::new();
376    }
377
378    let prefix_len = from
379        .iter()
380        .zip(to)
381        .take_while(|(left, right)| left == right)
382        .count();
383    let suffix_len = from[prefix_len..]
384        .iter()
385        .rev()
386        .zip(to[prefix_len..].iter().rev())
387        .take_while(|(left, right)| left == right)
388        .count();
389    let from_middle_end = from.len() - suffix_len;
390    let to_middle_end = to.len() - suffix_len;
391    let mut operations = Vec::with_capacity(4);
392
393    if prefix_len > 0 {
394        operations.push(DeltaOp::Keep {
395            offset: 0,
396            len: prefix_len,
397        });
398    }
399    if from_middle_end > prefix_len {
400        operations.push(DeltaOp::Delete {
401            offset: prefix_len,
402            len: from_middle_end - prefix_len,
403        });
404    }
405    if to_middle_end > prefix_len {
406        operations.push(DeltaOp::Insert {
407            offset: from_middle_end,
408            data: to[prefix_len..to_middle_end].to_vec(),
409        });
410    }
411    if suffix_len > 0 {
412        operations.push(DeltaOp::Keep {
413            offset: from_middle_end,
414            len: suffix_len,
415        });
416    }
417    operations
418}
419
420fn apply_handoff_operations(base: &[u8], operations: &[DeltaOp]) -> OclaResult<Vec<u8>> {
421    if operations.is_empty() {
422        return Ok(base.to_vec());
423    }
424
425    let mut cursor = 0;
426    let mut output = Vec::with_capacity(base.len());
427    for operation in operations {
428        match operation {
429            DeltaOp::Keep { offset, len } => {
430                validate_handoff_offset(*offset, cursor)?;
431                let end = cursor
432                    .checked_add(*len)
433                    .ok_or_else(|| invalid("handoff keep range overflow"))?;
434                let bytes = base
435                    .get(cursor..end)
436                    .ok_or_else(|| invalid("handoff keep extends beyond base content"))?;
437                output.extend_from_slice(bytes);
438                cursor = end;
439            }
440            DeltaOp::Insert { offset, data } => {
441                validate_handoff_offset(*offset, cursor)?;
442                output.extend_from_slice(data);
443            }
444            DeltaOp::Delete { offset, len } => {
445                validate_handoff_offset(*offset, cursor)?;
446                cursor = cursor
447                    .checked_add(*len)
448                    .ok_or_else(|| invalid("handoff delete range overflow"))?;
449                if cursor > base.len() {
450                    return Err(invalid("handoff delete extends beyond base content"));
451                }
452            }
453        }
454    }
455    if cursor != base.len() {
456        return Err(invalid("handoff delta does not consume base content"));
457    }
458    Ok(output)
459}
460
461fn validate_handoff_offset(offset: usize, cursor: usize) -> OclaResult<()> {
462    if offset != cursor {
463        return Err(invalid("handoff operation offset is out of order"));
464    }
465    Ok(())
466}
467
468fn handoff_delta_size(operations: &[DeltaOp]) -> usize {
469    operations.iter().fold(0_usize, |size, operation| {
470        let operation_size = match operation {
471            DeltaOp::Keep { .. } | DeltaOp::Delete { .. } => 2 * std::mem::size_of::<usize>(),
472            DeltaOp::Insert { data, .. } => 2 * std::mem::size_of::<usize>() + data.len(),
473        };
474        size.saturating_add(operation_size)
475    })
476}
477
478fn unix_time_ms() -> u64 {
479    SystemTime::now()
480        .duration_since(UNIX_EPOCH)
481        .unwrap_or_default()
482        .as_millis()
483        .try_into()
484        .unwrap_or(u64::MAX)
485}
486
487fn depth_of(entries: &HashMap<String, CapsuleEntry>, capsule_ref: &str) -> usize {
488    let mut depth = 0;
489    let mut current = capsule_ref;
490    let mut visited = HashSet::new();
491    while visited.insert(current) {
492        let Some(entry) = entries.get(current) else {
493            break;
494        };
495        let Some(parent_ref) = entry.parent_ref.as_deref() else {
496            break;
497        };
498        depth += 1;
499        current = parent_ref;
500    }
501    depth
502}
503
504fn invalid(message: impl Into<String>) -> OclaError {
505    OclaError::InvalidRequest(message.into())
506}
507
508#[cfg(test)]
509mod tests {
510    use super::{CapsuleStore, Delta, DeltaOp, global_capsule_store};
511
512    fn test_delta() -> Delta {
513        Delta {
514            offset: 1,
515            data: vec![b'a'],
516        }
517    }
518
519    #[test]
520    fn global_store_registers_capsule() {
521        let capsule_ref = global_capsule_store().register(b"global capsule");
522        assert_eq!(
523            global_capsule_store()
524                .resolve(&capsule_ref)
525                .expect("global resolves"),
526            b"global capsule"
527        );
528    }
529
530    #[test]
531    fn register_resolves_original_data() {
532        let store = CapsuleStore::new();
533        let capsule_ref = store.register(b"hello");
534        assert_eq!(
535            store.resolve(&capsule_ref).expect("root resolves"),
536            b"hello"
537        );
538    }
539    #[test]
540    fn fork_resolves_parent_data() {
541        let store = CapsuleStore::new();
542        let parent_ref = store.register(b"hello");
543        let child_ref = store.fork(&parent_ref, 100).expect("fork succeeds");
544        assert_eq!(store.resolve(&child_ref).expect("child resolves"), b"hello");
545        assert_eq!(store.budget_tokens(&child_ref).expect("budget exists"), 100);
546    }
547    #[test]
548    fn fork_delta_resolves_patched_data() {
549        let store = CapsuleStore::new();
550        let parent_ref = store.register(b"hello");
551        let child_ref = store.fork(&parent_ref, 100).expect("fork succeeds");
552        store
553            .apply_delta(&child_ref, test_delta())
554            .expect("delta applies");
555        assert_eq!(store.resolve(&child_ref).expect("child resolves"), b"hallo");
556    }
557
558    #[test]
559    fn later_overlapping_delta_wins_within_layer() {
560        let store = CapsuleStore::new();
561        let parent_ref = store.register(b"hello");
562        let fork_ref = store.fork(&parent_ref, 100).expect("fork succeeds");
563
564        store
565            .apply_delta(
566                &fork_ref,
567                Delta {
568                    offset: 1,
569                    data: vec![b'a'],
570                },
571            )
572            .expect("first delta applies");
573        store
574            .apply_delta(
575                &fork_ref,
576                Delta {
577                    offset: 1,
578                    data: vec![b'u'],
579                },
580            )
581            .expect("second delta applies");
582
583        assert_eq!(store.resolve(&fork_ref).expect("fork resolves"), b"hullo");
584    }
585
586    #[test]
587    fn child_delta_overrides_parent_delta() {
588        let store = CapsuleStore::new();
589        let root_ref = store.register(b"hello");
590        let parent_ref = store.fork(&root_ref, 100).expect("parent fork succeeds");
591        store
592            .apply_delta(
593                &parent_ref,
594                Delta {
595                    offset: 1,
596                    data: vec![b'a'],
597                },
598            )
599            .expect("parent delta applies");
600        let child_ref = store.fork(&parent_ref, 100).expect("child fork succeeds");
601        store
602            .apply_delta(
603                &child_ref,
604                Delta {
605                    offset: 1,
606                    data: vec![b'u'],
607                },
608            )
609            .expect("child delta applies");
610
611        assert_eq!(store.resolve(&child_ref).expect("child resolves"), b"hullo");
612    }
613
614    #[test]
615    fn merge_back_preserves_chronological_delta_order() {
616        let store = CapsuleStore::new();
617        let root_ref = store.register(b"hello");
618        let parent_ref = store.fork(&root_ref, 100).expect("parent fork succeeds");
619        store
620            .apply_delta(
621                &parent_ref,
622                Delta {
623                    offset: 1,
624                    data: vec![b'a'],
625                },
626            )
627            .expect("parent delta applies");
628        let child_ref = store.fork(&parent_ref, 100).expect("child fork succeeds");
629        store
630            .apply_delta(
631                &child_ref,
632                Delta {
633                    offset: 1,
634                    data: vec![b'u'],
635                },
636            )
637            .expect("child delta applies");
638
639        store.merge_back(&child_ref).expect("merge succeeds");
640
641        assert_eq!(
642            store.resolve(&parent_ref).expect("parent resolves"),
643            b"hullo"
644        );
645        assert_eq!(store.resolve(&child_ref).expect("child resolves"), b"hullo");
646    }
647    #[test]
648    fn merge_back_projects_deltas_to_parent() {
649        let store = CapsuleStore::new();
650        let parent_ref = store.register(b"hello");
651        let child_ref = store.fork(&parent_ref, 100).expect("fork succeeds");
652        store
653            .apply_delta(&child_ref, test_delta())
654            .expect("delta applies");
655        store.merge_back(&child_ref).expect("merge succeeds");
656        assert_eq!(
657            store.resolve(&parent_ref).expect("parent resolves"),
658            b"hallo"
659        );
660        assert_eq!(store.resolve(&child_ref).expect("child resolves"), b"hallo");
661    }
662    #[test]
663    fn stats_report_entries_storage_and_depth() {
664        let store = CapsuleStore::new();
665        let parent_ref = store.register(b"hello");
666        let child_ref = store.fork(&parent_ref, 100).expect("fork succeeds");
667        store
668            .apply_delta(&child_ref, test_delta())
669            .expect("delta applies");
670        let stats = store.stats();
671        assert_eq!(stats.total_entries, 2);
672        assert_eq!(stats.total_bytes, 6);
673        assert_eq!(stats.max_depth, 1);
674    }
675
676    #[test]
677    fn snapshot_rollback_restores_snapshot_content() {
678        let store = CapsuleStore::new();
679        let root_ref = store.register(b"hello");
680        let child_ref = store.fork(&root_ref, 100).expect("fork succeeds");
681        store
682            .apply_delta(
683                &child_ref,
684                Delta {
685                    offset: 1,
686                    data: vec![b'a'],
687                },
688            )
689            .expect("first delta applies");
690        let snapshot = store.snapshot(&child_ref).expect("snapshot succeeds");
691        store
692            .apply_delta(
693                &child_ref,
694                Delta {
695                    offset: 1,
696                    data: vec![b'u'],
697                },
698            )
699            .expect("second delta applies");
700
701        store
702            .rollback(&child_ref, &snapshot.snapshot_id)
703            .expect("rollback succeeds");
704
705        assert_eq!(store.resolve(&child_ref).expect("child resolves"), b"hallo");
706    }
707
708    #[test]
709    fn handoff_delta_for_identical_capsules_is_empty() {
710        let store = CapsuleStore::new();
711        let first_ref = store.register(b"unchanged");
712        let second_ref = store.register(b"unchanged");
713
714        let delta = store
715            .compute_handoff_delta(&first_ref, &second_ref)
716            .expect("delta computes");
717
718        assert!(delta.operations.is_empty());
719        assert_eq!(delta.compressed_size, 0);
720    }
721
722    #[test]
723    fn handoff_delta_for_different_capsules_has_insert_and_delete() {
724        let store = CapsuleStore::new();
725        let from_ref = store.register(b"hello");
726        let to_ref = store.register(b"halo!");
727
728        let delta = store
729            .compute_handoff_delta(&from_ref, &to_ref)
730            .expect("delta computes");
731
732        assert!(
733            delta
734                .operations
735                .iter()
736                .any(|operation| matches!(operation, DeltaOp::Insert { .. }))
737        );
738        assert!(
739            delta
740                .operations
741                .iter()
742                .any(|operation| matches!(operation, DeltaOp::Delete { .. }))
743        );
744    }
745
746    #[test]
747    fn apply_handoff_delta_round_trip_matches_target() {
748        let store = CapsuleStore::new();
749        let from_ref = store.register(b"goodbye");
750        let to_ref = store.register(b"good day!");
751        let delta = store
752            .compute_handoff_delta(&from_ref, &to_ref)
753            .expect("delta computes");
754
755        let applied_ref = store
756            .apply_handoff_delta(&from_ref, &delta)
757            .expect("delta applies");
758
759        assert_eq!(
760            store.resolve(&applied_ref).expect("applied resolves"),
761            store.resolve(&to_ref).expect("target resolves")
762        );
763    }
764
765    #[test]
766    fn rollback_with_invalid_snapshot_id_errors() {
767        let store = CapsuleStore::new();
768        let capsule_ref = store.register(b"hello");
769
770        assert!(store.rollback(&capsule_ref, "snapshot:missing").is_err());
771    }
772}