use super::super::MESSAGES_DIR;
use super::{tool_result, tool_use};
use crate::prompt::child_dispatch::{ChildDispatchRequest, run};
use crate::prompt::dispatch::assembler::assemble;
use crate::prompt::inbox::Launcher;
use crate::template::{GitRunner, RealGit};
use crate::workspace::{self, fixture};
use brazen::{Content, Message};
use std::io;
use std::path::Path;
const PARENT: &str = "20260101-p1";
struct NoopLauncher;
impl Launcher for NoopLauncher {
fn launch(&self, _ws: &Path, _agent: &str) -> io::Result<()> {
Ok(())
}
}
fn unanswered(history: &[Message]) -> Vec<String> {
let mut out = Vec::new();
for (i, message) in history.iter().enumerate() {
for block in &message.content {
let Content::ToolUse { id, .. } = block else {
continue;
};
let answered = history.get(i + 1).is_some_and(|next| {
next.content.iter().any(
|b| matches!(b, Content::ToolResult { tool_use_id, .. } if tool_use_id == id),
)
});
if !answered {
out.push(id.clone());
}
}
}
out
}
#[test]
fn a_child_forked_mid_tool_step_assembles_a_wire_valid_first_history() {
let (_h, ws) = fixture::workspace();
fixture::amend_config(&ws, &[("souls/worker.md", "worker soul body\n")]);
let parent_wt = fixture::spawn_root(&ws, PARENT);
let g = RealGit::new();
std::fs::create_dir_all(parent_wt.join(MESSAGES_DIR)).unwrap();
for (name, body) in [
("001-user.md", "do a thing".to_string()),
(
"002-gpt-5.4.json",
serde_json::to_string(&[tool_use("call_read")]).unwrap(),
),
(
"003-tool.json",
serde_json::to_string(&[tool_result("call_read")]).unwrap(),
),
(
"004-gpt-5.4.json",
serde_json::to_string(&[
Content::Text("dispatching a worker".into()),
tool_use("call_dispatch"),
])
.unwrap(),
),
] {
std::fs::write(parent_wt.join(MESSAGES_DIR).join(name), body).unwrap();
}
g.run(&parent_wt, &["add", "-A"]).unwrap();
g.run(&parent_wt, &["commit", "-m", "transcript [p1]"])
.unwrap();
assert_eq!(
unanswered(&assemble(&parent_wt, None).unwrap()),
vec!["call_dispatch".to_string()],
"the fork point must be mid-tool-step for this test to mean anything"
);
let child = run(
&ChildDispatchRequest {
repo: &ws,
parent_branch: PARENT,
parent_worktree: &parent_wt,
role: crate::prompt::WORKER_ROLE,
goal: "summarize the repo\n",
name: None,
fork_point: None,
pins: crate::prompt::PinnedDocs::none(),
},
&g,
&crate::prompt::SystemClock,
&crate::prompt::NanoIdGen,
&NoopLauncher,
)
.unwrap();
let child_wt = workspace::agent_worktree(&ws, &child);
let history = assemble(&child_wt, None).unwrap();
assert!(
unanswered(&history).is_empty(),
"child history still carries an unanswered tool_use: {history:?}"
);
let listing = g
.run_capture(
&workspace::repo_git(&ws),
&[
"ls-tree",
"-r",
"--name-only",
&workspace::agent_ref(&child),
],
)
.unwrap();
let has = |p: &str| listing.lines().any(|l| l == p);
assert!(has("messages/001-user.md"), "{listing}");
assert!(has("messages/002-gpt-5.4.json"), "{listing}");
assert!(has("messages/003-tool.json"), "{listing}");
assert!(!has("messages/004-gpt-5.4.json"), "{listing}");
let parent_listing = g
.run_capture(
&workspace::repo_git(&ws),
&[
"ls-tree",
"-r",
"--name-only",
&workspace::agent_ref(PARENT),
],
)
.unwrap();
assert!(
parent_listing
.lines()
.any(|l| l == "messages/004-gpt-5.4.json"),
"{parent_listing}"
);
}