Watch
0
0
Fork
You've already forked hyperhive
0

c0re: keep power intent in memory, drop agent_power/kv tables and the stop snapshot

This commit is contained in:
damocles 2026-11-07 01:22:18 +01:00 • committed by mara
commit b77fa9edc3
3 changed files with 222 additions and 553 deletions

View file

@ -42,29 +42,23 @@ schemas, file layouts, and internal migration mechanics.
### `/var/lib/hyperhive/db/broker.sqlite` (host)
Three tables, all in one file — the message queue, a small key/value
table, and the per-agent power-intent registry:
One table, the message queue:
- `messages` — every inter-agent / operator-authored message.
`sender / recipient / body / sent_at / delivered_at / acked_at /
in_reply_to / priority`. `in_reply_to` links a reply to its parent
row id; the dashboard and per-agent inbox render these as threaded
rows.
- `kv` — small persistent key/value store (`key PK / value`) for
host-side bookkeeping that doesn't warrant its own table.
⚠️ The `mcp__hyperhive__remind` queue lives in a harness-local,
per-agent store — see [`/harness/` contents
below](#state-dirs-per-agent) for where reminders (and todos)
live.
- `agent_power` — one row per agent: `agent PK / wanted (up |
offline) / updated_at`, owned by `hive-c0re/src/stores/power.rs`.
This is the durable power *intent* the job queue reconciles the
observed container state against; intent survives hive-c0re
restarts even though in-flight queue work doesn't. See
[`docs/4-implementation/scheduler/coordinator.md`'s Desired-state
section](../../4-implementation/scheduler/coordinator.md#desired-state-spec-vs-status) for who
writes and reads it and how reconciliation works.
⚠️ The `mcp__hyperhive__remind` queue lives in a harness-local,
per-agent store — see [`/harness/` contents
below](#state-dirs-per-agent) for where reminders (and todos)
live.
The swarm owns power intent, so hive-c0re keeps none on disk. An older build
also kept a `kv` table (the agents running at the last `hivectl stop`) and an
`agent_power` table (the durable `wanted` intent) here; opening the file drops
both.
On a host whose hive-c0re once kept scheduled prompts, the file also
holds `scheduled_prompts` and `scheduled_prompt_targets`. Nothing opens
@ -80,8 +74,6 @@ Retention:
rows are always kept — the harness `ack_turn`s only after a
successful turn, so `requeue_inflight` can still requeue
an unacked row on a crash.
- `agent_power` rows live until the operator destroys the agent (one row per
agent — nothing to vacuum).
### `/harness/hyperhive-events.sqlite` (per agent)

View file

@ -454,7 +454,7 @@ Bare `hivectl stop` stops every agent and the gateway; scope flags narrow it to
Start containers hive-wide — the inverse of `hivectl stop`.
Bare `hivectl start` restores the agents stopped by the last broad-scope `stop` (or starts everything if none); scope flags narrow it. The swarm's forge and matrix are never in scope.
Bare `hivectl start` starts every agent; scope flags narrow it. The swarm's forge and matrix are never in scope.
**Usage:** `hivectl start [OPTIONS]`

View file

@ -69,7 +69,7 @@ Cheap — no build slot:
| `DestroyContainer` | `nixos-container destroy` + un-registration (drop from the roster, clear the ephemeral runtime dir). Runs downstream of a `Stop`, so deliberately excluded from `takes_container_down` — the container is already down by the time it claims |
| `PurgeState` | the `purge = true` half of a destroy: delete the agent's state subvolume (via hive-priv) plus its state dir. Own node because it's conditional and the irreversible step |
| `DestroyBookkeeping` | the post-destroy tail — meta sync, drop the power intent, notify the manager, rescan, re-emit the tombstone. Same split rationale as `RebuildBookkeeping`/`Swap`. The destructive work is `PurgeState`'s |
| `SetWanted` | write the durable power intent (`wanted = Up`/`Offline`) as the head node of a power-op DAG. Takes the agent lease even though it's a store write, so the intent write and the tail `Reconcile` are atomic per-agent — two racing power ops can't clobber each other's intent before either reconciles |
| `SetWanted` | record the power intent (`wanted = Up`/`Offline`, in memory) as the head node of a power-op DAG. Takes the agent lease even though it writes no container state, so the intent write and the tail `Reconcile` are atomic per-agent — two racing power ops can't clobber each other's intent before either reconciles |
| `EmitRebuilt` | tail of a rebuild — emit the agent's `Rebuilt` manager event (ok/fail per outcome, nothing on cancel). One node per agent _and_ per outcome |
| `WriteDropin` | `set_nspawn_flags` + `set_resource_limits` + daemon-reload |
| `KnowledgePull` | `/knowledge` pull (`knowledge::pull`): at boot (commits that landed while `hive-c0re` was down), on the swarm knowledge-changed event, and hourly as a fallback. Holds `Resource::KnowledgeTree` (capacity 1), so two pulls never overlap on the working tree; each trigger queues its own pass, which waits for the resource if one is already live. Agentless |
@ -82,7 +82,7 @@ and `agent_set::refresh` serializes itself with its own mutex.
### Every operation as a DAG
The `stop` / `start` power ops write the durable `wanted` intent via a head
The `stop` / `start` power ops record the in-memory `wanted` intent via a head
`SetWanted` node (not a pre-submit side effect) — it holds the agent lease,
so intent-write + reconcile is atomic per-agent. `restart` is the exception:
it writes _no_ intent (no `SetWanted` head) — it bounces the container and
@ -138,11 +138,14 @@ Notable collapses:
### Desired-state (spec vs status)
Per-agent power _intent_ — `wanted: Up | Offline` — is durable as the
`agent_power` table in the coordinator DB (`hive-c0re/src/stores/power.rs`).
Per-agent power _intent_ — `wanted: Up | Offline` — is what the operator last
asked of an agent through a power op, held in memory by `PowerStore`
(`hive-c0re/src/stores/power.rs`) until hive-c0re restarts. Nothing is durable:
the swarm owns lasting intent, and declares each agent's state in the hive's
wanted bucket (`workers/wanted.rs`), which outranks the local intent.
`container_view` remains the observed _status_; `Reconcile` nodes converge the
two. Setting `wanted` is never a queued node: the power layer
(`job_queue/power.rs`) writes the row synchronously, then inserts the DAG
(`job_queue/power.rs`) writes it synchronously, then inserts the DAG
whose `Reconcile` reads the fresh value — rapid toggles are last-writer-wins.
Every operator power surface —
dashboard buttons, the MCP tools, and `hivectl stop/start/restart/kill` —
@ -151,8 +154,12 @@ and crash-watch suppression can't drift per surface; the only direct starts
left are the gateway's (no lease, no
harness). Cancelling a still-queued power DAG reverts `wanted` to the
observed state — a cancel means "don't do it," not "do it later." hive-c0re
seeds agents without a row from observed state on first touch (running ⇒
`Up`); destroy removes the row.
seeds an agent without an intent from observed state on first touch (running ⇒
`Up`); destroy removes it. After a restart no agent has an intent, so each one
is seeded from what its container is doing and nothing starts or stops by
itself; the swarm's declaration then starts the agents it declares `Up` and
stops the ones it declares `Offline`. A broad `hivectl start` starts every
agent in scope.
The admin-socket responses carry the submitted DAG ids; `hivectl` polls
`HostRequest::QueueNodes` (~1s) and prints a progress line per DAG — roll-up
@ -314,8 +321,8 @@ already attempted, so a closure that fails to deploy gets one try per process.
The boot log line records the rebuild / deferred / up-to-date counts so the
operator sees at a glance how much work the boot triggered. `auto_update::run`
seeds agents without an `agent_power` row from observed state during
classification (the one-time migration; thereafter the DB is authoritative).
seeds agents without an intent from observed state during classification, so a
boot with no operator action in this process never finds drift of its own.
## Meta flake