From 6e18bbc8399b557f14312211df3849c91975e8e4 Mon Sep 17 00:00:00 2001 From: atlas Date: Mon, 3 Aug 2026 15:46:09 +0200 Subject: [PATCH] jobq: say the graph is runtime-only, because it is MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The crate described itself as a persistent scheduler and NodeId promised stability across restarts. Neither is true: hive-c0re constructs an empty Graph on every boot and re-derives desired state with its reconcile sweep, and nothing in the workspace writes or loads a graph. hive-c0re's own job_queue module doc has said "runtime-only (no persistence)" all along — only the extracted library's prose drifted. Seven claims corrected across the module doc, NodeId, the id-counter error and the Graph type, plus the repo map. The module doc now states the fact positively rather than just dropping the word: serde exists so the graph can be projected onto a wire and so a store could be added later, ids and timestamps are stable within a run. NodeId spells out the consequence, since that is the part that could mislead someone: an id stored outside the process is a historical record, not a handle that will resolve after a restart. --- CLAUDE.md | 9 ++++++--- hive-jobq/src/lib.rs | 33 +++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index dd5c38a6..f3ddc381 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,9 +53,12 @@ hand-maintained per-file tree drifts out of sync with the code. streamable-http listener, `hive-mcp-http` systemd unit) + its claude launch-config layer (tool-group/capability → `--allowedTools`, `--mcp-config` render). -- **`hive-jobq/`** — persistent job-DAG scheduler, extracted from - hive-c0re's in-tree `job_queue` as a domain-agnostic library. One - persistent graph for the whole system (not a DAG per job); enqueuing +- **`hive-jobq/`** — job-DAG scheduler, extracted from hive-c0re's + in-tree `job_queue` as a domain-agnostic library. **Runtime-only — + nothing writes the graph to disk**; hive-c0re starts empty each boot + and re-derives desired state via the reconcile sweep, so node ids and + timestamps are stable within a run, not across restarts. One + shared graph for the whole system (not a DAG per job); enqueuing inserts a self-contained sub-DAG and returns the ids of the nodes the job asked for, in the order it named them. Generic over the node payload `N` and the resource name `R`; resource deps are named diff --git a/hive-jobq/src/lib.rs b/hive-jobq/src/lib.rs index 0e899dc7..fdad544e 100644 --- a/hive-jobq/src/lib.rs +++ b/hive-jobq/src/lib.rs @@ -1,9 +1,12 @@ -//! `hive-jobq` — a persistent job-DAG scheduler, extracted from hive-c0re's -//! in-tree `job_queue` as a domain-agnostic library. +//! `hive-jobq` — a job-DAG scheduler, extracted from hive-c0re's in-tree +//! `job_queue` as a domain-agnostic library. +//! +//! **Runtime-only: nothing writes this graph to disk** — ids and timestamps are +//! stable within a run, not across restarts. See [`Graph`] and [`NodeId`]. //! //! # Model (v2) //! -//! One **persistent graph** for the whole system, not a DAG per job. Enqueuing +//! One **shared graph** for the whole system, not a DAG per job. Enqueuing //! inserts a self-contained sub-DAG of nodes and returns their ids; the //! scheduler runs a continuous loop, starting every node whose [`Dep`]s are //! satisfied: @@ -18,7 +21,7 @@ //! A node carries two independent axes: its [`Dep`]s (ordering + resource //! needs) and its [`Node::parent`] (structural grouping) — the parent chain, //! not the [`Dep::Node`] edges, is what the [`scheduler`] consults for resource -//! re-entrancy. A [`NodeId`] is opaque, stable, and monotonic (persisted). The +//! re-entrancy. A [`NodeId`] is opaque, stable and monotonic within a run. The //! payload `N` is generic so the library stays container-agnostic. //! //! A resource unit is held for the acquiring node + its whole [`Node::parent`] @@ -36,12 +39,15 @@ use chrono::{DateTime, Utc}; /// Opaque, stable, monotonic node identifier. /// -/// Assigned by the [`Graph`] on insert and persisted, so it is stable across -/// restarts. +/// Assigned by the [`Graph`] on insert, so it is stable for the lifetime of +/// that graph. **Not stable across restarts** — nothing persists the graph, so +/// a fresh process mints ids from zero again (see the module doc). Anything +/// storing an id outside the process is keeping a historical record, not a +/// handle it can resolve later. /// /// The inner field is crate-private: an id can only originate from the graph's -/// monotonic counter (or deserialization of a persisted graph), never be -/// fabricated by a caller — that is what makes it opaque. +/// monotonic counter (or deserializing a graph that was serialized from one), +/// never be fabricated by a caller — that is what makes it opaque. #[derive( Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, )] @@ -351,14 +357,21 @@ pub enum GraphError { /// so the next minted id would collide with one already in the graph. #[error("next_id {next_id} must exceed the largest existing node id {max_id}")] NextIdTooSmall { - /// The persisted counter value. + /// The counter value carried by the deserialized graph. next_id: u64, /// The largest id already present. max_id: u64, }, } -/// The single persistent graph of all nodes. +/// The single in-memory graph of all nodes. +/// +/// **Not persisted.** The `Serialize`/`Deserialize` impls exist so a graph can +/// be projected onto a wire (`hive-jobq-wire`) and so a store *could* be added, +/// but no caller writes or loads one: hive-c0re constructs an empty graph on +/// every boot and re-derives desired state with its reconcile sweep (see +/// `hive-c0re/src/job_queue/mod.rs`). A derive is a capability, not a promise +/// that something uses it. /// /// New jobs are inserted as sub-DAGs of nodes; the scheduler walks this graph /// filling open slots. Completed nodes are retained (no pruning in v1).