use super::advance::{AGENT, RecLauncher, worker_config};
use super::fixtures::*;
use crate::prompt::Clock;
use crate::prompt::child_dispatch::{ChildDispatchRequest, run as dispatch_child};
use crate::prompt::compactor::tools;
use crate::prompt::dispatch::advance::{AdvanceOutcome, run};
use crate::prompt::inbox::{self, deposit_result};
use crate::prompt::{Error, PinnedDocs};
use crate::template::{GitRunner, RealGit};
use crate::workspace::agent_name::mint::test_rng;
use crate::workspace::{agent_worktree, fixture};
const OPENING: &str = "port the parser to the new grammar\n";
struct DescentClock;
impl Clock for DescentClock {
fn now_iso8601(&self) -> String {
"iso".into()
}
fn now_compact(&self) -> String {
"ct1".into()
}
}
#[test]
fn a_conversation_past_a_compaction_still_has_its_opening_prompt_goal_soul_and_name() {
let (_h, ws) = fixture::workspace();
let parent = AGENT;
let parent_wt = fixture::spawn_root(&ws, parent);
let git = RealGit::new();
let (clock, id) = (FixedClock::default(), FixedIdGen);
let rec = RecLauncher::default();
std::fs::create_dir_all(parent_wt.join("messages")).unwrap();
std::fs::write(parent_wt.join("messages/001-user.md"), OPENING).unwrap();
std::fs::write(parent_wt.join("messages/002-user.md"), "any progress?\n").unwrap();
std::fs::write(parent_wt.join("goal.md"), OPENING).unwrap();
std::fs::write(parent_wt.join("soul.md"), "you are a worker\n").unwrap();
std::fs::write(parent_wt.join("name"), "swift-heron\n").unwrap();
git.run(&parent_wt, &["add", "-A"]).unwrap();
git.run(&parent_wt, &["commit", "-m", "checkpoint"])
.unwrap();
let child = dispatch_child(
&ChildDispatchRequest {
repo: &ws,
parent_branch: parent,
parent_worktree: &parent_wt,
role: "compactor",
goal: "compact",
name: None,
fork_point: None,
cwd: None,
pins: PinnedDocs::none(),
},
&git,
&DescentClock,
&id,
&rec,
test_rng(),
)
.unwrap();
let cwt = agent_worktree(&ws, &child);
assert_eq!(
std::fs::read_to_string(cwt.join("messages/001-user.md")).unwrap(),
OPENING,
"the compactor inherits the entry it used to delete"
);
tools::write_summary(&cwt, "the parser port is underway\n").unwrap();
git.run(&cwt, &["add", "-A"]).unwrap();
let declined = tools::mark_for_deletion(&cwt, &child, "summary/001.md", &git).unwrap_err();
assert!(
matches!(&declined, Error::NotCompactionEligible { path, what }
if path == "summary/001.md" && what.contains("own product")),
"{declined:?}"
);
let declined =
tools::mark_for_deletion(&cwt, &child, "messages/001-user.md", &git).unwrap_err();
assert!(
matches!(&declined, Error::NotCompactionEligible { path, .. }
if path == "messages/001-user.md"),
"{declined:?}"
);
for slot in crate::prompt::dispatch::step_commit::SYSTEM_SLOT_FILES {
let declined = tools::mark_for_deletion(&cwt, &child, slot, &git).unwrap_err();
assert!(
matches!(&declined, Error::NotCompactionEligible { path, .. } if path == slot),
"{slot}: {declined:?}"
);
}
tools::mark_for_deletion(&cwt, &child, "messages/002-user.md", &git).unwrap();
git.run(&cwt, &["add", "-A"]).unwrap();
git.run(&cwt, &["commit", "-m", "compaction"]).unwrap();
let tip = git.run_capture(&cwt, &["rev-parse", "HEAD"]).unwrap();
deposit_result(
&ws,
parent,
&child,
inbox::Epitaph::FinalResponse,
tip.trim(),
Some("compacted"),
&clock,
&git,
)
.unwrap();
inbox::deposit(&ws, parent, "user", "carry on", &clock).unwrap();
let adapter = StubAdapter::scripted([StubAdapter::reply_ok(&happy_response_bytes())]);
let (sleeper, tools_stub, stub_git) = (
StubSleeper::default(),
StubToolExecutor::ok(),
StubGit::ok(),
);
let mut deps = valid_deps(&adapter, &sleeper, &stub_git, &clock, &id, &tools_stub, &ws);
deps.git = &git;
deps.launcher = &rec;
let out = run(&ws, parent, None, &deps, &mut || Ok(worker_config())).unwrap();
assert!(matches!(out, AdvanceOutcome::Terminal), "the step ran");
assert_eq!(
std::fs::read_to_string(parent_wt.join("summary/001.md")).unwrap(),
"the parser port is underway\n"
);
assert!(!parent_wt.join("messages/002-user.md").exists());
for slot in crate::prompt::dispatch::step_commit::SYSTEM_SLOT_FILES {
assert!(parent_wt.join(slot).exists(), "{slot} survived the landing");
}
assert_eq!(
std::fs::read_to_string(parent_wt.join("messages/001-user.md")).unwrap(),
OPENING
);
let req: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(ws.join(format!("steps/{parent}/001/request.json"))).unwrap(),
)
.unwrap();
assert!(
req["messages"].to_string().contains("port the parser"),
"{}",
req["messages"]
);
}