Compare commits

...
Author SHA1 Message Date
atlas
c7feeb7f5b test(#2591): assert hivectl header shows source, not a derived label 2026-07-23 18:01:26 +02:00
atlas
0ee4647d1e refactor(#2591): drop the client-side DAG label map — show source + node kinds
Per mara (#2660): the client shouldn't re-derive a friendly name from
node kinds — that re-bakes the domain knowledge the raw-graph redesign
removed. The queue card / hivectl header now shows what the backend sends
(the DAG's source + the raw node kinds); the node chain conveys the
operation. Removes the DagView::label() helper (hivectl was its only
consumer after iris dropped the frontend map in #2660) and points
hivectl's header at d.source instead.
2026-07-23 18:01:26 +02:00
2 changed files with 16 additions and 42 deletions

View file

@ -192,32 +192,4 @@ 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"
}
}
}

View file

@ -67,7 +67,7 @@ async fn wait_for_dags_plain(socket: &Path, ids: Vec<u64>) -> 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.label(), dag_agents(d)));
failed.push(format!("{} {}", d.source.as_str(), dag_agents(d)));
}
} else {
all_terminal = false;
@ -135,7 +135,7 @@ async fn wait_for_dags_animated(socket: &Path, ids: Vec<u64>) -> Result<()> {
hdr.set_message(format!(
"{} {} {} · {}",
state_glyph(d.rollup_state()),
d.label(),
d.source.as_str(),
dag_agents(d),
fmt_dur(dag_elapsed(d, now)),
));
@ -164,7 +164,7 @@ async fn wait_for_dags_animated(socket: &Path, ids: Vec<u64>) -> Result<()> {
}
if d.nodes.iter().all(|n| n.state.is_terminal()) {
if d.rollup_state() == hive_sh4re::jobs::State::Failed {
failed.push(format!("{} {}", d.label(), dag_agents(d)));
failed.push(format!("{} {}", d.source.as_str(), 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, 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.
/// 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.
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.label(),
d.source.as_str(),
dag_agents(d)
);
for (i, n) in d.nodes.iter().enumerate() {
@ -337,10 +337,12 @@ mod tests {
}
#[test]
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.)
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.)
let dag = DagView {
id: 7,
source: Source::Manual,
@ -356,7 +358,7 @@ mod tests {
],
};
let line = render_dag_line(&dag);
assert!(line.starts_with("rebuild alice"), "{line}");
assert!(line.starts_with("manual alice"), "{line}");
assert!(
line.contains("✔ prebuild → ✔ stop_for_update → ▶ swap → ⏸ reconcile"),
"{line}"
@ -377,7 +379,7 @@ mod tests {
nodes: vec![failed],
};
let line = render_dag_line(&dag);
assert!(line.contains("rebuild"), "{line}");
assert!(line.contains("manual"), "{line}");
assert!(line.contains("— nix build exploded"), "{line}");
}
}