feat(agents): classify agents by lifecycle and reap the finished and orphaned
A mission mints a crew, and the only thing that reaped one was DELETING the mission. A mission that merely completed left its agents in the roster forever, and a crew whose reap was skipped or failed left agents bound to nothing — indistinguishable in the UI from the operator's own staff. Four states, from one query: owned no agent_template_link row → hand-created. NEVER reaped. active on a running/draft mission → working right now. Kept. completed every mission terminal → reaped after a 24h grace. orphaned minted, bound to nothing → reaped. The discriminator is `agent_template_link`, which mission_orchestrator writes per minted claw. This matters more than it looks: verified on live data, a hand-created agent and an orphaned crew member both have ZERO team links and are structurally identical by binding alone. Judging orphanhood by "no team" would delete the user's workforce. Provenance is the only honest signal. The grace window exists because the results view, the World's 24h replay and "who did this work?" all read the crew AFTER the run ends; reaping on the terminal transition deletes the answer exactly when the question gets asked. A completed crew with no usable timestamp is KEPT — a missing date must never read as "old enough to delete". Also fixes the delete summary, which reported how many claws were FOUND rather than purged: "reaped 4 claw(s)" was printed by a delete that purged none, which is precisely the log you would read while wondering why the agents are still there. It now reports purged / kept / FAILED, and failed > 0 is the orphan case. Verified against live data — all four states observed, including the two that look alike. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
eb120a10dd
commit
a494634f81
@@ -401,6 +401,16 @@ async fn run() -> Result<(), String> {
|
||||
// row has never deleted a directory — which is why the gateway, the smallest
|
||||
// disk in the fleet, accumulates mission trees that nothing reclaims.
|
||||
cm_api::mission_gc::spawn(pool.clone(), std::time::Duration::from_secs(3600));
|
||||
// Agent lifecycle: reap crews whose missions finished (after a 24h grace so
|
||||
// the results view can still show who did the work) and crews left bound to
|
||||
// nothing. Never touches an agent without an `agent_template_link` row —
|
||||
// that is the operator's own staff, which looks identical to an orphan if
|
||||
// you judge by team membership alone.
|
||||
cm_api::agent_lifecycle::spawn(
|
||||
pool.clone(),
|
||||
runtime.clone(),
|
||||
std::time::Duration::from_secs(3600),
|
||||
);
|
||||
// Fleet backstop: a node whose heartbeats stop (without a clean channel
|
||||
// close) goes offline within ~28s even if its control channel hangs.
|
||||
cm_api::fleet::spawn_node_sweeper(pool.clone(), std::time::Duration::from_secs(8), 20);
|
||||
|
||||
Reference in New Issue
Block a user