A soft delete marked the row and left it. The agent stayed in the table forever,
kept appearing on any surface that forgot `deleted_at IS NULL`, and deleting it
again did nothing — the decision was recorded and never honoured. Two agents on
this deployment had been in that state since June.
`deleted` is now a fifth lifecycle state, collected with NO grace window: a
human already decided, months ago. It takes usage_events with it, which is the
explicit trade — the alternative is rows that outlive the decision to delete
them.
Two endpoints, because this was previously only answerable by reading the
database by hand:
GET /api/claws/lifecycle the census: who is active, completed,
orphaned, deleted — and what is reapable
POST /api/claws/lifecycle/sweep run the reap now, rather than waiting out
the hourly timer for a decision already made
Verified end to end: census reported both as `deleted`/`reapable`, the sweep
returned {"reaped":2,"failed":0}, and agents and usage_events both went to 0.
The safety property is unchanged and re-asserted by a new test: adding `deleted`
did not make `owned` or `active` reapable.
Co-Authored-By: Claude Opus 5 <[email protected]>
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]>