diff --git a/hive-c0re/src/job_queue/model.rs b/hive-c0re/src/job_queue/model.rs index a15e11d1..9f45c3b1 100644 --- a/hive-c0re/src/job_queue/model.rs +++ b/hive-c0re/src/job_queue/model.rs @@ -308,15 +308,6 @@ impl hive_jobq_wire::WireNode for NodeKind { { data.insert("inputs".to_owned(), inputs.clone().into()); } - // The DAG container's own metadata — nowhere else on the wire, since - // `GraphNode` carries no DAG-level fields (a group root is an - // ordinary node). `hivectl` needs `source` for its progress line; - // `reason` rides along for free rather than adding a second variant - // later for the one field the first pass missed. - if let NodeKind::Dag { source, reason, .. } = self { - data.insert("source".to_owned(), source.as_str().into()); - data.insert("reason".to_owned(), reason.clone().into()); - } // Not in the payload at all — the build log is keyed on node identity // in a side table, which is why `data` is handed the id. if let Some(log) = crate::build_logs::global().and_then(|h| h.id_for_node(id)) { diff --git a/hivectl/src/dag_progress.rs b/hivectl/src/dag_progress.rs index 7d137b52..fa25516b 100644 --- a/hivectl/src/dag_progress.rs +++ b/hivectl/src/dag_progress.rs @@ -7,10 +7,11 @@ //! and a plain line-on-change stream otherwise. //! //! Consumes `hive-jobq-wire`'s generic [`GraphNode`]/`NodePayload`: a -//! node's kind comes from `payload.label`, and node-specific extras -//! (`source`, `agent`) ride in `payload.data`'s opaque kvps — the shape -//! `hive-c0re`'s `impl WireNode for NodeKind` produces (see its doc -//! comment). There's no separate roll-up field on the wire: a node's own +//! node's kind (and, for a root, what submitted it) comes straight off +//! `payload.label`; node-specific extras (`agent`) ride in +//! `payload.data`'s opaque kvps — the shape `hive-c0re`'s +//! `impl WireNode for NodeKind` produces (see its doc comment). There's +//! no separate roll-up field on the wire: a node's own //! `state` already reflects everything below it (see `hive_jobq_wire`'s //! doc comment), so nothing here re-derives a roll-up or waits for every //! node to go terminal before reading a result. @@ -110,7 +111,7 @@ async fn wait_for_nodes_plain(socket: &Path, ids: Vec) -> Result<()> { } if root.state.is_terminal() { if root.state == State::Failed { - failed.push(format!("{} {}", node_source(root), node_agents(nodes))); + failed.push(format!("{} {}", root.payload.label, node_agents(nodes))); } pending.remove(&id); } @@ -174,7 +175,7 @@ async fn wait_for_nodes_animated(socket: &Path, ids: Vec) -> Result<()> { hdr.set_message(format!( "{} {} {} · {}", state_glyph(root.state), - node_source(root), + root.payload.label, node_agents(nodes), fmt_dur(node_elapsed(root, now)), )); @@ -203,7 +204,7 @@ async fn wait_for_nodes_animated(socket: &Path, ids: Vec) -> Result<()> { } if root.state.is_terminal() { if root.state == State::Failed { - failed.push(format!("{} {}", node_source(root), node_agents(nodes))); + failed.push(format!("{} {}", root.payload.label, node_agents(nodes))); } pending.remove(&id); } @@ -236,18 +237,6 @@ fn finish_wait(mut failed: Vec) -> Result<()> { } } -/// A root's `source` tag (`"manual"`, `"meta_update"`, …), read from its -/// `payload.data` — see `hive-c0re`'s `impl WireNode for NodeKind`'s -/// `NodeKind::Dag` arm, the one place it's put on the wire. Falls back to -/// the label when absent (defensive; every real root sets it). -fn node_source(root: &GraphNode) -> &str { - root.payload - .data - .get("source") - .and_then(serde_json::Value::as_str) - .unwrap_or(&root.payload.label) -} - /// Distinct agents across a root's nodes, comma-joined for display. Each /// node's `agent` (when it targets one) rides in `payload.data["agent"]` — /// an opaque kvp, not a typed field, since `GraphNode` carries nothing @@ -348,9 +337,9 @@ fn state_glyph(state: State) -> &'static str { } } -/// One progress line for a root: roll-up glyph, `source`, agents, then the +/// One progress line for a root: roll-up glyph, label, agents, then the /// node chain — the CLI twin of the dashboard's queue card. The glyph and -/// `source` come off `root`; everything else comes straight off the wire. +/// label come off `root`; everything else comes straight off the wire. /// Used by the plain (non-TTY) path. /// /// The chain shows every entry `wire_snapshot` sends, `Done` ones @@ -363,7 +352,7 @@ fn render_node_line(root: &GraphNode, nodes: &[GraphNode]) -> String { let mut out = format!( "{} {} {:<12}", state_glyph(root.state), - node_source(root), + root.payload.label, node_agents(nodes) ); let children: Vec<&GraphNode> = nodes.iter().filter(|n| n.id != root.id).collect(); @@ -386,7 +375,7 @@ mod tests { use super::render_node_line; - fn root_node(id: u64, source: &str, state: State) -> GraphNode { + fn root_node(id: u64, label: &str, state: State) -> GraphNode { GraphNode { id, parent: None, @@ -397,8 +386,8 @@ mod tests { finished_at: None, error: None, payload: NodePayload { - label: "job".to_owned(), - data: json!({ "source": source }), + label: label.to_owned(), + data: json!({}), }, } } @@ -421,11 +410,12 @@ mod tests { } #[test] - fn render_node_line_shows_source_and_chain() { + fn render_node_line_shows_label_and_chain() { // The header shows what the backend sends: the glyph off `root`'s - // own `state` (`Running` here) + `source` ("manual"); the operation - // is read off the node chain, not a client-side label. `Done` - // nodes stay in the chain — nothing here filters them off. + // own `state` (`Running` here) + `root`'s own `payload.label` + // ("manual"); the operation is read off the node chain, not any + // extra metadata. `Done` nodes stay in the chain — nothing here + // filters them off. let root = root_node(7, "manual", State::Running); let nodes = vec![ root.clone(),