use super::advance::{AGENT, RecLauncher, model_entry, worker_config, workspace_with_tail};
use super::fixtures::*;
use crate::prompt::dispatch::advance::{AdvanceOutcome, run};
use crate::prompt::inbox;
use brazen::Content;
fn crashed_tail() -> Vec<(&'static str, String)> {
vec![
("001-user.md", "hi".to_string()),
(
"002-claude-sonnet-5.json",
model_entry(&[
Content::Text("running".into()),
Content::ToolUse {
id: "t1".into(),
name: "bash".into(),
input: serde_json::json!({"command": "true"}),
signature: None,
},
]),
),
]
}
#[test]
fn a_deposit_revives_a_crashed_window_through_settlement() {
let (ws, wt) = workspace_with_tail(&crashed_tail());
let (clock, id) = (FixedClock::default(), FixedIdGen);
inbox::deposit(ws.path(), AGENT, "user", "hello?", &clock).unwrap();
let adapter = StubAdapter::scripted([StubAdapter::reply_ok(&happy_response_bytes())]);
let (sleeper, git) = (StubSleeper::default(), StubGit::ok());
let tools = StubToolExecutor::ok();
let rec = RecLauncher::default();
let mut deps = valid_deps(&adapter, &sleeper, &git, &clock, &id, &tools, ws.path());
deps.launcher = &rec;
let out = run(ws.path(), AGENT, None, &deps, &mut || Ok(worker_config())).unwrap();
assert!(matches!(out, AdvanceOutcome::Terminal), "got {out:?}");
let settlement = std::fs::read_to_string(wt.join("messages/003-tool.json")).unwrap();
assert!(settlement.contains("t1"), "{settlement}");
assert!(
settlement.contains("died before recording an outcome"),
"{settlement}"
);
let delivered = std::fs::read_to_string(wt.join("messages/004-user.md")).unwrap();
assert!(delivered.contains("hello?"), "{delivered}");
assert!(wt.join("messages/005-claude-sonnet-5.json").exists());
}
#[test]
fn a_partially_answered_crash_settles_only_the_unanswered_ids() {
let entries = vec![
("001-user.md", "hi".to_string()),
(
"002-claude-sonnet-5.json",
model_entry(&[
Content::ToolUse {
id: "t1".into(),
name: "bash".into(),
input: serde_json::json!({"command": "true"}),
signature: None,
},
Content::ToolUse {
id: "t2".into(),
name: "bash".into(),
input: serde_json::json!({"command": "false"}),
signature: None,
},
]),
),
(
"003-tool.json",
model_entry(&[Content::ToolResult {
tool_use_id: "t1".into(),
content: vec![Content::Text("done".into())],
is_error: false,
}]),
),
];
let (ws, wt) = workspace_with_tail(&entries);
let (clock, id) = (FixedClock::default(), FixedIdGen);
inbox::deposit(ws.path(), AGENT, "user", "and?", &clock).unwrap();
let adapter = StubAdapter::scripted([StubAdapter::reply_ok(&happy_response_bytes())]);
let (sleeper, git) = (StubSleeper::default(), StubGit::ok());
let tools = StubToolExecutor::ok();
let rec = RecLauncher::default();
let mut deps = valid_deps(&adapter, &sleeper, &git, &clock, &id, &tools, ws.path());
deps.launcher = &rec;
let out = run(ws.path(), AGENT, None, &deps, &mut || Ok(worker_config())).unwrap();
assert!(matches!(out, AdvanceOutcome::Terminal), "got {out:?}");
let settlement = std::fs::read_to_string(wt.join("messages/004-tool.json")).unwrap();
assert!(settlement.contains("\"t2\""), "{settlement}");
assert!(!settlement.contains("\"t1\""), "{settlement}");
assert!(wt.join("messages/005-user.md").exists());
}
#[test]
fn a_never_stepped_user_tail_settles_nothing_and_steps_normally() {
let entries = vec![("001-user.md", "hi".to_string())];
let (ws, wt) = workspace_with_tail(&entries);
let (clock, id) = (FixedClock::default(), FixedIdGen);
let adapter = StubAdapter::scripted([StubAdapter::reply_ok(&happy_response_bytes())]);
let (sleeper, git) = (StubSleeper::default(), StubGit::ok());
let tools = StubToolExecutor::ok();
let rec = RecLauncher::default();
let mut deps = valid_deps(&adapter, &sleeper, &git, &clock, &id, &tools, ws.path());
deps.launcher = &rec;
let out = run(ws.path(), AGENT, None, &deps, &mut || Ok(worker_config())).unwrap();
assert!(matches!(out, AdvanceOutcome::Terminal), "got {out:?}");
assert!(wt.join("messages/002-claude-sonnet-5.json").exists());
assert!(!wt.join("messages/002-tool.json").exists());
}