From 55705fd617ba1390a03295e38ebb5413f74262c0 Mon Sep 17 00:00:00 2001 From: atlas Date: Mon, 3 Aug 2026 15:38:51 +0200 Subject: [PATCH] jobq: drop the Dag payload's created_at, read it off the node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The container node now carries its own `created_at` like every other node, so the payload copy recorded the same instant a second time — and only the payload one was reachable to a viewer that doesn't know what a Dag is. `dag_view` reads `node.created_at` off the container instead. `DagView` keeps the field on the wire: hivectl's dag_progress uses it as the elapsed fallback for a DAG that hasn't started yet. It just has one source now. Removing the field left chrono entirely unused in model.rs, which is the compiler confirming the payload had no other use for a timestamp. --- hive-c0re/src/job_queue/mod.rs | 26 +++++++++----------------- hive-c0re/src/job_queue/model.rs | 12 ++++++------ 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/hive-c0re/src/job_queue/mod.rs b/hive-c0re/src/job_queue/mod.rs index 32beb2c5..6b2411df 100644 --- a/hive-c0re/src/job_queue/mod.rs +++ b/hive-c0re/src/job_queue/mod.rs @@ -217,15 +217,7 @@ impl JobQueue { ) -> anyhow::Result { let mut inner = self.lock(); let container = inner - .append( - NodeKind::Dag { - source, - reason, - created_at: Utc::now(), - }, - Vec::new(), - None, - ) + .append(NodeKind::Dag { source, reason }, Vec::new(), None) .map_err(|e| anyhow::anyhow!("job_queue: container insert failed: {e}"))?; insert_group(&mut inner, declare, Some(container))?; drop(inner); @@ -407,15 +399,15 @@ fn container(sched: &Sched, dag_id: u64) -> Option { /// DAG drops out of the snapshot entirely (a `Failed` one lingers until /// aged out). fn dag_view(sched: &Sched, container: NodeId) -> Option { - // Read straight off the container's payload: the three fields have a + // Read straight off the container's payload: the domain fields have a // single home there, so an intermediate owned copy of them was a second // type describing the same data rather than a grouping side-table. - let NodeKind::Dag { - source, - reason, - created_at, - } = &sched.graph().node(container)?.payload - else { + // + // `created_at` is NOT among them — it comes off the container node itself + // below, where the graph stamps it for every node. Keeping a payload copy + // would record one instant in two places. + let node = sched.graph().node(container)?; + let NodeKind::Dag { source, reason } = &node.payload else { return None; }; let all: Vec<_> = sched.graph().descendants(container).collect(); @@ -494,7 +486,7 @@ fn dag_view(sched: &Sched, container: NodeId) -> Option { id: container.get(), source: *source, reason: reason.clone(), - created_at: *created_at, + created_at: node.created_at, started_at: started.into_iter().min(), finished_at: is_terminal.then(|| finished.into_iter().max()).flatten(), nodes, diff --git a/hive-c0re/src/job_queue/model.rs b/hive-c0re/src/job_queue/model.rs index e783c05c..3b8aaf4b 100644 --- a/hive-c0re/src/job_queue/model.rs +++ b/hive-c0re/src/job_queue/model.rs @@ -12,7 +12,6 @@ //! DAG can span agents). See `docs/coordinator.md::Job queue` for the //! full design. -use chrono::{DateTime, Utc}; pub use hive_host_sock::jobs::{DagView, PermPayload, Source, State}; use serde::Serialize; @@ -276,11 +275,12 @@ pub enum NodeKind { /// the DAG state. Pure grouping — lease- and /// build-slot-exempt; the executor instant-completes it (`Done`) so it /// reaches `Finishing` and its children start. - Dag { - source: Source, - reason: String, - created_at: DateTime, - }, + /// + /// No `created_at` here: the graph stamps [`hive_jobq::Node::created_at`] on + /// every node at insert, so the container already has one. A second copy in + /// the payload would be the same instant recorded twice, with only this + /// variant's version reachable to a generic viewer. + Dag { source: Source, reason: String }, } /// How a hive-c0re node describes itself to a generic graph viewer.