fix(#3020): K3PT ST4T3 says what it shows, and stops reading the job graph
`build_tombstone_views` folded `transient_snapshot`'s keys into its `live` set, so an agent with in-flight transient work was treated as not-a-tombstone. Since #3010 that set is derived from the running job graph, which made a page about on-disk state a function of the scheduler. Dropping the filter exposes what was always true underneath: nothing records a destroy. Every definition-side artifact — state subvolume, proposed + applied repos, `deployed/0`, meta registration, topology entry — is written by `Provision` before the container exists and survives `lifecycle::destroy`. So a mid-spawn agent is byte-identical on disk to a tombstone. Per mara on #3020: remove the filter, warn on the page, keep the issue open for the swarm-controller / snapshot-storage rework where the problem shape changes anyway. - dashboard/tombstones.rs: drop the param + the chain; document the real semantics - core.js: amber caveat banner above the rows; row badge `destroyed` -> `offline`, which is what an absent container actually proves - dashboard.css: `.tombstone-warn`, modelled on `.port-conflict` but amber and without the pulse — a permanent banner that pulses trains you to ignore it - docs/web-ui/dashboard.md: the pane was described as "destroyed-but-state-kept agents", now the exact wrong claim
This commit is contained in:
parent
6ce71556e2
commit
6a8a729f58
5 changed files with 62 additions and 11 deletions
|
|
@ -375,7 +375,7 @@ pub(super) async fn api_state(
|
|||
.into_iter()
|
||||
.map(history_view)
|
||||
.collect();
|
||||
let tombstones = build_tombstone_views(&state.coord, &containers, &transient_snapshot);
|
||||
let tombstones = build_tombstone_views(&state.coord, &containers);
|
||||
let port_conflicts = build_port_conflicts(&containers);
|
||||
|
||||
// Both operator-targeted and peer threads surface on the dashboard
|
||||
|
|
|
|||
|
|
@ -32,17 +32,29 @@ pub struct TombstoneView {
|
|||
|
||||
/// State-dir names that don't appear in the live container list. Each
|
||||
/// one surfaces in the dashboard as a row with R3V1V3 + PURG3 actions.
|
||||
///
|
||||
/// ⚠️ **This lists every agent whose container is absent, not only destroyed
|
||||
/// ones** — a mid-spawn agent (state dir seeded by `Provision`, container not
|
||||
/// yet made by `Create`) is indistinguishable from a tombstone here, because
|
||||
/// *nothing records a destroy*. Every definition-side artifact — state
|
||||
/// subvolume, proposed + applied repos, `deployed/0`, meta registration,
|
||||
/// topology entry — is written before the container exists and survives
|
||||
/// `lifecycle::destroy`, which removes the container and leaves the rest.
|
||||
///
|
||||
/// This used to be papered over by treating agents with in-flight transient
|
||||
/// work as live. That made the page's contents a function of the **job
|
||||
/// graph** (`transient_snapshot` is derived from running nodes), which is the
|
||||
/// wrong dependency for "what state is on disk" — so the filter is gone and
|
||||
/// the page says what it actually shows. The real fix is to record the
|
||||
/// destroy rather than infer it from an absence, deferred to the
|
||||
/// swarm-controller / snapshot-storage rework where this changes shape anyway.
|
||||
pub(super) fn build_tombstone_views(
|
||||
coord: &Coordinator,
|
||||
containers: &[ContainerView],
|
||||
transient_snapshot: &std::collections::HashMap<String, Vec<crate::coordinator::TransientState>>,
|
||||
) -> Vec<TombstoneView> {
|
||||
let _ = coord; // kept_state_names is a free fn but takes &self by future plan
|
||||
let live: std::collections::HashSet<&str> = containers
|
||||
.iter()
|
||||
.map(|c| c.name.as_str())
|
||||
.chain(transient_snapshot.keys().map(String::as_str))
|
||||
.collect();
|
||||
let live: std::collections::HashSet<&str> =
|
||||
containers.iter().map(|c| c.name.as_str()).collect();
|
||||
Coordinator::kept_state_names()
|
||||
.into_iter()
|
||||
.filter(|name| !live.contains(name.as_str()))
|
||||
|
|
@ -96,8 +108,7 @@ fn dir_size_bytes(root: &Path) -> u64 {
|
|||
/// is tiny.
|
||||
pub(crate) async fn emit_tombstones_snapshot(coord: &Arc<Coordinator>) {
|
||||
let containers = coord.containers_snapshot().await;
|
||||
let transient_snapshot = coord.transient_snapshot();
|
||||
let tombstones = build_tombstone_views(coord, &containers, &transient_snapshot);
|
||||
let tombstones = build_tombstone_views(coord, &containers);
|
||||
coord.emit_dashboard_event(crate::dashboard_events::DashboardEvent::TombstonesChanged {
|
||||
seq: coord.next_seq(),
|
||||
tombstones,
|
||||
|
|
|
|||
Loading…
Reference in a new issue