diff --git a/docs/coordinator.md b/docs/coordinator.md index 047296c0..893cb908 100644 --- a/docs/coordinator.md +++ b/docs/coordinator.md @@ -12,9 +12,9 @@ Every container/meta operation (rebuild, meta-update, first-spawn, power changes) is submitted to the global job-DAG queue (`hive-c0re/src/job_queue/`) as a **DAG of primitive nodes**. One scheduler task drives all DAGs; concurrency comes from the resource classes below, not from multiple workers. -The old special cases — the graceful-stop watcher thread, the deferred-start -fast-lane follow-up, the meta-update cascade pre-enqueue — are all just DAG -*shapes* now. +Special cases like graceful stop, deferred starts, and the meta-update +cascade need no bespoke code paths — each is expressed as a DAG *shape* +built from the same primitive nodes. ### Two levels: DAG and node @@ -30,8 +30,7 @@ A DAG is **declared, not described**: a template builds it through `b.node(kind)` handed back, and the builder inserts the nodes itself. A handle only exists for a node already declared, so every edge points backwards and a cycle cannot be written down — there is no submit-time validation pass, because -there is no malformed spec to reject. (The old queue had a petgraph `toposort` -here, guarding against the positional indices that used to express edges.) +there is no malformed spec to reject. ### Node inventory (primitives) @@ -61,7 +60,7 @@ Cheap — no build slot: | `Drain` | await the harness clearing the fence, bounded by the 3-min graceful-stop timeout; resolves ok either way | | `WriteDropin` | `set_nspawn_flags` + `set_resource_limits` + daemon-reload | | `WritePermFile` | commit `tool-groups.json` / `capabilities.json` (single git commit under `META_LOCK`) + emit the P3RM1SS10NS snapshots | -| `Reparent` | `set-parent` / `set-parent-bulk`: apply every `(child, new_parent)` move under one `META_LOCK` commit (`meta::bulk_commit_topology`), send the per-agent move notifications, rescan + diff-emit. Agentless like `MetaLock` — a bulk move can span multiple agents, and a reparent touches the meta repo, not any one container. `moves` is `(Ident, Option)` pairs, not raw strings — mara: "use Ident type instead of string" (#2719, issuecomment 42691). Rides `Template::MetaUpdate` rather than a dedicated `Template` variant — that enum is on its way out (see `#2665`, still open/blocked on a scope question) and is already internal-only (it never reaches the graph wire), so the stand-in only affects `terminal_hook` dispatch (resolves to no hook either way) and history-retention bucketing | +| `Reparent` | `set-parent` / `set-parent-bulk`: apply every `(child, new_parent)` move under one `META_LOCK` commit (`meta::bulk_commit_topology`), send the per-agent move notifications, rescan + diff-emit. Agentless like `MetaLock` — a bulk move can span multiple agents, and a reparent touches the meta repo, not any one container. `moves` is typed `(Ident, Option)` pairs, not raw strings. Rides the existing `Template::MetaUpdate` variant rather than a dedicated one — it's internal-only (never reaches the graph wire), so the stand-in only affects `terminal_hook` dispatch (resolves to no hook either way) and history-retention bucketing | There is deliberately **no `GitCommit` node**: `meta.rs` fuses each mutation with its commit under its internal `META_LOCK` mutex, so a standalone commit @@ -308,10 +307,11 @@ span agents; consumers derive a group's agent(s) from its nodes. Build logs are likewise **per-node**: the dashboard renders the node tree and keys the live-log panel off the running node. -The event used to ship the whole queue as a typed `DagView`/`NodeView` -projection. That was a second rendering of the same graph, kept in -agreement by hand with the endpoint every consumer actually read; it is -gone, and the event's whole job is now telling a client *when* to refetch. +The event carries no payload by design: shipping a typed projection of +the whole queue in the event itself would be a second rendering of the +same graph that has to be kept in agreement by hand with the endpoint +every consumer actually reads. Telling a client *when* to refetch is +the event's whole job. --- @@ -411,8 +411,7 @@ Sequence for a rebuild DAG (each step is its own queue node): (near-instant after the prebuild). 5. `Reconcile` — boot into the new generation when `wanted = Up`; the in-container activation script transitions old → new. Holds no build - slot, so the next DAG's `Prebuild` overlaps the container boot — the old - "deferred start" split, now structural. + slot, so the next DAG's `Prebuild` overlaps the container boot. The approval deploy uses this same chain rather than a rebuild path of its own. Its `DeployApply` node does not build: it merges, opens the two-phase