diff --git a/hive-sh4re/src/jobs.rs b/hive-sh4re/src/jobs.rs index a3075977..fc854fa0 100644 --- a/hive-sh4re/src/jobs.rs +++ b/hive-sh4re/src/jobs.rs @@ -192,4 +192,32 @@ impl DagView { State::Done } } + + /// A short human label for the DAG, derived from its node kinds — the + /// shared replacement for the old `Template` wire string now that the kind + /// isn't sent. Priority-ordered so the most distinctive node wins (an + /// approval deploy reads as "deploy" even though it also rebuilds). Purely + /// cosmetic (progress/queue display); consumers that need exactness inspect + /// the node kinds directly. + #[must_use] + pub fn label(&self) -> &'static str { + let has = |k: &str| self.nodes.iter().any(|n| n.kind == k); + if has("approval_deploy") { + "deploy" + } else if has("meta_lock") { + "meta-update" + } else if has("create") || has("provision") { + "spawn" + } else if has("swap") || has("prebuild") || has("post_swap") { + "rebuild" + } else if has("write_perm_file") { + "perm-change" + } else if has("signal") || has("drain") { + "graceful" + } else if has("set_wanted") || has("stop_for_update") { + "power" + } else { + "reconcile" + } + } } diff --git a/hivectl/src/dag_progress.rs b/hivectl/src/dag_progress.rs index 4d257800..89ff638c 100644 --- a/hivectl/src/dag_progress.rs +++ b/hivectl/src/dag_progress.rs @@ -67,7 +67,7 @@ async fn wait_for_dags_plain(socket: &Path, ids: Vec) -> Result<()> { // sees whether the agent came back. if d.nodes.iter().all(|n| n.state.is_terminal()) { if d.rollup_state() == hive_sh4re::jobs::State::Failed { - failed.push(format!("{} {}", d.source.as_str(), dag_agents(d))); + failed.push(format!("{} {}", d.label(), dag_agents(d))); } } else { all_terminal = false; @@ -135,7 +135,7 @@ async fn wait_for_dags_animated(socket: &Path, ids: Vec) -> Result<()> { hdr.set_message(format!( "{} {} {} · {}", state_glyph(d.rollup_state()), - d.source.as_str(), + d.label(), dag_agents(d), fmt_dur(dag_elapsed(d, now)), )); @@ -164,7 +164,7 @@ async fn wait_for_dags_animated(socket: &Path, ids: Vec) -> Result<()> { } if d.nodes.iter().all(|n| n.state.is_terminal()) { if d.rollup_state() == hive_sh4re::jobs::State::Failed { - failed.push(format!("{} {}", d.source.as_str(), dag_agents(d))); + failed.push(format!("{} {}", d.label(), dag_agents(d))); } } else { all_terminal = false; @@ -290,16 +290,16 @@ fn state_glyph(state: hive_sh4re::jobs::State) -> &'static str { } } -/// One progress line for a DAG: roll-up glyph, `source`, agents, then the -/// node chain — the CLI twin of the dashboard's queue card. The header shows -/// what the backend sends (`source` + the raw node kinds); only the roll-up -/// state glyph is derived from the node set. Used by the plain (non-TTY) path. +/// One progress line for a DAG: roll-up glyph, derived label, agents, then +/// the node chain — the CLI twin of the dashboard's queue card. Both the +/// roll-up state and the label are derived from the node set (the wire no +/// longer carries them). Used by the plain (non-TTY) path. fn render_dag_line(d: &hive_sh4re::jobs::DagView) -> String { use std::fmt::Write as _; let mut out = format!( "{} {} {:<12}", state_glyph(d.rollup_state()), - d.source.as_str(), + d.label(), dag_agents(d) ); for (i, n) in d.nodes.iter().enumerate() { @@ -337,12 +337,10 @@ mod tests { } #[test] - fn render_dag_line_shows_source_and_chain() { - // The header shows what the backend sends: the roll-up state glyph - // (derived — Running here) + the DAG `source` ("manual"); the operation - // is read off the node chain, not a client-side label. (`Done` nodes - // are included here to exercise glyph rendering; production filters - // them off.) + fn render_dag_line_shows_chain_with_derived_label_and_state() { + // Roll-up state (Running) + label ("rebuild") are derived from the node + // set — the wire no longer carries them. (`Done` nodes are included + // here to exercise glyph rendering; production filters them off.) let dag = DagView { id: 7, source: Source::Manual, @@ -358,7 +356,7 @@ mod tests { ], }; let line = render_dag_line(&dag); - assert!(line.starts_with("▶ manual alice"), "{line}"); + assert!(line.starts_with("▶ rebuild alice"), "{line}"); assert!( line.contains("✔ prebuild → ✔ stop_for_update → ▶ swap → ⏸ reconcile"), "{line}" @@ -379,7 +377,7 @@ mod tests { nodes: vec![failed], }; let line = render_dag_line(&dag); - assert!(line.contains("✖ manual"), "{line}"); + assert!(line.contains("✖ rebuild"), "{line}"); assert!(line.contains("— nix build exploded"), "{line}"); } }