use serde_json::Value;
use abstractcode::transcript::{Fold, FoldEffect, Item};
fn fixture() -> Value {
let raw = include_str!("fixtures/failed_agent_subrun_tree.json");
serde_json::from_str(raw).expect("fixture parses")
}
#[test]
fn failed_agent_subrun_tree_concludes_from_the_terminal_report() {
let tree = fixture();
let root_id = tree["root_run_id"].as_str().unwrap().to_string();
let agent_id = tree["agent_run_id"].as_str().unwrap().to_string();
assert_eq!(tree["agent_run_status"], "failed", "fixture self-check");
assert_eq!(tree["root_run_status"], "waiting", "fixture self-check");
let mut fold = Fold::new();
fold.begin_run(&root_id);
let mut followed: Vec<String> = Vec::new();
for rec in tree["ledgers"][&root_id].as_array().unwrap() {
for fx in fold.apply(&root_id, rec) {
if let FoldEffect::FollowRun(sub) = fx {
followed.push(sub);
}
}
}
assert!(
followed.contains(&agent_id),
"the agent subrun is discovered from the root's subworkflow wait"
);
for rec in tree["ledgers"][&agent_id].as_array().unwrap() {
fold.apply(&agent_id, rec);
}
assert!(
fold.items
.iter()
.any(|i| matches!(i, Item::Error { text } if text.contains("Model unloaded"))),
"the provider failure renders as an error card"
);
assert!(
!fold.finished,
"records alone never conclude: a failed effect record can retry or absorb"
);
assert_eq!(
fold.answer_run_id(),
Some(agent_id.as_str()),
"the cycling agent bound as the answer source (exec polls this)"
);
fold.subrun_terminal(&agent_id, "failed");
assert!(fold.finished, "the composer is freed");
assert!(fold.failed, "and the outcome is Failed (exec exits 1)");
assert!(
fold.items.iter().rev().any(
|i| matches!(i, Item::Error { text } if text.contains("the agent run ended: failed"))
),
"the conclusion names what happened (the ✗ done summary follows it)"
);
let items = fold.items.len();
fold.subrun_terminal("97b4dde0-a125-4e8f-bed3-e8732c4054d1", "cancelled");
assert_eq!(fold.items.len(), items);
}
#[test]
fn agent_dying_before_its_first_cycle_still_concludes() {
let tree = fixture();
let root_id = tree["root_run_id"].as_str().unwrap().to_string();
let agent_id = tree["agent_run_id"].as_str().unwrap().to_string();
let mut fold = Fold::new();
fold.begin_run(&root_id);
for rec in tree["ledgers"][&root_id].as_array().unwrap() {
let _ = fold.apply(&root_id, rec);
}
assert_eq!(
fold.answer_run_id(),
Some(agent_id.as_str()),
"the agent bound from the ROOT's spawn declaration alone \
(details.sub_workflow_id = visual_react_agent_…) — no cycle needed"
);
assert!(!fold.finished, "nothing concluded yet");
fold.subrun_terminal(&agent_id, "failed");
assert!(fold.finished, "the turn concludes honestly");
assert!(fold.failed, "as a failure (exec exits 1)");
assert!(
fold.items.iter().rev().any(
|i| matches!(i, Item::Error { text } if text.contains("the agent run ended: failed"))
),
"the conclusion names what happened (the ✗ done summary follows it)"
);
let mut fold2 = Fold::new();
fold2.begin_run(&root_id);
for rec in tree["ledgers"][&root_id].as_array().unwrap() {
fold2.apply(&root_id, rec);
}
fold2.subrun_terminal("eee5ff82-cc78-49f3-b55b-94d7026aa23a", "completed");
fold2.subrun_terminal("97b4dde0-a125-4e8f-bed3-e8732c4054d1", "failed");
assert!(
!fold2.finished,
"helper terminals never conclude — only the declared agent's does"
);
}