feat(#2446): release a DAG's per-agent lease when that agent's subgraph is terminal

A per-agent lifecycle lease gates that agent's container globally across
concurrent DAGs, so it should be held for exactly as long as the agent's
work in a DAG is in flight, no longer. settle() previously freed every
lease a DAG held only at whole-DAG terminal, so a multi-agent DAG (a
hive-wide restart) kept agent A's container locked until B and C also
finished, blocking any other DAG wanting A.

Now free each agent's lease the moment its own subgraph within the DAG is
terminal (no live node still targets it), and drop that agent's dashboard
transient pill on the same edge via a new per-agent release channel. A
single-agent DAG is unaffected: its agent's subgraph goes terminal exactly
when the whole DAG does, so behaviour is identical.
This commit is contained in:
atlas 2026-07-15 23:36:35 +02:00 committed by mara
commit 9fdadb99c0
4 changed files with 136 additions and 25 deletions

View file

@ -302,6 +302,19 @@ impl Dag {
self.nodes.iter().all(|n| n.state.is_terminal())
}
/// True when no live (non-terminal) node of this DAG still targets
/// `agent` — i.e. that agent's subgraph within the DAG has settled.
/// Used to release an agent's lifecycle lease the moment its own
/// work is done, rather than waiting for the whole DAG to terminate.
/// Vacuously true for an agent the DAG has no node for; callers gate
/// on actually holding that agent's lease first.
pub fn agent_subgraph_terminal(&self, agent: &str) -> bool {
self.nodes
.iter()
.filter(|n| n.agent == agent)
.all(|n| n.state.is_terminal())
}
/// First failed node's error, for the roll-up `error` field.
pub fn first_error(&self) -> Option<&str> {
self.nodes