job_queue: drop the NodeKind::Dag/root filter from the QueueNodes lookup

This commit is contained in:
damocles 2026-08-03 20:03:48 +02:00 committed by mara
commit 309c1c91c6
4 changed files with 48 additions and 43 deletions

View file

@ -289,8 +289,8 @@ impl JobQueue {
#[must_use]
pub fn first_error(&self, dag_id: u64) -> Option<String> {
let inner = self.lock();
let container = container(&inner, dag_id)?;
inner.graph().first_error(container).map(ToOwned::to_owned)
let node = find_node(&inner, dag_id)?;
inner.graph().first_error(node).map(ToOwned::to_owned)
}
/// `(agent, label, takes_container_down)` for the live transient-pill set,
@ -379,38 +379,40 @@ impl JobQueue {
.collect()
}
/// One DAG's container node plus its live subtree, as generic wire
/// nodes — the `QueueNodes` polling surface behind `hivectl`'s
/// wait/progress loop. Sibling of [`Self::snapshot`]
/// (which serves the same graph through the typed `DagView`/`NodeView`
/// projection for the dashboard's `/api/state.rebuild_queue`), this one
/// goes through [`GraphWire::wire_snapshot`] instead — no `Done`-node
/// filtering, no roll-up field (the root's own `state` answers that,
/// see `hive_jobq_wire`'s doc comment).
/// A node plus its live subtree, as generic wire nodes — the
/// `QueueNodes` polling surface behind `hivectl`'s wait/progress loop.
/// Sibling of [`Self::snapshot`] (which serves the same graph through
/// the typed `DagView`/`NodeView` projection for the dashboard's
/// `/api/state.rebuild_queue`), this one goes through
/// [`GraphWire::wire_snapshot`] instead — no `Done`-node filtering, no
/// roll-up field (a node's own `state` answers that, see
/// `hive_jobq_wire`'s doc comment). Looks the id up by identity alone —
/// no assumption that it names a DAG container or a root; "just show
/// whatever the backend sends" for whatever id the caller asks about.
///
/// Empty when `dag_id` names no DAG container in the graph. Today that
/// only happens for a genuinely unknown id: nothing prunes the graph
/// yet (bounded-prune is a Stage-C follow-up, see [`visible_dags`]), so
/// a *completed* DAG's nodes keep riding here with a terminal `state`
/// Empty when `id` names no node in the graph. Today that only happens
/// for a genuinely unknown id: nothing prunes the graph yet
/// (bounded-prune is a Stage-C follow-up, see [`visible_dags`]), so a
/// *completed* DAG's nodes keep riding here with a terminal `state`
/// rather than disappearing — callers watching for "done" should read
/// the root's `state`, not emptiness.
#[must_use]
pub fn dag_nodes(&self, dag_id: u64) -> Vec<GraphNode> {
pub fn node_subtree(&self, id: u64) -> Vec<GraphNode> {
let inner = self.lock();
let Some(root) = container(&inner, dag_id) else {
let Some(node) = find_node(&inner, id) else {
return Vec::new();
};
inner.graph().wire_snapshot([root])
inner.graph().wire_snapshot([node])
}
}
/// The container node of `dag_id` — the `NodeKind::Dag` root whose id equals
/// `dag_id`. `NodeId` is un-fabricable from a raw `u64`, so this is a search.
fn container(sched: &Sched, dag_id: u64) -> Option<NodeId> {
sched.graph().nodes().find_map(|n| {
(n.parent.is_none() && n.id.get() == dag_id && matches!(n.payload, NodeKind::Dag { .. }))
.then_some(n.id)
})
/// The graph node whose id equals `id`, whatever its kind or depth.
/// `NodeId` is un-fabricable from a raw `u64`, so this is a search.
fn find_node(sched: &Sched, id: u64) -> Option<NodeId> {
sched
.graph()
.nodes()
.find_map(|n| (n.id.get() == id).then_some(n.id))
}
/// Project a DAG into its wire [`DagView`]: a near-raw view of the