refactor(#2808): the wire state enum is the scheduler's own
`hive_host_sock::jobs::State` was a hand-maintained copy of `hive_jobq::State` — five variants spelled the same in both, kept in sync by whoever remembered. Adding `Skipped` last week meant adding it twice. The wire crate now re-exports the scheduler's enum and `to_wire_state` is gone. Two states that were hidden now reach clients. `to_wire_state` renamed `Pending` to `Queued` and folded `Finishing` into `Running`, so the dashboard could not distinguish a node waiting on its dependencies from one whose own work is done while its sub-nodes still run. Both are now visible, and consumers say which they mean. Every consumer had to move with it, and only the Rust ones said so: the exhaustive matches in `hivectl` and `DagView::rollup_state` failed to compile, while the dashboard's fourteen string comparisons would have gone quietly wrong — a `finishing` node no longer counting as running, a `pending` node no longer as queued. The frontend also builds CSS class names out of the state string (`rqe-` + state, `rqe-node-` + state) and keys its glyph map on it, all lowercase. Those go through a `stateSlug` helper now; comparisons use the wire spelling, presentation lowercases. Without that split every queue entry and node chip would have silently lost its styling. Dropping the `State as JobState` alias in hive-c0re falls out of this: the alias only existed to tell two `State` types apart, and there is one now.
This commit is contained in:
parent
6fd91ccf6a
commit
b9aab7e923
8 changed files with 72 additions and 93 deletions
|
|
@ -40,36 +40,7 @@ impl Source {
|
|||
}
|
||||
}
|
||||
|
||||
/// Lifecycle state of a node — and, rolled up, of a DAG.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum State {
|
||||
Queued,
|
||||
Running,
|
||||
Done,
|
||||
Failed,
|
||||
Cancelled,
|
||||
/// The node's own dependency edges ruled it out: an outcome branch that
|
||||
/// wasn't taken. Distinct from `Cancelled`, which is work actively
|
||||
/// dropped. A skipped node is an expected part of a healthy run — every
|
||||
/// approval DAG has two not-taken tails and every rebuild has one — so
|
||||
/// consumers must not read it as a failure or cancellation signal.
|
||||
Skipped,
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Whether the node will never change state again. `Skipped` counts:
|
||||
/// a branch that was ruled out is as final as one that ran, and the
|
||||
/// wait loops (`hivectl`'s progress display, the daemon's
|
||||
/// dag-settled check) hang forever if it doesn't.
|
||||
#[must_use]
|
||||
pub fn is_terminal(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
State::Done | State::Failed | State::Cancelled | State::Skipped
|
||||
)
|
||||
}
|
||||
}
|
||||
pub use hive_jobq::State;
|
||||
|
||||
/// Kind-specific payload for `Template::PermChange` DAGs.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
|
@ -187,17 +158,20 @@ impl DagView {
|
|||
/// Rust consumer (hivectl, the wait loops, tests) uses so the dashboard's
|
||||
/// JS render and the host agree: `Failed` if any node failed, else
|
||||
/// **`Cancelled` if any cancelled**, else `Running` if any running, else
|
||||
/// `Queued` if any queued, else `Done`. `Done` nodes are excluded
|
||||
/// `Pending` if any pending, else `Done`. `Done` nodes are excluded
|
||||
/// from the wire, so a DAG that is *entirely* done isn't sent at all —
|
||||
/// its absence from the snapshot is what signals completion.
|
||||
///
|
||||
/// `Cancelled` outranks both `Running` and `Queued` because a cancelled DAG
|
||||
/// `Cancelled` outranks both `Running` and `Pending` because a cancelled DAG
|
||||
/// still has its weak-edged tail node to run (it reports the cancellation),
|
||||
/// so `Queued`-then-`Running` would flicker back at the operator who just
|
||||
/// so `Pending`-then-`Running` would flicker back at the operator who just
|
||||
/// cancelled it and read as "the cancel didn't take". Outside that window
|
||||
/// the states barely co-occur: a cancel *cascade* originates at a `Failed`
|
||||
/// node, which returns early above.
|
||||
///
|
||||
/// `Finishing` counts as running: the node's own work is done but its
|
||||
/// sub-nodes are still going, so the DAG is still in flight.
|
||||
///
|
||||
/// `Skipped` contributes nothing: a not-taken branch is an expected part of
|
||||
/// a healthy run, so counting it would make every successful DAG roll up
|
||||
/// non-`Done`.
|
||||
|
|
@ -208,13 +182,13 @@ impl DagView {
|
|||
#[must_use]
|
||||
pub fn rollup_state(&self) -> State {
|
||||
let mut any_running = false;
|
||||
let mut any_queued = false;
|
||||
let mut any_pending = false;
|
||||
let mut any_cancelled = false;
|
||||
for n in &self.nodes {
|
||||
match n.state {
|
||||
State::Failed => return State::Failed,
|
||||
State::Running => any_running = true,
|
||||
State::Queued => any_queued = true,
|
||||
State::Running | State::Finishing => any_running = true,
|
||||
State::Pending => any_pending = true,
|
||||
State::Cancelled => any_cancelled = true,
|
||||
State::Done | State::Skipped => {}
|
||||
}
|
||||
|
|
@ -223,8 +197,8 @@ impl DagView {
|
|||
State::Cancelled
|
||||
} else if any_running {
|
||||
State::Running
|
||||
} else if any_queued {
|
||||
State::Queued
|
||||
} else if any_pending {
|
||||
State::Pending
|
||||
} else {
|
||||
State::Done
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue