feat(agents): make delete permanent, and expose the census
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]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ccbc387f4b
commit
b290025fc4
+31
-23
@@ -1,59 +1,55 @@
|
||||
//! REST API for Clawmates (spec §13). One route resource per module.
|
||||
|
||||
pub mod benchmark_runner;
|
||||
pub mod agent_lifecycle;
|
||||
pub mod agent_names;
|
||||
pub mod auto_merge;
|
||||
pub mod benchmark_runner;
|
||||
pub mod beszel;
|
||||
pub mod brain_seed;
|
||||
pub mod cleanup_sweeper;
|
||||
pub mod agent_names;
|
||||
pub mod mission_gc;
|
||||
pub mod container_exec;
|
||||
pub mod corpus;
|
||||
mod error;
|
||||
pub mod evaluator;
|
||||
pub mod evaluator_tools;
|
||||
mod extract;
|
||||
pub mod fleet;
|
||||
pub mod fleet_herdr;
|
||||
pub mod harvest;
|
||||
pub mod level_up;
|
||||
pub mod library;
|
||||
mod mcp_door;
|
||||
mod mcp_skills;
|
||||
pub mod mission_orchestrator;
|
||||
pub mod mission_refiner;
|
||||
pub mod auto_merge;
|
||||
pub mod corpus;
|
||||
pub mod harvest;
|
||||
pub mod library;
|
||||
pub mod mission_delivery;
|
||||
pub mod mission_events;
|
||||
pub mod microvm_client;
|
||||
pub mod microvm_executor;
|
||||
pub mod microvm_turn_executor;
|
||||
pub mod subscription;
|
||||
pub mod vm_placement;
|
||||
pub mod vm_stop_gate;
|
||||
pub mod vm_tool_tap;
|
||||
pub mod mission_delivery;
|
||||
pub mod mission_events;
|
||||
pub mod mission_fs;
|
||||
pub mod mission_gc;
|
||||
pub mod mission_orchestrator;
|
||||
pub mod mission_outputs;
|
||||
pub mod papers;
|
||||
pub mod phase_config;
|
||||
pub mod session_executor;
|
||||
pub mod repo_digest;
|
||||
pub mod runtime_preflight;
|
||||
pub mod validator_preflight;
|
||||
pub mod mission_plan;
|
||||
pub mod mission_refiner;
|
||||
pub mod mission_roster;
|
||||
pub mod mission_runtime;
|
||||
pub mod mission_workspace;
|
||||
pub mod node_rules;
|
||||
pub mod papers;
|
||||
pub mod phase_config;
|
||||
pub mod phase_runner;
|
||||
pub mod root_copy;
|
||||
pub mod phase_summarizer;
|
||||
pub mod quota;
|
||||
mod recursive_exec;
|
||||
pub mod repo_digest;
|
||||
pub mod root_copy;
|
||||
mod routes;
|
||||
pub mod runtime_preflight;
|
||||
mod runtime_provision;
|
||||
pub mod security_scan;
|
||||
pub mod session_executor;
|
||||
pub mod skills_loader;
|
||||
pub mod subscription;
|
||||
pub mod swarm;
|
||||
pub mod task_card_parser;
|
||||
pub mod task_card_worker;
|
||||
@@ -61,6 +57,10 @@ pub mod team_template_loader;
|
||||
pub mod tool_versions;
|
||||
mod topology_exec;
|
||||
pub mod topology_worker;
|
||||
pub mod validator_preflight;
|
||||
pub mod vm_placement;
|
||||
pub mod vm_stop_gate;
|
||||
pub mod vm_tool_tap;
|
||||
pub mod workflow_registry;
|
||||
|
||||
use axum::routing::{delete, get, patch, post};
|
||||
@@ -243,6 +243,11 @@ pub fn router(state: AppState) -> Router {
|
||||
.route("/api/user/me", get(routes::identity::me))
|
||||
.route("/api/claws", post(routes::claws::create))
|
||||
.route("/api/claws/batch-delete", post(routes::claws::batch_delete))
|
||||
.route("/api/claws/lifecycle", get(routes::claws::lifecycle_census))
|
||||
.route(
|
||||
"/api/claws/lifecycle/sweep",
|
||||
post(routes::claws::lifecycle_sweep),
|
||||
)
|
||||
.route("/api/claws/{id}", patch(routes::claws::patch))
|
||||
.route("/api/claws/{id}", delete(routes::claws::delete))
|
||||
.route("/api/claws/{id}/model", patch(routes::claws::set_model))
|
||||
@@ -510,7 +515,10 @@ pub fn router(state: AppState) -> Router {
|
||||
"/api/missions/refine-draft",
|
||||
post(routes::missions::refine_draft),
|
||||
)
|
||||
.route("/api/missions/{id}/merge", post(routes::missions::merge_branch))
|
||||
.route(
|
||||
"/api/missions/{id}/merge",
|
||||
post(routes::missions::merge_branch),
|
||||
)
|
||||
.route(
|
||||
"/api/missions/{id}/artifacts/{artifact_id}/content",
|
||||
get(routes::missions::artifact_content),
|
||||
|
||||
Reference in New Issue
Block a user