use tempfile::tempdir;
use super::AGENT;
use crate::git_tree::AgentState;
use crate::steps_view::{ORPHANED_MAIL, Orphan, Tail, build_aged};
pub(super) fn write_messages(ws: &std::path::Path, names: &[&str]) {
let dir = messages(ws);
for name in names {
std::fs::write(dir.join(name), b"x").unwrap();
}
}
pub(super) fn messages(ws: &std::path::Path) -> std::path::PathBuf {
let dir = ws.join("agents").join(AGENT).join("messages");
std::fs::create_dir_all(&dir).unwrap();
dir
}
pub(super) const DECLINE: &str = "litany: branch tip is an assistant entry with tool_use unmatched";
pub(super) fn write_driver_log(ws: &std::path::Path, words: &str) {
let log = ws.join("steps").join(AGENT);
std::fs::create_dir_all(&log).unwrap();
std::fs::write(log.join("driver.log"), words).unwrap();
}
#[test]
fn mail_with_no_driver_and_a_spoken_log_carries_the_words() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_messages(ws, &["001-user.md", "002-opus.json", "003-user.md"]);
write_driver_log(ws, DECLINE);
let view = build_aged(ws, AGENT, AgentState::Stopped);
let Orphan::Spoke(Tail::Mail, words) = &view.orphan else {
panic!("expected Spoke(Mail), got {:?}", view.orphan);
};
assert!(words.contains("unmatched"), "{words}");
let banner = view.orphan.banner();
assert!(banner.contains(ORPHANED_MAIL), "{banner}");
assert!(banner.contains("driver.log"), "{banner}");
assert!(banner.contains("unmatched"), "{banner}");
}
#[test]
fn mail_with_no_driver_and_no_log_is_mute_and_says_so() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_messages(ws, &["001-user.md"]);
let view = build_aged(ws, AGENT, AgentState::Quiescent);
assert_eq!(view.orphan, Orphan::Mute(Tail::Mail));
let banner = view.orphan.banner();
assert!(banner.contains(ORPHANED_MAIL), "{banner}");
assert!(banner.contains("nothing on disk says why"), "{banner}");
}
#[test]
fn a_settled_tail_is_no_orphan() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_messages(ws, &["001-user.md", "002-opus.json"]);
let view = build_aged(ws, AGENT, AgentState::Stopped);
assert_eq!(view.orphan, Orphan::None);
assert!(!view.orphan.orphaned());
assert!(view.orphan.banner().is_empty());
}
#[test]
fn a_driven_agent_is_never_an_orphan() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_messages(ws, &["001-user.md"]);
for state in [AgentState::Live, AgentState::InFlight] {
assert_eq!(build_aged(ws, AGENT, state).orphan, Orphan::None);
}
}
#[test]
fn an_absent_transcript_is_no_orphan() {
let dir = tempdir().unwrap();
let view = build_aged(dir.path(), AGENT, AgentState::Stopped);
assert_eq!(view.orphan, Orphan::None);
}
#[test]
fn an_empty_transcript_directory_is_no_orphan() {
let dir = tempdir().unwrap();
let ws = dir.path();
messages(ws);
assert_eq!(
build_aged(ws, AGENT, AgentState::Stopped).orphan,
Orphan::None
);
}