Skip to main content

lean_ctx/core/context_kernel/
multi_agent_e2e.rs

1#[cfg(test)]
2mod tests {
3    use std::collections::HashMap;
4
5    use super::super::capsule_wire::{self, ContextCapsuleV1};
6    use super::super::etpao::{EtpaoMetrics, EtpaoTracker};
7    use super::super::knowledge_health;
8    use super::super::result_fusion::{self, ChildResult, FusionStrategy};
9    use super::super::types::{ContextReceiptV1, ReceiptOutcome};
10
11    fn receipt(id: &str, delivered_tokens: usize, cache_hits: usize) -> ContextReceiptV1 {
12        ContextReceiptV1 {
13            receipt_id: format!("receipt:{id}"),
14            plan_id: format!("plan:{id}"),
15            delivered_tokens,
16            cache_hits,
17            cache_misses: usize::from(cache_hits == 0),
18            outcome: ReceiptOutcome::Accepted,
19            quality_signals: Vec::new(),
20            feedback_attribution: HashMap::new(),
21        }
22    }
23
24    fn capsule(owner: &str, refs: Vec<String>, budget: u64) -> ContextCapsuleV1 {
25        ContextCapsuleV1::new("multi-agent-e2e", refs, budget, owner)
26    }
27
28    fn child_result(
29        agent_id: &str,
30        evidence: &[&str],
31        confidence: f64,
32        quality_score: f64,
33        contradicts: &[&str],
34    ) -> ChildResult {
35        ChildResult {
36            agent_id: agent_id.to_owned(),
37            receipt_ref: format!("receipt:{agent_id}"),
38            evidence: evidence.iter().map(|item| (*item).to_owned()).collect(),
39            confidence,
40            quality_score,
41            contradicts: contradicts
42                .iter()
43                .map(|agent| (*agent).to_owned())
44                .collect(),
45        }
46    }
47
48    #[test]
49    fn three_deep_chain_budget_cascade() {
50        let mut tracker = EtpaoTracker::default();
51        let mut parent = capsule("parent", vec!["ref:root".to_owned()], 10_000);
52
53        let mut child = capsule("child", vec!["ref:child".to_owned()], 5_000);
54        child.parent_capsule = Some(parent.capsule_id.clone());
55        parent.budget_remaining = parent.budget_remaining.saturating_sub(child.budget_tokens);
56        tracker.record_receipt(
57            "parent",
58            &receipt("parent-to-child", 5_000, 0),
59            ReceiptOutcome::Accepted,
60        );
61
62        let mut grandchild = capsule("grandchild", vec!["ref:grandchild".to_owned()], 2_500);
63        grandchild.parent_capsule = Some(child.capsule_id.clone());
64        child.budget_remaining = child
65            .budget_remaining
66            .saturating_sub(grandchild.budget_tokens);
67        tracker.record_receipt(
68            "child",
69            &receipt("child-to-grandchild", 2_500, 0),
70            ReceiptOutcome::Accepted,
71        );
72
73        assert_eq!(parent.budget_remaining, 5_000);
74        assert_eq!(child.budget_remaining, 2_500);
75        assert_eq!(grandchild.budget_remaining, 2_500);
76        assert!(grandchild.budget_remaining <= child.budget_remaining);
77        assert!(child.budget_remaining <= parent.budget_remaining);
78        assert_eq!(
79            tracker.get("parent").map(|metrics| metrics.tokens_input),
80            Some(5_000)
81        );
82        assert_eq!(
83            tracker.get("child").map(|metrics| metrics.tokens_input),
84            Some(2_500)
85        );
86    }
87
88    #[test]
89    fn sibling_context_dedup() {
90        let shared = ["ref:A", "ref:B"];
91        let siblings: Vec<_> = ["ref:one", "ref:two", "ref:three"]
92            .into_iter()
93            .enumerate()
94            .map(|(index, unique)| {
95                capsule(
96                    &format!("sibling-{index}"),
97                    shared
98                        .into_iter()
99                        .chain([unique])
100                        .map(str::to_owned)
101                        .collect(),
102                    1_000,
103                )
104            })
105            .collect();
106
107        let report = capsule_wire::dedup_siblings(&siblings);
108
109        assert_eq!(report.shared_refs, vec!["ref:A", "ref:B"]);
110        assert!(report.dedup_ratio > 0.0);
111        assert!(report.unique_per_capsule.iter().all(|refs| refs.len() == 1));
112    }
113
114    #[test]
115    fn delta_transfer_smaller_than_full() {
116        let original_refs: Vec<_> = (0..10)
117            .map(|index| format!("ref:original-context-object-{index:02}-with-stable-metadata"))
118            .collect();
119        let base = capsule("parent", original_refs.clone(), 5_000);
120        let mut updated_refs = original_refs[..8].to_vec();
121        updated_refs.extend([
122            "ref:new-context-object-10-with-stable-metadata".to_owned(),
123            "ref:new-context-object-11-with-stable-metadata".to_owned(),
124        ]);
125        let mut updated = capsule("parent", updated_refs, 5_000);
126        updated.created_at_epoch = base.created_at_epoch;
127
128        let delta = capsule_wire::compute_delta(&base, &updated);
129        let delta_size = serde_json::to_vec(&delta).expect("delta serializes").len();
130        let full_size = serde_json::to_vec(&updated)
131            .expect("capsule serializes")
132            .len();
133        let applied = capsule_wire::apply_delta(&base, &delta);
134
135        assert!(delta_size * 2 < full_size);
136        assert_eq!(applied, updated);
137    }
138
139    #[test]
140    fn result_fusion_resolves_contradiction() {
141        let results = [
142            child_result("agent-a", &["finding:accepted"], 0.95, 0.95, &["agent-b"]),
143            child_result("agent-b", &["finding:rejected"], 0.80, 0.90, &["agent-a"]),
144            child_result("agent-c", &["finding:accepted"], 0.85, 0.90, &[]),
145        ];
146
147        let report = result_fusion::fuse_results(&results, FusionStrategy::WeightedMerge);
148
149        assert!(report.conflicts.iter().any(|conflict| {
150            (conflict.agent_a == "agent-a" && conflict.agent_b == "agent-b")
151                || (conflict.agent_a == "agent-b" && conflict.agent_b == "agent-a")
152        }));
153        assert_eq!(report.winning_agent, "agent-a");
154        assert!(
155            report
156                .merged_evidence
157                .contains(&"finding:accepted".to_owned())
158        );
159    }
160
161    #[test]
162    fn etpao_improves_with_context_reuse() {
163        let mut tracker = EtpaoTracker::default();
164        for index in 0..5 {
165            tracker.record_receipt(
166                "chain",
167                &receipt(&format!("uncached-{index}"), 1_000, 0),
168                ReceiptOutcome::Accepted,
169            );
170        }
171        let first: EtpaoMetrics = tracker.aggregate();
172        let first_etpao = first.etpao();
173
174        for index in 0..5 {
175            tracker.record_receipt(
176                "chain",
177                &receipt(&format!("cached-{index}"), 100, 1),
178                ReceiptOutcome::Accepted,
179            );
180        }
181        let second_etpao = tracker.aggregate().etpao();
182
183        assert!(second_etpao < first_etpao);
184    }
185
186    #[test]
187    fn knowledge_health_detects_stale_facts() {
188        let mut facts = vec![(true, false); 3];
189        facts.extend([(false, false); 5]);
190        facts.extend([(false, true); 2]);
191
192        let report = knowledge_health::assess_health(&facts, 0, 0, 0, 0);
193
194        assert!((report.freshness_score - 0.3).abs() < f64::EPSILON);
195        assert!((report.stale_ratio - 0.7).abs() < f64::EPSILON);
196        assert!((report.contradiction_rate - 0.2).abs() < f64::EPSILON);
197    }
198}