use crate::harness::*;
#[test]
fn agents_frozen_lane_reports_escalation_blocked_not_completed() {
let enc = "-Users-testuser-Projects-frozen";
let sess = "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d";
let hex = "ab8a4c5868015a8be";
let frozen = concat!(
r#"{"type":"user","isSidechain":true,"agentId":"ab8a4c5868015a8be","timestamp":"2026-06-26T10:40:00.000Z","message":{"role":"user","content":"teardown"}}"#,
"\n",
r#"{"type":"assistant","timestamp":"2026-06-26T10:42:00.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Removing the transient credential files."}]}}"#,
"\n",
r#"{"type":"assistant","timestamp":"2026-06-26T10:43:31.906Z","message":{"role":"assistant","content":[{"type":"tool_use","id":"toolu_0137gHdLDnXKsa94qGmmnbqV","name":"Bash","input":{"command":"for f in a.txt b.txt; do [ -f \"$SCRATCH/$f\" ] && rm -f \"$SCRATCH/$f\"; done"}}]}}"#,
"\n",
);
let h = Home::new();
h.write(
&format!("{enc}/{sess}.jsonl"),
"{\"type\":\"user\",\"timestamp\":\"2026-06-26T09:00:00.000Z\",\"message\":{\"role\":\"user\",\"content\":\"go\"}}\n",
);
h.write(&format!("{enc}/{sess}/subagents/agent-{hex}.jsonl"), frozen);
let find = |stdout: &str| -> serde_json::Value {
json_rows(stdout, "agent")
.into_iter()
.find(|n| n["agent_id"] == hex)
.expect("the subagent row")
};
let out = h.run(&["agents", &format!("@{sess}"), "--format", "json"]);
assert!(out.success, "stderr: {}", out.stderr);
let node = find(&out.stdout);
assert_eq!(
node["status"], "running",
"a frozen lane must not be completed: {node}"
);
assert_eq!(node["pending_classification"], "escalation-blocked");
assert_eq!(node["pending_tool_name"], "Bash");
assert_eq!(
node["pending_tool_use_id"],
"toolu_0137gHdLDnXKsa94qGmmnbqV"
);
assert!(
node["completed_utc"].is_null() && node["completed_local"].is_null(),
"a frozen lane must not carry a completion instant: {node}"
);
assert!(
node["duration"].is_null(),
"no duration while frozen: {node}"
);
assert_eq!(node["last_activity_utc"], "2026-06-26T10:43:31.906Z");
assert_eq!(node["last_activity_utc"], node["pending_since_utc"]);
let txt = h.run(&["agents", &format!("@{sess}")]);
assert!(
txt.stdout.contains("PENDING") && txt.stdout.contains("escalation-blocked"),
"no pending line: {}",
txt.stdout
);
assert!(
txt.stdout.contains("\n PENDING"),
"PENDING must be an indented detail line: {}",
txt.stdout
);
assert!(
txt.stdout.contains("HOISTS"),
"escalation-blocked carries the hoist explainer: {}",
txt.stdout
);
assert!(
!txt.stdout.contains("completed 2026") && !txt.stdout.contains("last-seen"),
"frozen lane must not print a terminal-instant line: {}",
txt.stdout
);
let resolved = format!(
"{frozen}{}{}",
"{\"type\":\"user\",\"timestamp\":\"2026-06-26T11:20:13.911Z\",\"message\":{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"toolu_0137gHdLDnXKsa94qGmmnbqV\",\"content\":\"shredded\"}]}}\n",
"{\"type\":\"assistant\",\"timestamp\":\"2026-06-26T11:21:00.000Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"Teardown complete.\"}]}}\n"
);
h.write(
&format!("{enc}/{sess}/subagents/agent-{hex}.jsonl"),
&resolved,
);
let out2 = h.run(&["agents", &format!("@{sess}"), "--format", "json"]);
let node2 = find(&out2.stdout);
assert_eq!(node2["status"], "completed");
assert!(
node2["pending_classification"].is_null(),
"resolved lane must not be pending: {node2}"
);
assert_eq!(node2["completed_utc"], "2026-06-26T11:21:00.000Z");
assert_eq!(node2["last_activity_utc"], node2["completed_utc"]);
}
#[test]
fn agents_returned_message_on_open_lane_carries_history_caution() {
let enc = "-Users-testuser-Projects-rmcaution";
let sess = "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d";
let hex = "ab8a4c5868015a8be";
let frozen = concat!(
r#"{"type":"user","isSidechain":true,"agentId":"ab8a4c5868015a8be","timestamp":"2026-06-26T10:40:00.000Z","message":{"role":"user","content":"teardown"}}"#,
"\n",
r#"{"type":"assistant","timestamp":"2026-06-26T10:42:00.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Work is complete — confirming the shutdown request."}]}}"#,
"\n",
r#"{"type":"assistant","timestamp":"2026-06-26T10:43:31.906Z","message":{"role":"assistant","content":[{"type":"tool_use","id":"toolu_0137gHdLDnXKsa94qGmmnbqV","name":"Bash","input":{"command":"echo wait"}}]}}"#,
"\n",
);
let h = Home::new();
h.write(
&format!("{enc}/{sess}.jsonl"),
"{\"type\":\"user\",\"timestamp\":\"2026-06-26T09:00:00.000Z\",\"message\":{\"role\":\"user\",\"content\":\"go\"}}\n",
);
h.write(&format!("{enc}/{sess}/subagents/agent-{hex}.jsonl"), frozen);
let out = h.run(&["agents", &format!("@{sess}"), "--returned-message"]);
assert!(out.success, "stderr: {}", out.stderr);
assert!(
out.stdout
.contains("history — predates the still-open lane, NOT the outcome"),
"open-lane returned message must be branded as history: {}",
out.stdout
);
let resolved = format!(
"{frozen}{}{}",
"{\"type\":\"user\",\"timestamp\":\"2026-06-26T11:20:13.911Z\",\"message\":{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"toolu_0137gHdLDnXKsa94qGmmnbqV\",\"content\":\"ok\"}]}}\n",
"{\"type\":\"assistant\",\"timestamp\":\"2026-06-26T11:21:00.000Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"Teardown complete.\"}]}}\n"
);
h.write(
&format!("{enc}/{sess}/subagents/agent-{hex}.jsonl"),
&resolved,
);
let out2 = h.run(&["agents", &format!("@{sess}"), "--returned-message"]);
assert!(
out2.stdout.contains("returned") && !out2.stdout.contains("predates the still-open lane"),
"a completed lane's returned message must stay unbranded: {}",
out2.stdout
);
}
#[test]
fn agents_running_not_frozen_prints_last_seen_not_completed() {
let enc = "-Users-testuser-Projects-midflight";
let sess = "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d";
let hex = "beef4c5868015a8be";
let h = Home::new();
h.write(
&format!("{enc}/{sess}.jsonl"),
"{\"type\":\"user\",\"timestamp\":\"2026-06-26T09:00:00.000Z\",\"message\":{\"role\":\"user\",\"content\":\"go\"}}\n",
);
h.write(
&format!("{enc}/{sess}/subagents/agent-{hex}.jsonl"),
concat!(
r#"{"type":"user","isSidechain":true,"agentId":"beef4c5868015a8be","timestamp":"2026-06-26T10:00:00.000Z","message":{"role":"user","content":"scan"}}"#,
"\n",
r#"{"type":"assistant","timestamp":"2026-06-26T10:01:00.000Z","message":{"role":"assistant","content":[{"type":"tool_use","id":"toolu_mid1","name":"Bash","input":{"command":"ls"}}]}}"#,
"\n",
r#"{"type":"user","timestamp":"2026-06-26T10:02:00.000Z","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_mid1","content":"ok"}]}}"#,
"\n",
),
);
let out = h.run(&["agents", &format!("@{sess}"), "--format", "json"]);
assert!(out.success, "stderr: {}", out.stderr);
let node = json_rows(&out.stdout, "agent")
.into_iter()
.find(|n| n["agent_id"] == hex)
.expect("the subagent row");
assert_eq!(node["status"], "running", "honest middle: {node}");
assert!(node["pending_classification"].is_null(), "{node}");
assert!(
node["completed_utc"].is_null() && node["duration"].is_null(),
"running lane must not claim completion: {node}"
);
assert_eq!(node["last_activity_utc"], "2026-06-26T10:02:00.000Z");
let txt = h.run(&["agents", &format!("@{sess}")]);
assert!(
txt.stdout.contains("last-seen") && !txt.stdout.contains("completed 2026"),
"text must print last-seen, not completed: {}",
txt.stdout
);
}
#[test]
fn agents_row_without_timestamps_omits_duration() {
let h = Home::new();
h.write(
&format!("{ENC}/{SESS}.jsonl"),
"{\"type\":\"user\",\"message\":{\"role\":\"user\",\"content\":\"hi\"}}\n",
);
h.write(
&format!("{ENC}/{SESS}/subagents/agent-nots99.jsonl"),
concat!(
r#"{"type":"user","isSidechain":true,"agentId":"nots99","message":{"role":"user","content":"start, no timestamp"}}"#, "\n",
r#"{"type":"assistant","message":{"role":"assistant","content":[{"type":"tool_use","id":"t","name":"Bash","input":{}}]}}"#, "\n",
),
);
let out = h.run(&["agents", at(SESS).as_str()]);
assert!(out.success, "stderr: {}", out.stderr);
assert!(
out.stdout.contains("unknown"),
"status unknown: {}",
out.stdout
);
assert!(
!out.stdout.contains("duration"),
"no duration line: {}",
out.stdout
);
}
#[test]
fn agents_true_trigger_time_is_the_parent_tool_use_ts() {
let h = topology_home();
let out = h.run(&["agents", at(SESS).as_str(), "--format", "json"]);
assert!(out.success, "stderr: {}", out.stderr);
let rows = json_rows(&out.stdout, "agent");
let builtin = rows
.iter()
.find(|v| v.get("agent_id").and_then(|a| a.as_str()) == Some("topo11"))
.expect("the built-in topo11 row");
assert_eq!(builtin["trigger_utc"], "2026-06-07T04:59:58.000Z");
assert_eq!(builtin["started_utc"], "2026-06-07T05:00:00.000Z");
assert_ne!(builtin["trigger_utc"], builtin["started_utc"]);
assert_eq!(builtin["spawn_tool"], "Agent");
assert_eq!(builtin["spawn_tool_use_id"], "toolu_x");
}
#[test]
fn agents_default_axis_is_trigger_not_start() {
let h = topology_home();
let default_axis = h.run(&[
"agents",
at(SESS).as_str(),
"--since",
"2026-06-07T04:59:59Z",
"--shape",
"builtin-task",
"--format",
"json",
]);
assert!(default_axis.success, "stderr: {}", default_axis.stderr);
let default_has_topo11 = default_axis.stdout.contains("topo11");
assert!(
!default_has_topo11,
"default (trigger) axis must EXCLUDE topo11 triggered before the bound: {}",
default_axis.stdout
);
let by_start = h.run(&[
"agents",
at(SESS).as_str(),
"--since",
"2026-06-07T04:59:59Z",
"--order-by",
"start",
"--shape",
"builtin-task",
"--format",
"json",
]);
assert!(by_start.success, "stderr: {}", by_start.stderr);
assert!(
by_start.stdout.contains("topo11"),
"--order-by start must INCLUDE topo11 started after the bound: {}",
by_start.stdout
);
let footer = h.run(&["agents", at(SESS).as_str()]);
assert!(footer.stdout.contains("window-axis=trigger"));
}
#[test]
fn agents_returned_message_three_way_resolution() {
let h = topology_home();
let out = h.run(&[
"agents",
at(SESS).as_str(),
"--returned-message",
"--format",
"json",
]);
assert!(out.success, "stderr: {}", out.stderr);
let rows = json_rows(&out.stdout, "agent");
let builtin = rows
.iter()
.find(|v| v["agent_id"] == "topo11")
.expect("topo11");
assert_eq!(
builtin["returned_message"],
"SYNC-RETURN: the built-in carry answer"
);
assert_eq!(builtin["returned_message_source"], "sync-tool-result");
let wf = rows
.iter()
.find(|v| v["agent_id"] == "topo22")
.expect("topo22");
assert_eq!(wf["returned_message"], "WF-RETURN: journal payload");
assert_eq!(wf["returned_message_source"], "workflow-journal");
let runs = json_rows(&out.stdout, "run");
assert_eq!(runs.len(), 1, "{}", out.stdout);
assert_eq!(runs[0]["run_id"], "wf_topo");
assert_eq!(runs[0]["workflow_name"], "carry-wf");
assert_eq!(wf["workflow_id"], "wf_topo", "join key intact: {wf}");
}
#[test]
fn agents_returned_message_omitted_by_default() {
let h = topology_home();
let out = h.run(&["agents", at(SESS).as_str(), "--format", "json"]);
assert!(out.success, "stderr: {}", out.stderr);
let rows = json_rows(&out.stdout, "agent");
assert!(!rows.is_empty());
assert!(
rows.iter().all(|n| n.get("returned_message").is_none()),
"returned_message must be omitted by default: {rows:?}"
);
}