use super::*;
use crate::core::session::{ControlModel, Session, SessionStatus};
use crate::daemon::idle_nudge::has_live_children;
use crate::daemon::state::pending_stops::PENDING_STOP_TTL_SECS;
use crate::daemon::state::sessions::{
DECLARED_STALE_AFTER_SECS, DELEGATION_RETENTION_SECS, RUNNING_STALE_AFTER_SECS,
STALE_RETENTION_SECS,
};
use crate::session_manager::worktree_ownership::SentinelOwner;
use std::sync::Arc;
fn state_with_session() -> (Arc<DaemonState>, SessionId) {
let state = Arc::new(DaemonState::new());
let id = SessionId::new();
let mut s = Session::new(id, "/tmp/p", ControlModel::Tmux, None);
s.status = SessionStatus::Active;
state.register_session(s);
(state, id)
}
fn pre(tool: &str, agent: &str, desc: &str, tool_use_id: &str) -> Value {
serde_json::json!({
"tool": tool,
"cwd": "/tmp/p",
"transcript_path": "/tmp/p/parent.jsonl",
"tool_use_id": tool_use_id,
"input": { "subagent_type": agent, "description": desc, "prompt": "do it" }
})
}
fn post_async(tool: &str, tool_use_id: &str, agent_id: &str) -> Value {
serde_json::json!({
"tool": tool,
"tool_use_id": tool_use_id,
"tool_response": {
"isAsync": true,
"status": "async_launched",
"agentId": agent_id,
"resolvedModel": "claude-haiku-4-5-20251001"
}
})
}
fn stop(agent_id: &str) -> Value {
serde_json::json!({ "agent_id": agent_id, "agent_type": "general-purpose" })
}
fn only(state: &DaemonState, session: SessionId) -> Delegation {
let all = state.delegations_for(session);
assert_eq!(all.len(), 1, "expected exactly one delegation, got {all:?}");
all.into_iter().next().expect("one")
}
#[test]
fn ignores_unrelated_tools() {
let (state, sid) = state_with_session();
let payload = serde_json::json!({ "tool": "Bash", "input": { "command": "ls" } });
observe(&state, sid, HookEvent::PreToolUse, &payload);
assert!(state.delegations_for(sid).is_empty());
}
#[test]
fn pre_tool_use_creates_running_delegation() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "build the thing", "toolu_1"),
);
let d = only(&state, sid);
assert_eq!(d.status, DelegationStatus::Running);
assert_eq!(d.source, DelegationSource::HookObserved);
assert_eq!(d.agent, "engineer");
assert_eq!(d.task, "build the thing");
assert_eq!(d.tool_use_id.as_deref(), Some("toolu_1"));
assert_eq!(d.cwd, Some(std::path::PathBuf::from("/tmp/p")));
assert!(d.started_at.is_some());
}
#[test]
fn pre_tool_use_records_declared_isolation() {
let (state, sid) = state_with_session();
let mut p = pre("Agent", "rust-engineer", "edit files", "toolu_iso");
p["input"]["isolation"] = serde_json::json!("worktree");
observe(&state, sid, HookEvent::PreToolUse, &p);
assert_eq!(only(&state, sid).isolation.as_deref(), Some("worktree"));
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "rust-engineer", "edit files", "toolu_bare"),
);
assert_eq!(only(&state, sid).isolation, None);
}
#[test]
fn legacy_task_tool_name_is_tracked() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Task", "qa", "verify", "toolu_t"),
);
assert_eq!(only(&state, sid).status, DelegationStatus::Running);
}
#[test]
fn duplicate_pre_tool_use_is_idempotent() {
let (state, sid) = state_with_session();
let p = pre("Agent", "engineer", "task", "toolu_1");
observe(&state, sid, HookEvent::PreToolUse, &p);
observe(&state, sid, HookEvent::PreToolUse, &p);
assert_eq!(state.delegations_for(sid).len(), 1);
}
#[test]
fn async_launch_keeps_delegation_running() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "a403cdbc"),
);
let d = only(&state, sid);
assert_eq!(
d.status,
DelegationStatus::Running,
"an async_launched dispatch must stay Running — terminalizing here \
reports 'no agents in flight' while they are all still running"
);
assert_eq!(d.agent_id.as_deref(), Some("a403cdbc"));
assert_eq!(d.tier, ModelTier::Haiku, "tier refined from resolvedModel");
assert!(has_live_children(&state.delegations_for(sid)));
}
#[test]
fn synchronous_post_tool_use_completes_delegation() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Task", "qa", "t", "toolu_s"),
);
let payload = serde_json::json!({
"tool": "Task",
"tool_use_id": "toolu_s",
"tool_response": { "isAsync": false, "is_error": false }
});
observe(&state, sid, HookEvent::PostToolUse, &payload);
let d = only(&state, sid);
assert_eq!(d.status, DelegationStatus::Completed);
assert!(d.ended_at.is_some());
}
#[test]
fn liveness_silent_response_is_read_as_a_return_known_gap() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
let payload = serde_json::json!({
"tool": "Agent",
"tool_use_id": "toolu_1",
"tool_response": { "resolvedModel": "claude-haiku-4-5-20251001" }
});
observe(&state, sid, HookEvent::PostToolUse, &payload);
let d = only(&state, sid);
assert_eq!(
d.status,
DelegationStatus::Completed,
"documented residual — change this assertion only together with the \
stop-side recovery path"
);
assert_eq!(d.tier, ModelTier::Haiku, "tier is still refined");
}
#[test]
fn post_tool_use_failure_marks_failed() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "qa", "t", "toolu_f"),
);
let payload = serde_json::json!({ "tool": "Agent", "tool_use_id": "toolu_f" });
observe(&state, sid, HookEvent::PostToolUseFailure, &payload);
assert_eq!(only(&state, sid).status, DelegationStatus::Failed);
}
#[test]
fn subagent_stop_completes_matching_delegation() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "a403cdbc"),
);
observe(&state, sid, HookEvent::SubagentStop, &stop("a403cdbc"));
let d = only(&state, sid);
assert_eq!(d.status, DelegationStatus::Completed);
assert!(d.ended_at.is_some());
assert!(!has_live_children(&state.delegations_for(sid)));
}
fn stop_of_type(agent_id: &str, agent_type: &str) -> Value {
serde_json::json!({ "agent_id": agent_id, "agent_type": agent_type })
}
#[test]
fn a_completed_agents_record_stops_blocking_when_its_id_was_never_taught() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "rust-engineer", "fix the bug", "toolu_lost"),
);
let cwd = std::path::PathBuf::from("/tmp/p");
assert_eq!(
state.live_shared_tree_writers(&cwd, None).len(),
1,
"the live record blocks an ADR-0049 commit while the agent is running"
);
observe(
&state,
sid,
HookEvent::SubagentStop,
&stop_of_type("a403cdbc", "rust-engineer"),
);
let d = only(&state, sid);
assert_eq!(
d.status,
DelegationStatus::Stale,
"identified by type, so `Stale` — tracking gave up, the agent is not asserted finished"
);
assert!(
d.stale_by_agent_type,
"and flagged as type-reconciled, so it still OCCUPIES the tree for a dispatch \
(#6556 critic round, HIGH 1)"
);
assert!(
d.ended_at.is_none(),
"staling must not stamp ended_at, which means `reached a terminal status`"
);
assert!(
state.live_shared_tree_writers(&cwd, None).is_empty(),
"a completed agent's record must stop blocking a documents-only commit"
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_lost", "a403cdbc"),
);
assert_eq!(
only(&state, sid).status,
DelegationStatus::Completed,
"the deferred stop must still be able to correct a staled record"
);
}
#[test]
fn two_unresolved_records_of_one_type_are_left_alone() {
let (state, sid) = state_with_session();
for id in ["toolu_a", "toolu_b"] {
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "rust-engineer", id, id),
);
}
observe(
&state,
sid,
HookEvent::SubagentStop,
&stop_of_type("a403cdbc", "rust-engineer"),
);
let live = state.delegations_for(sid);
assert_eq!(live.len(), 2);
assert!(
live.iter().all(|d| d.status == DelegationStatus::Running),
"ambiguity must decline rather than guess: {live:?}"
);
}
#[test]
fn a_stop_without_an_agent_type_reconciles_nothing() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "rust-engineer", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::SubagentStop,
&serde_json::json!({ "agent_id": "a403cdbc" }),
);
assert_eq!(
only(&state, sid).status,
DelegationStatus::Running,
"no agent_type is no evidence about which record ended"
);
}
#[test]
fn reconciliation_never_touches_a_record_that_learned_its_id() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "rust-engineer", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "known-id"),
);
observe(
&state,
sid,
HookEvent::SubagentStop,
&stop_of_type("other-id", "rust-engineer"),
);
assert_eq!(
only(&state, sid).status,
DelegationStatus::Running,
"a record with an id is only ever closed by that id"
);
}
#[test]
fn subagent_stop_failure_marks_failed() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "e", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
observe(&state, sid, HookEvent::SubagentStopFailure, &stop("aid1"));
assert_eq!(only(&state, sid).status, DelegationStatus::Failed);
}
#[test]
fn subagent_stop_without_agent_id_terminalizes_nothing() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "a403cdbc"),
);
observe(
&state,
sid,
HookEvent::SubagentStop,
&serde_json::json!({ "transcript_path": "/tmp/p/parent.jsonl" }),
);
assert_eq!(only(&state, sid).status, DelegationStatus::Running);
assert!(has_live_children(&state.delegations_for(sid)));
}
#[test]
fn subagent_stop_with_unknown_agent_id_is_a_noop() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "e", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "known"),
);
observe(&state, sid, HookEvent::SubagentStop, &stop("stranger"));
assert_eq!(only(&state, sid).status, DelegationStatus::Running);
}
#[test]
fn subagent_stop_is_idempotent() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "e", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
let first = only(&state, sid).ended_at;
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert_eq!(only(&state, sid).ended_at, first, "must not re-terminalize");
}
#[test]
fn concurrent_delegations_terminalize_independently() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "alpha", "a", "toolu_a"),
);
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "beta", "b", "toolu_b"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_a", "aid_alpha"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_b", "aid_beta"),
);
assert_eq!(state.delegations_for(sid).len(), 2);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid_alpha"));
let all = state.delegations_for(sid);
let alpha = all.iter().find(|d| d.agent == "alpha").expect("alpha");
let beta = all.iter().find(|d| d.agent == "beta").expect("beta");
assert_eq!(alpha.status, DelegationStatus::Completed);
assert_eq!(
beta.status,
DelegationStatus::Running,
"terminalizing alpha must NOT close the concurrently-running beta"
);
assert!(
has_live_children(&all),
"beta is still live, so the session must not look idle"
);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid_beta"));
assert!(!has_live_children(&state.delegations_for(sid)));
}
#[test]
fn dedups_declaration_and_observation() {
let (state, sid) = state_with_session();
let declared = Delegation::new(sid, None, "engineer", ModelTier::Opus, "declared task");
let declared_id = declared.id;
state.upsert_delegation(declared);
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "declared task", "toolu_1"),
);
let d = only(&state, sid);
assert_eq!(d.id, declared_id, "must reuse the declared record");
assert_eq!(d.status, DelegationStatus::Running);
assert_eq!(d.tool_use_id.as_deref(), Some("toolu_1"));
assert_eq!(d.source, DelegationSource::McpDeclared);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert_eq!(only(&state, sid).status, DelegationStatus::Completed);
}
#[test]
fn dedup_ignores_different_agent() {
let (state, sid) = state_with_session();
state.upsert_delegation(Delegation::new(
sid,
None,
"research",
ModelTier::Sonnet,
"investigate",
));
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "build", "toolu_1"),
);
assert_eq!(
state.delegations_for(sid).len(),
2,
"a different agent is a different delegation"
);
}
#[test]
fn dedup_window_expires() {
let (state, sid) = state_with_session();
let mut old = Delegation::new(sid, None, "engineer", ModelTier::Sonnet, "same task");
old.created_at = Utc::now() - chrono::Duration::seconds(DEDUP_WINDOW_SECS + 60);
state.upsert_delegation(old);
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "same task", "toolu_1"),
);
assert_eq!(state.delegations_for(sid).len(), 2);
}
#[test]
fn dedup_does_not_steal_an_already_bound_record() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "first", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "second", "toolu_2"),
);
assert_eq!(state.delegations_for(sid).len(), 2);
}
#[test]
fn delegations_are_scoped_per_session() {
let (state, sid_a) = state_with_session();
let sid_b = SessionId::new();
let mut s = Session::new(sid_b, "/tmp/q", ControlModel::Tmux, None);
s.status = SessionStatus::Active;
state.register_session(s);
for sid in [sid_a, sid_b] {
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_shared"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_shared", "aid_shared"),
);
}
observe(&state, sid_a, HookEvent::SubagentStop, &stop("aid_shared"));
assert_eq!(only(&state, sid_a).status, DelegationStatus::Completed);
assert_eq!(
only(&state, sid_b).status,
DelegationStatus::Running,
"a stop in session A must not touch session B"
);
}
#[test]
fn post_tool_use_without_tool_response_stays_running() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
let payload = serde_json::json!({ "tool": "Agent", "tool_use_id": "toolu_1" });
observe(&state, sid, HookEvent::PostToolUse, &payload);
let d = only(&state, sid);
assert_eq!(
d.status,
DelegationStatus::Running,
"no response is 'unknown', not 'finished' — terminalizing here reports \
'no agents in flight' while the subagent is still running"
);
assert!(d.ended_at.is_none());
assert!(has_live_children(&state.delegations_for(sid)));
}
#[test]
fn post_tool_use_with_unrecognized_response_stays_running() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
let payload = serde_json::json!({
"tool": "Agent",
"tool_use_id": "toolu_1",
"tool_response": { "content": [{ "type": "text", "text": "…" }] }
});
observe(&state, sid, HookEvent::PostToolUse, &payload);
assert_eq!(only(&state, sid).status, DelegationStatus::Running);
assert!(has_live_children(&state.delegations_for(sid)));
}
#[test]
fn post_tool_use_with_only_an_agent_id_stays_running() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
let payload = serde_json::json!({
"tool": "Agent",
"tool_use_id": "toolu_1",
"tool_response": { "agentId": "aid_p1" }
});
observe(&state, sid, HookEvent::PostToolUse, &payload);
let d = only(&state, sid);
assert_eq!(d.status, DelegationStatus::Running);
assert_eq!(
d.agent_id.as_deref(),
Some("aid_p1"),
"the join key is still learned — it is just not read as completion"
);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid_p1"));
assert_eq!(only(&state, sid).status, DelegationStatus::Completed);
}
#[test]
fn changed_async_status_value_with_an_agent_id_stays_running() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
let payload = serde_json::json!({
"tool": "Agent",
"tool_use_id": "toolu_1",
"tool_response": { "status": "launched", "agentId": "aid_p3" }
});
observe(&state, sid, HookEvent::PostToolUse, &payload);
assert_eq!(only(&state, sid).status, DelegationStatus::Running);
}
#[test]
fn null_agent_id_does_not_launch_a_phantom_delegation() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
let payload = serde_json::json!({
"tool": "Agent",
"tool_use_id": "toolu_1",
"tool_response": { "agentId": null }
});
observe(&state, sid, HookEvent::PostToolUse, &payload);
let d = only(&state, sid);
assert_eq!(
d.status,
DelegationStatus::Completed,
"a null agentId is not a usable handle and carries no other launch \
marker, so it must not pin the delegation Running with nothing left \
to resolve it"
);
assert_eq!(d.agent_id, None);
}
#[test]
fn empty_string_agent_id_does_not_launch_a_phantom_delegation() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
let payload = serde_json::json!({
"tool": "Agent",
"tool_use_id": "toolu_1",
"tool_response": { "agentId": "" }
});
observe(&state, sid, HookEvent::PostToolUse, &payload);
let d = only(&state, sid);
assert_eq!(
d.status,
DelegationStatus::Completed,
"an empty-string agentId is not a usable handle and carries no other \
launch marker, so it must not pin the delegation Running with \
nothing left to resolve it"
);
assert_eq!(d.agent_id, None);
}
#[test]
fn renamed_async_marker_does_not_terminalize() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
let payload = serde_json::json!({
"tool": "Agent",
"tool_use_id": "toolu_1",
"tool_response": { "agent_id": "a403", "is_async": true, "state": "async_launched" }
});
observe(&state, sid, HookEvent::PostToolUse, &payload);
let d = only(&state, sid);
assert_eq!(d.status, DelegationStatus::Running);
assert!(
d.agent_id.is_none(),
"no key we recognise, so nothing learned"
);
}
#[test]
fn stale_running_delegation_stops_suppressing_the_nudge() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
assert!(has_live_children(&state.delegations_for(sid)));
let sweep = state.sweep_delegations_at(Utc::now() + chrono::Duration::hours(3));
assert_eq!(sweep.staled, 0, "a 3h-old agent is still plausibly running");
assert!(has_live_children(&state.delegations_for(sid)));
let past = Utc::now() + chrono::Duration::seconds(RUNNING_STALE_AFTER_SECS + 60);
let sweep = state.sweep_delegations_at(past);
assert_eq!(sweep.staled, 1);
let d = only(&state, sid);
assert_eq!(
d.status,
DelegationStatus::Stale,
"tracking gave up — but it must NOT claim the agent completed"
);
assert_ne!(d.status, DelegationStatus::Completed);
assert!(!has_live_children(&state.delegations_for(sid)));
}
#[test]
fn late_subagent_stop_still_resolves_a_stale_delegation() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
state
.sweep_delegations_at(Utc::now() + chrono::Duration::seconds(RUNNING_STALE_AFTER_SECS + 1));
assert_eq!(only(&state, sid).status, DelegationStatus::Stale);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert_eq!(
only(&state, sid).status,
DelegationStatus::Completed,
"a late stop replaces 'we lost track' with the truth"
);
}
#[test]
fn declared_but_never_dispatched_goes_stale_quickly() {
let (state, sid) = state_with_session();
state.upsert_delegation(Delegation::new(
sid,
None,
"engineer",
ModelTier::Sonnet,
"declared",
));
let sweep = state.sweep_delegations_at(
Utc::now() + chrono::Duration::seconds(DECLARED_STALE_AFTER_SECS + 1),
);
assert_eq!(sweep.staled, 1);
assert_eq!(only(&state, sid).status, DelegationStatus::Stale);
assert!(!has_live_children(&state.delegations_for(sid)));
}
#[test]
fn terminal_delegations_are_evicted_after_retention() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert_eq!(state.delegations_for(sid).len(), 1);
let sweep = state.sweep_delegations_at(
Utc::now() + chrono::Duration::seconds(DELEGATION_RETENTION_SECS + 1),
);
assert_eq!(sweep.evicted, 1);
assert!(
state.delegations_for(sid).is_empty(),
"the map must not grow monotonically for the daemon's lifetime"
);
}
#[test]
fn live_delegations_are_never_evicted() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
let sweep = state.sweep_delegations_at(Utc::now() + chrono::Duration::days(30));
assert_eq!(sweep.evicted, 0, "a live record is never evicted");
assert_eq!(state.delegations_for(sid).len(), 1);
}
#[test]
fn a_stale_delegation_has_no_ended_at() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
state
.sweep_delegations_at(Utc::now() + chrono::Duration::seconds(RUNNING_STALE_AFTER_SECS + 1));
let d = only(&state, sid);
assert_eq!(d.status, DelegationStatus::Stale);
assert!(
d.ended_at.is_none(),
"the sweep must not claim the delegation ended — it did not end, we \
stopped trusting the record"
);
}
#[test]
fn stale_delegation_stays_resolvable_far_past_the_terminal_window() {
let (state, sid) = state_with_session();
let t0 = Utc::now();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
state.sweep_delegations_at(t0 + chrono::Duration::seconds(RUNNING_STALE_AFTER_SECS + 1));
assert_eq!(only(&state, sid).status, DelegationStatus::Stale);
state.sweep_delegations_at(
t0 + chrono::Duration::seconds(RUNNING_STALE_AFTER_SECS + DELEGATION_RETENTION_SECS + 60),
);
assert_eq!(
state.delegations_for(sid).len(),
1,
"the record that used to vanish at ~7 h must still be here"
);
let sweep =
state.sweep_delegations_at(t0 + chrono::Duration::seconds(STALE_RETENTION_SECS - 60));
assert_eq!(sweep.evicted, 0);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert_eq!(
only(&state, sid).status,
DelegationStatus::Completed,
"a stop arriving ~24 h later still replaces 'we lost track' with the truth"
);
}
#[test]
fn a_stale_delegation_is_eventually_evicted() {
let (state, sid) = state_with_session();
let t0 = Utc::now();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
state.sweep_delegations_at(t0 + chrono::Duration::seconds(RUNNING_STALE_AFTER_SECS + 1));
let sweep =
state.sweep_delegations_at(t0 + chrono::Duration::seconds(STALE_RETENTION_SECS + 60));
assert_eq!(sweep.evicted, 1);
assert!(state.delegations_for(sid).is_empty());
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert!(state.delegations_for(sid).is_empty());
}
#[test]
fn dedup_does_not_merge_a_different_task() {
let (state, sid) = state_with_session();
state.upsert_delegation(Delegation::new(
sid,
None,
"rust-engineer",
ModelTier::Sonnet,
"fix the delegation tracker",
));
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre(
"Agent",
"rust-engineer",
"rebase the release branch",
"toolu_2",
),
);
let all = state.delegations_for(sid);
assert_eq!(all.len(), 2, "different work is a different delegation");
let observed = all
.iter()
.find(|d| d.source == DelegationSource::HookObserved)
.expect("observed record");
assert_eq!(
observed.task, "rebase the release branch",
"the dispatched task text must survive"
);
}
#[test]
fn dedup_declines_a_description_less_dispatch() {
let (state, sid) = state_with_session();
state.upsert_delegation(Delegation::new(
sid,
None,
"engineer",
ModelTier::Sonnet,
"fix the delegation tracker",
));
let payload = serde_json::json!({
"tool": "Agent",
"tool_use_id": "toolu_1",
"input": { "subagent_type": "engineer" }
});
observe(&state, sid, HookEvent::PreToolUse, &payload);
assert_eq!(state.delegations_for(sid).len(), 2);
}
#[test]
fn dedup_merges_an_unlabelled_declaration() {
let (state, sid) = state_with_session();
let declared = Delegation::new(sid, None, "engineer", ModelTier::Opus, "");
let declared_id = declared.id;
state.upsert_delegation(declared);
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "rebase the release branch", "toolu_1"),
);
let d = only(&state, sid);
assert_eq!(d.id, declared_id);
assert_eq!(d.task, "rebase the release branch");
}
#[test]
fn dedup_merges_a_summarised_task_description() {
let (state, sid) = state_with_session();
let declared = Delegation::new(
sid,
None,
"engineer",
ModelTier::Opus,
"Fix the delegation tracker fail-open bug",
);
let declared_id = declared.id;
state.upsert_delegation(declared);
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre(
"Agent",
"engineer",
"fix the delegation TRACKER",
"toolu_1",
),
);
let d = only(&state, sid);
assert_eq!(d.id, declared_id);
assert_eq!(d.status, DelegationStatus::Running);
}
#[test]
fn missing_subagent_type_still_tracks_as_live() {
let (state, sid) = state_with_session();
let payload = serde_json::json!({ "tool": "Agent", "tool_use_id": "toolu_x" });
observe(&state, sid, HookEvent::PreToolUse, &payload);
let d = only(&state, sid);
assert_eq!(d.agent, "unknown");
assert_eq!(d.status, DelegationStatus::Running);
}
fn subagent_call(agent_id: &str, cwd: &str) -> Value {
serde_json::json!({
"tool": "Bash",
"cwd": cwd,
"agent_id": agent_id,
"input": { "command": "ls" }
})
}
#[test]
fn subagent_tool_call_registers_its_worktree() {
let (state, sid) = state_with_session();
let tmp = tempfile::tempdir().expect("tempdir");
let wt = harness_store_dir(&tmp, "agent-a403");
dispatched_from(&state, sid, tmp.path(), "a403", "toolu_1");
assert_eq!(only(&state, sid).worktree_path, None, "nothing yet");
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("a403", &wt.to_string_lossy()),
);
assert_eq!(
only(&state, sid).worktree_path.as_deref(),
Some(wt.as_path()),
"the subagent's own cwd is the tree it was granted"
);
assert!(
matches!(
crate::session_manager::worktree_ownership::read_sentinel_owner(&wt),
SentinelOwner::Agent(owner, _) if owner.agent_id == "a403"
),
"the tree must carry an ownership sentinel naming this agent"
);
}
#[test]
fn subagent_tool_calls_track_the_current_working_directory() {
let (state, sid) = state_with_session();
let tmp = tempfile::tempdir().expect("tempdir");
let wt = harness_store_dir(&tmp, "agent-enterexit");
dispatched_from(&state, sid, tmp.path(), "enterexit", "toolu_1");
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("enterexit", &wt.to_string_lossy()),
);
let d = only(&state, sid);
assert_eq!(d.worktree_path.as_deref(), Some(wt.as_path()));
assert_eq!(
d.last_agent_cwd.as_deref(),
Some(wt.as_path()),
"the current directory is recorded alongside the grant"
);
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("enterexit", &tmp.path().to_string_lossy()),
);
let d = only(&state, sid);
assert_eq!(
d.last_agent_cwd.as_deref(),
Some(tmp.path()),
"the agent moved, and the record must say so"
);
assert_eq!(
d.worktree_path.as_deref(),
Some(wt.as_path()),
"while the ownership latch survives — the reap still needs the tree's owner"
);
}
#[test]
fn a_failed_sentinel_write_registers_no_worktree() {
let (state, sid) = state_with_session();
let tmp = tempfile::tempdir().expect("tempdir");
let wt = harness_store_dir(&tmp, "agent-a405");
dispatched_from(&state, sid, tmp.path(), "a405", "toolu_1");
std::fs::create_dir(wt.join(".trusty-mpm-worktree")).expect("occupy the sentinel path");
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("a405", &wt.to_string_lossy()),
);
assert_eq!(
only(&state, sid).worktree_path,
None,
"an unattributable tree must stay unregistered — owner-unknown is never \
auto-deleted, and that is the safe side of this failure"
);
}
fn harness_store_dir(tmp: &tempfile::TempDir, name: &str) -> std::path::PathBuf {
let wt = tmp.path().join(".claude").join("worktrees").join(name);
std::fs::create_dir_all(&wt).expect("create harness worktree dir");
wt
}
fn dispatched_from(
state: &Arc<DaemonState>,
sid: SessionId,
from: &std::path::Path,
agent_id: &str,
tool_use_id: &str,
) {
let mut payload = pre("Agent", "rust-engineer", "build it", tool_use_id);
payload["cwd"] = serde_json::json!(from.to_string_lossy());
observe(state, sid, HookEvent::PreToolUse, &payload);
observe(
state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", tool_use_id, agent_id),
);
}
fn dispatched(state: &Arc<DaemonState>, sid: SessionId, agent_id: &str, tool_use_id: &str) {
observe(
state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "rust-engineer", "build it", tool_use_id),
);
observe(
state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", tool_use_id, agent_id),
);
}
#[test]
fn a_cwd_outside_the_harness_store_gets_no_sentinel() {
let (state, sid) = state_with_session();
dispatched(&state, sid, "a601", "toolu_1");
let elsewhere = tempfile::tempdir().expect("tempdir");
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("a601", &elsewhere.path().to_string_lossy()),
);
assert_eq!(
only(&state, sid).worktree_path,
None,
"a path outside the harness store must not be registered"
);
assert_eq!(
crate::session_manager::worktree_ownership::read_sentinel_owner(elsewhere.path()),
SentinelOwner::Unknown,
"and no sentinel may be left behind in a directory trusty-mpm does not own"
);
}
#[test]
fn a_cwd_owned_by_another_agent_is_never_overwritten() {
let (state, sid) = state_with_session();
let tmp = tempfile::tempdir().expect("tempdir");
dispatched_from(&state, sid, tmp.path(), "a602", "toolu_1");
let peer_tree = harness_store_dir(&tmp, "agent-peer");
crate::session_manager::worktree_ownership::write_agent_sentinel(
&peer_tree,
crate::session_manager::worktree_ownership::AgentWorktreeOwner {
agent_id: "a-the-peer".to_string(),
delegation_id: crate::core::agent::DelegationId(uuid::Uuid::new_v4()),
parent_session_id: sid,
},
)
.expect("write the peer's sentinel");
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("a602", &peer_tree.to_string_lossy()),
);
assert_eq!(only(&state, sid).worktree_path, None, "must not register");
assert!(
matches!(
crate::session_manager::worktree_ownership::read_sentinel_owner(&peer_tree),
SentinelOwner::Agent(owner, _) if owner.agent_id == "a-the-peer"
),
"the peer's sentinel must survive untouched"
);
}
#[test]
fn a_cwd_owned_by_a_managed_session_is_never_overwritten() {
let (state, sid) = state_with_session();
let tmp = tempfile::tempdir().expect("tempdir");
dispatched_from(&state, sid, tmp.path(), "a603", "toolu_1");
let session_tree = harness_store_dir(&tmp, "agent-looks-like-one");
let owner = crate::session_manager::record::ManagedSessionId::new();
std::fs::write(
session_tree.join(".trusty-mpm-worktree"),
crate::session_manager::worktree_ownership::sentinel_payload_bytes(owner),
)
.expect("write a session sentinel");
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("a603", &session_tree.to_string_lossy()),
);
assert_eq!(only(&state, sid).worktree_path, None, "must not register");
assert!(
matches!(
crate::session_manager::worktree_ownership::read_sentinel_owner(&session_tree),
SentinelOwner::Known(got, _) if got == owner
),
"the session's sentinel must survive untouched"
);
}
#[test]
fn a_cwd_in_another_projects_store_is_refused() {
let (state, sid) = state_with_session();
dispatched(&state, sid, "a605", "toolu_1");
let other_project = tempfile::tempdir().expect("tempdir");
let foreign = harness_store_dir(&other_project, "agent-elsewhere");
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("a605", &foreign.to_string_lossy()),
);
assert_eq!(
only(&state, sid).worktree_path,
None,
"a harness-shaped path in another project's store must not be claimed"
);
assert_eq!(
crate::session_manager::worktree_ownership::read_sentinel_owner(&foreign),
SentinelOwner::Unknown,
"and no sentinel may be planted there"
);
}
#[test]
fn a_cwd_another_live_delegation_holds_is_refused() {
let (state, sid) = state_with_session();
let tmp = tempfile::tempdir().expect("tempdir");
let peer_tree = harness_store_dir(&tmp, "agent-live-peer");
dispatched_from(&state, sid, tmp.path(), "a606", "toolu_1");
let mut sibling = crate::core::agent::Delegation::observed(
crate::core::session::SessionId::new(),
"rust-engineer",
"peer work",
Some("toolu_peer".to_string()),
);
sibling.agent_id = Some("a-live-peer".to_string());
sibling.worktree_path = Some(peer_tree.clone());
state.upsert_delegation(sibling);
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("a606", &peer_tree.to_string_lossy()),
);
let claimed = state
.delegations_for(sid)
.into_iter()
.find(|d| d.agent_id.as_deref() == Some("a606"))
.and_then(|d| d.worktree_path);
assert_eq!(
claimed, None,
"a tree a running sibling already registers must not be claimed"
);
}
#[test]
fn an_agent_may_rewrite_its_own_sentinel() {
let (state, sid) = state_with_session();
let tmp = tempfile::tempdir().expect("tempdir");
dispatched_from(&state, sid, tmp.path(), "a604", "toolu_1");
let own_tree = harness_store_dir(&tmp, "agent-a604");
crate::session_manager::worktree_ownership::write_agent_sentinel(
&own_tree,
crate::session_manager::worktree_ownership::AgentWorktreeOwner {
agent_id: "a604".to_string(),
delegation_id: crate::core::agent::DelegationId(uuid::Uuid::new_v4()),
parent_session_id: sid,
},
)
.expect("write its own earlier sentinel");
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("a604", &own_tree.to_string_lossy()),
);
assert_eq!(
only(&state, sid).worktree_path.as_deref(),
Some(own_tree.as_path()),
"an agent must be able to re-register its own tree"
);
}
#[test]
fn concurrent_subagents_register_their_own_trees() {
let (state, sid) = state_with_session();
let tmp = tempfile::tempdir().expect("tempdir");
let trees: Vec<_> = ["a501", "a502", "a503"]
.iter()
.enumerate()
.map(|(i, agent)| {
dispatched_from(&state, sid, tmp.path(), agent, &format!("toolu_{i}"));
(*agent, harness_store_dir(&tmp, &format!("agent-{agent}")))
})
.collect();
std::thread::scope(|s| {
for (agent, wt) in &trees {
let state = &state;
s.spawn(move || {
observe(
state,
sid,
HookEvent::PreToolUse,
&subagent_call(agent, &wt.to_string_lossy()),
);
});
}
});
for (agent, wt) in &trees {
let recorded = state
.delegations_for(sid)
.into_iter()
.find(|d| d.agent_id.as_deref() == Some(*agent))
.and_then(|d| d.worktree_path)
.unwrap_or_else(|| panic!("{agent} registered no worktree"));
assert_eq!(&recorded, wt, "{agent} must hold its OWN tree");
assert!(
matches!(
crate::session_manager::worktree_ownership::read_sentinel_owner(wt),
SentinelOwner::Agent(owner, _) if owner.agent_id == *agent
),
"{agent}'s tree must carry {agent}'s sentinel, not a sibling's"
);
}
}
#[test]
fn subagent_sharing_the_dispatchers_tree_registers_nothing() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "documentation", "write it", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "a404"),
);
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("a404", "/tmp/p"),
);
assert_eq!(only(&state, sid).worktree_path, None);
}
#[test]
fn an_unknown_agent_id_registers_nothing() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&subagent_call("ghost", "/tmp/p/.claude/worktrees/ghost"),
);
assert!(state.delegations_for(sid).is_empty());
}
fn by_tool_use(state: &DaemonState, session: SessionId, tool_use_id: &str) -> Delegation {
state
.delegations_for(session)
.into_iter()
.find(|d| d.tool_use_id.as_deref() == Some(tool_use_id))
.unwrap_or_else(|| panic!("no delegation carries tool_use_id {tool_use_id}"))
}
#[test]
fn out_of_order_subagent_stop_resolves_when_its_post_tool_use_lands() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "first", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "qa", "second", "toolu_2"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_2", "aid2"),
);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert_eq!(
by_tool_use(&state, sid, "toolu_1").status,
DelegationStatus::Running,
"nothing may be terminalized before the id is known"
);
assert_eq!(
by_tool_use(&state, sid, "toolu_2").status,
DelegationStatus::Running,
"an unmatched stop must never close another delegation"
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
let resolved = by_tool_use(&state, sid, "toolu_1");
assert_eq!(
resolved.status,
DelegationStatus::Completed,
"the out-of-order stop must resolve without waiting on the staleness sweep"
);
assert!(resolved.ended_at.is_some(), "ended_at must be stamped");
assert_eq!(
by_tool_use(&state, sid, "toolu_2").status,
DelegationStatus::Running,
"the control delegation must still be live"
);
assert!(has_live_children(&state.delegations_for(sid)));
}
#[test]
fn an_out_of_order_stop_failure_lands_as_failed() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "t", "toolu_1"),
);
observe(&state, sid, HookEvent::SubagentStopFailure, &stop("aid1"));
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
assert_eq!(only(&state, sid).status, DelegationStatus::Failed);
}
#[test]
fn a_pending_stop_never_terminalizes_a_delegation_it_does_not_name() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "engineer", "one", "toolu_1"),
);
observe(&state, sid, HookEvent::SubagentStop, &stop("stranger"));
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
assert_eq!(
only(&state, sid).status,
DelegationStatus::Running,
"a stop naming another agent must never close this one"
);
}
#[test]
fn a_redelivered_stop_for_a_finished_delegation_records_no_pending_stop() {
let (state, sid) = state_with_session();
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "e", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert_eq!(state.pending_stops().len(), 0);
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert_eq!(
state.pending_stops().len(),
0,
"a redelivered stop for a terminal delegation must record nothing"
);
}
#[test]
fn an_unmatched_post_tool_use_does_not_consume_the_pending_stop() {
let (state, sid) = state_with_session();
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert_eq!(state.pending_stops().len(), 1);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_ghost", "aid1"),
);
assert_eq!(
state.pending_stops().len(),
1,
"an unresolved launch must not spend the deferred stop"
);
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "e", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
assert_eq!(only(&state, sid).status, DelegationStatus::Completed);
assert_eq!(state.pending_stops().len(), 0, "and is spent exactly once");
}
#[test]
fn a_pending_stop_does_not_cross_sessions() {
let (state, sid) = state_with_session();
let other = SessionId::new();
let mut s = Session::new(other, "/tmp/q", ControlModel::Tmux, None);
s.status = SessionStatus::Active;
state.register_session(s);
observe(&state, other, HookEvent::SubagentStop, &stop("aid1"));
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "e", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
assert_eq!(
only(&state, sid).status,
DelegationStatus::Running,
"another session's stop must not resolve this one"
);
}
#[test]
fn an_expired_pending_stop_never_resolves_a_later_launch() {
let (state, sid) = state_with_session();
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
let past_ttl = chrono::Utc::now() + chrono::Duration::seconds(PENDING_STOP_TTL_SECS + 1);
state.sweep_delegations_at(past_ttl);
assert_eq!(state.pending_stops().len(), 0, "the sweep ages the ledger");
observe(
&state,
sid,
HookEvent::PreToolUse,
&pre("Agent", "e", "t", "toolu_1"),
);
observe(
&state,
sid,
HookEvent::PostToolUse,
&post_async("Agent", "toolu_1", "aid1"),
);
assert_eq!(only(&state, sid).status, DelegationStatus::Running);
}
#[test]
fn the_delegation_sweep_ages_the_deferred_stop_ledger() {
let (state, sid) = state_with_session();
observe(&state, sid, HookEvent::SubagentStop, &stop("aid1"));
assert_eq!(state.pending_stops().len(), 1);
state.sweep_delegations_at(chrono::Utc::now());
assert_eq!(state.pending_stops().len(), 1, "a fresh entry survives");
state.sweep_delegations_at(
chrono::Utc::now() + chrono::Duration::seconds(PENDING_STOP_TTL_SECS + 1),
);
assert_eq!(state.pending_stops().len(), 0);
}