docs(approvals): reframe stale 'the manager' as the root agent
The last docs/ piece of the manager-cleanup. The manager is no longer a structural role — root-ness is purely topological. Reframe: - title 'Approvals + manager + helper events' -> 'Approvals + helper events' - section headers: 'Manager view of applied'/'Manager policy'/'Manager (ruth) is hive-c0re-managed'/'Helper events to the manager' -> root-agent / root-bootstrap-container equivalents - body prose: 'the manager (ruth)' -> 'the root agent' (or 'the submitter' in the approval-flow steps) - authority semantics: 'manager-only' -> approvals are submitted by an agent with the approvals tool group, for its direct children - dropped the stale 'the manager refuses to destroy itself' line (the bootstrap container is now destroyable + transient; recreated on startup) Kept the genuine code/protocol identifiers (nixosConfigurations.manager, manager_server, role:manager prompt block, notify_manager, the /run/hyperhive/manager/ socket path) — renaming those would diverge from the source (de-hardcoding is its own backend cleanup).
This commit is contained in:
parent
211dab6f90
commit
e18ddff0b0
1 changed files with 86 additions and 71 deletions
|
|
@ -1,17 +1,20 @@
|
||||||
# Approvals + manager + helper events
|
# Approvals + helper events
|
||||||
|
|
||||||
The approval queue is hyperhive's pivot: nothing that changes the
|
The approval queue is hyperhive's pivot: nothing that changes the
|
||||||
shape of an agent (its config, whether it exists) happens without an
|
shape of an agent (its config, whether it exists) happens without an
|
||||||
operator click. The manager (`ruth`) is the policy gate in front of
|
operator click. The submitting agent — any agent with the `approvals`
|
||||||
that queue; helper events are how it stays informed about what
|
tool group, which manages the config of its **direct children** (the
|
||||||
happens after a decision lands.
|
root agent for top-level agents; a sub-manager for its own subtree) — is
|
||||||
|
the policy gate in front of that queue; helper events are how it stays
|
||||||
|
informed about what happens after a decision lands.
|
||||||
|
|
||||||
## End-to-end approval flow
|
## End-to-end approval flow
|
||||||
|
|
||||||
1. Manager edits files under `/agents/<name>/config/` (any tracked
|
1. The submitting agent (the child's parent, holding the `approvals`
|
||||||
path, but `agent.nix` is the contract entry point) and commits
|
tool group) edits files under the child's `/agents/<name>/config/`
|
||||||
with its own git identity.
|
(any tracked path, but `agent.nix` is the contract entry point) and
|
||||||
2. Manager submits the commit sha via `request_apply_commit(agent,
|
commits with its own git identity.
|
||||||
|
2. The submitting agent submits the commit sha via `request_apply_commit(agent,
|
||||||
commit_ref)`. `commit_ref` must be a commit **sha** (7-40 hex
|
commit_ref)`. `commit_ref` must be a commit **sha** (7-40 hex
|
||||||
chars, short or full) — a branch or tag name is rejected so the
|
chars, short or full) — a branch or tag name is rejected so the
|
||||||
approval pins an immutable commit.
|
approval pins an immutable commit.
|
||||||
|
|
@ -22,9 +25,9 @@ happens after a decision lands.
|
||||||
resolved commit — `git fetch <remote> <sha>:<dst>` can't fetch
|
resolved commit — `git fetch <remote> <sha>:<dst>` can't fetch
|
||||||
by a bare sha (the left side of a refspec is a remote *ref
|
by a bare sha (the left side of a refspec is a remote *ref
|
||||||
name*), so the resolution happens on hive-c0re's side. The
|
name*), so the resolution happens on hive-c0re's side. The
|
||||||
approval row stores both the manager-supplied sha and the
|
approval row stores both the submitted sha and the
|
||||||
canonical hive-c0re-vouched sha. From here on the proposed
|
canonical hive-c0re-vouched sha. From here on the proposed
|
||||||
repo is irrelevant for this approval — the manager can amend,
|
repo is irrelevant for this approval — the submitter can amend,
|
||||||
force-push, or `rm -rf` the proposed repo and the queued
|
force-push, or `rm -rf` the proposed repo and the queued
|
||||||
approval still points at an immutable git object inside
|
approval still points at an immutable git object inside
|
||||||
applied.
|
applied.
|
||||||
|
|
@ -38,7 +41,7 @@ happens after a decision lands.
|
||||||
runs `nix flake lock` (no `--update-input` flags, so it only
|
runs `nix flake lock` (no `--update-input` flags, so it only
|
||||||
fills missing entries), and rejects if the committed
|
fills missing entries), and rejects if the committed
|
||||||
`flake.lock` differs from the result. Triggered when the
|
`flake.lock` differs from the result. Triggered when the
|
||||||
manager added or removed `inputs` in `flake.nix` without
|
submitter added or removed `inputs` in `flake.nix` without
|
||||||
re-running `nix flake lock`. Fix: run `nix flake lock` in
|
re-running `nix flake lock`. Fix: run `nix flake lock` in
|
||||||
the config repo, commit, and re-submit.
|
the config repo, commit, and re-submit.
|
||||||
- **Duplicate inputs** — groups lock nodes by their canonical
|
- **Duplicate inputs** — groups lock nodes by their canonical
|
||||||
|
|
@ -48,7 +51,7 @@ happens after a decision lands.
|
||||||
the `follows` directive, re-lock, and re-submit.
|
the `follows` directive, re-lock, and re-submit.
|
||||||
Both checks only flag *new* violations — agents whose lock
|
Both checks only flag *new* violations — agents whose lock
|
||||||
already carried duplicates before this check was added are
|
already carried duplicates before this check was added are
|
||||||
unaffected until a coordinated config-change pass via manager.
|
unaffected until a coordinated config-change pass.
|
||||||
4. Operator sees the proposal as a card on the dashboard — a
|
4. Operator sees the proposal as a card on the dashboard — a
|
||||||
full multi-file diff, toggleable between three bases (vs the
|
full multi-file diff, toggleable between three bases (vs the
|
||||||
running tree / vs the last approved proposal / vs the
|
running tree / vs the last approved proposal / vs the
|
||||||
|
|
@ -60,32 +63,36 @@ happens after a decision lands.
|
||||||
failure, main stays put and the working tree resets back to
|
failure, main stays put and the working tree resets back to
|
||||||
the previous deployed commit.
|
the previous deployed commit.
|
||||||
6. `HelperEvent::ApprovalResolved` (and `Rebuilt` for the
|
6. `HelperEvent::ApprovalResolved` (and `Rebuilt` for the
|
||||||
ApplyCommit kind) land in the manager's inbox, carrying both
|
ApplyCommit kind) land in the root agent's inbox, carrying both
|
||||||
the canonical sha and the terminal tag.
|
the canonical sha and the terminal tag. (Helper events currently route
|
||||||
|
to the root agent via `notify_manager` regardless of which agent
|
||||||
|
submitted the approval; routing them to the submitting agent is tracked
|
||||||
|
in #1953.)
|
||||||
|
|
||||||
### Withdrawing a pending approval
|
### Withdrawing a pending approval
|
||||||
|
|
||||||
The manager can call `cancel_loose_end(kind: "approval", id)` to
|
The submitting agent can call `cancel_loose_end(kind: "approval", id)` to
|
||||||
withdraw an approval that hasn't been acted on yet.
|
withdraw an approval that hasn't been acted on yet.
|
||||||
The row transitions to `ApprovalStatus::Cancelled` (distinct from
|
The row transitions to `ApprovalStatus::Cancelled` (distinct from
|
||||||
`Denied`/`Failed`), the dashboard pulls the card out of the
|
`Denied`/`Failed`), the dashboard pulls the card out of the
|
||||||
pending pane, and `ApprovalResolved { status: "cancelled" }` fires
|
pending pane, and `ApprovalResolved { status: "cancelled" }` fires
|
||||||
on the manager + dashboard channels. Approvals that have already
|
on the root agent + dashboard channels. Approvals that have already
|
||||||
been approved/denied/failed return an error — the resolution is
|
been approved/denied/failed return an error — the resolution is
|
||||||
final once the operator (or a lifecycle failure) acted on the row.
|
final once the operator (or a lifecycle failure) acted on the row.
|
||||||
|
|
||||||
Sub-agent surface refuses the `approval` kind with a clear error:
|
The socket refuses the `approval` kind with a clear error for any
|
||||||
sub-agents don't submit approvals, so they have nothing of their
|
agent that lacks the `approvals` tool group: only an agent with that
|
||||||
own to withdraw. Manager-only.
|
group submits approvals (for its direct children), so an agent
|
||||||
|
without it has nothing of its own to withdraw.
|
||||||
|
|
||||||
`InitConfig` approvals are the first step in a two-step spawn
|
`InitConfig` approvals are the first step in a two-step spawn
|
||||||
flow. On approve, hive-c0re seeds the proposed config repo with
|
flow. On approve, hive-c0re seeds the proposed config repo with
|
||||||
a default `agent.nix` template and sends the manager
|
a default `agent.nix` template and sends `HelperEvent::ConfigReady { agent }`
|
||||||
`HelperEvent::ConfigReady { agent }`. The manager then reviews,
|
to the root agent. The submitting agent then reviews,
|
||||||
edits, and commits the template before calling `request_apply_commit`
|
edits, and commits the template before calling `request_apply_commit`
|
||||||
to proceed to an `ApplyCommit` approval. The first `ApplyCommit`
|
to proceed to an `ApplyCommit` approval. The first `ApplyCommit`
|
||||||
creates the container; subsequent ones rebuild it with new config.
|
creates the container; subsequent ones rebuild it with new config.
|
||||||
This gives the manager (and operator) an explicit review gate on the
|
This gives the submitting agent (and operator) an explicit review gate on the
|
||||||
initial configuration before any container is created.
|
initial configuration before any container is created.
|
||||||
|
|
||||||
### Approval kinds (wire shapes)
|
### Approval kinds (wire shapes)
|
||||||
|
|
@ -94,7 +101,7 @@ initial configuration before any container is created.
|
||||||
`commit_ref` encoding because that field is overloaded as the
|
`commit_ref` encoding because that field is overloaded as the
|
||||||
kind-specific payload carrier.
|
kind-specific payload carrier.
|
||||||
|
|
||||||
- `ApplyCommit` — `commit_ref` is the manager-supplied git sha
|
- `ApplyCommit` — `commit_ref` is the submitted git sha
|
||||||
(7-40 hex chars). The canonical, hive-c0re-vouched sha after the
|
(7-40 hex chars). The canonical, hive-c0re-vouched sha after the
|
||||||
proposal fetch lives in `fetched_sha` on the same `Approval`
|
proposal fetch lives in `fetched_sha` on the same `Approval`
|
||||||
row (only `ApplyCommit` populates it). See the End-to-end flow
|
row (only `ApplyCommit` populates it). See the End-to-end flow
|
||||||
|
|
@ -116,15 +123,15 @@ kind-specific payload carrier.
|
||||||
CLI). The host-level `HostRequest::Spawn` variant bypasses the
|
CLI). The host-level `HostRequest::Spawn` variant bypasses the
|
||||||
approval queue entirely — privileged-context use only (operator
|
approval queue entirely — privileged-context use only (operator
|
||||||
on the host shell, test scripts, one-off recoveries). The
|
on the host shell, test scripts, one-off recoveries). The
|
||||||
manager-side `RequestSpawn` is gone; managers go through the
|
agent-side `RequestSpawn` is gone; the submitting agent goes through the
|
||||||
`InitConfig` → `ApplyCommit` two-step instead so the spawn
|
`InitConfig` → `ApplyCommit` two-step instead so the spawn
|
||||||
captures their customised config.
|
captures the customised config.
|
||||||
- `InitConfig` — `commit_ref` is empty; the variant just gates
|
- `InitConfig` — `commit_ref` is empty; the variant just gates
|
||||||
"seed the proposed repo with the default template" against
|
"seed the proposed repo with the default template" against
|
||||||
operator approval. Step 1 of the two-step spawn flow above.
|
operator approval. Step 1 of the two-step spawn flow above.
|
||||||
- `UpdateMetaInputs` — `commit_ref` stores the JSON-encoded inputs
|
- `UpdateMetaInputs` — `commit_ref` stores the JSON-encoded inputs
|
||||||
array (`"[]"` = all inputs, `"[\"nixpkgs\"]"` = just nixpkgs,
|
array (`"[]"` = all inputs, `"[\"nixpkgs\"]"` = just nixpkgs,
|
||||||
etc.). `agent` field is set to `ruth` (the requesting manager).
|
etc.). `agent` field is set to the requesting root agent.
|
||||||
On approve hive-c0re runs `nix flake update [inputs...]` on the
|
On approve hive-c0re runs `nix flake update [inputs...]` on the
|
||||||
meta flake and commits the resulting lock changes.
|
meta flake and commits the resulting lock changes.
|
||||||
- `SchedulePrompt` — `commit_ref` stores the JSON-encoded
|
- `SchedulePrompt` — `commit_ref` stores the JSON-encoded
|
||||||
|
|
@ -140,7 +147,7 @@ kind-specific payload carrier.
|
||||||
Two ways a row lands in `scheduled_prompts`:
|
Two ways a row lands in `scheduled_prompts`:
|
||||||
|
|
||||||
- **Operator-direct** (`source = "operator"`): the operator adds a schedule through the dashboard form. Lands in the table immediately, no approval gate — operator action is already the trust boundary.
|
- **Operator-direct** (`source = "operator"`): the operator adds a schedule through the dashboard form. Lands in the table immediately, no approval gate — operator action is already the trust boundary.
|
||||||
- **Agent-requested** (`source = "approval:<id>"`): a sub-agent (or the manager) submits a `RequestSchedulePrompt` through the manager socket. An `ApprovalKind::SchedulePrompt` row is queued; on approve, hive-c0re inserts the schedule row with `source = approval:<id>` so the audit trail points back at the operator decision (above).
|
- **Agent-requested** (`source = "approval:<id>"`): an agent submits a `RequestSchedulePrompt` through its MCP socket (the `request_schedule_prompt` tool, `scheduling` group). An `ApprovalKind::SchedulePrompt` row is queued; on approve, hive-c0re inserts the schedule row with `source = approval:<id>` so the audit trail points back at the operator decision (above).
|
||||||
|
|
||||||
No self-target shortcut: even agent-self schedules need approval. The existing `remind` MCP tool stays the quick self-wake path (no approval, lands directly in the agent's own inbox); this module is the bigger, multi-recipient, operator-visible thing.
|
No self-target shortcut: even agent-self schedules need approval. The existing `remind` MCP tool stays the quick self-wake path (no approval, lands directly in the agent's own inbox); this module is the bigger, multi-recipient, operator-visible thing.
|
||||||
|
|
||||||
|
|
@ -206,8 +213,10 @@ systemd drop-in, fails any pending approvals. Persistent state
|
||||||
**kept by default** — recreating the agent with the same name
|
**kept by default** — recreating the agent with the same name
|
||||||
reuses prior config + login. With `purge = true` the agent's
|
reuses prior config + login. With `purge = true` the agent's
|
||||||
`/var/lib/hyperhive/{agents,applied}/<name>/` trees are also
|
`/var/lib/hyperhive/{agents,applied}/<name>/` trees are also
|
||||||
wiped (config history + creds + notes gone forever). The manager
|
wiped (config history + creds + notes gone forever). The
|
||||||
refuses to destroy itself.
|
root/bootstrap container is destroyable like any other — hive-c0re
|
||||||
|
recreates it on the next startup if it's absent, so destroying it is
|
||||||
|
transient.
|
||||||
|
|
||||||
## Meta flake
|
## Meta flake
|
||||||
|
|
||||||
|
|
@ -255,7 +264,7 @@ from the agent list; if it differs from disk, runs
|
||||||
`nix flake lock` + commits as `regenerate meta flake` (or
|
`nix flake lock` + commits as `regenerate meta flake` (or
|
||||||
`seed meta from N agent(s)` on the very first call).
|
`seed meta from N agent(s)` on the very first call).
|
||||||
|
|
||||||
The manager has `/meta` RO-bound inside its container:
|
The root agent has `/meta` RO-bound inside its container:
|
||||||
`git -C /meta log --oneline` is the swarm-wide deploy log,
|
`git -C /meta log --oneline` is the swarm-wide deploy log,
|
||||||
`cat /meta/flake.lock | jq '.nodes["agent-<n>"].locked'`
|
`cat /meta/flake.lock | jq '.nodes["agent-<n>"].locked'`
|
||||||
resolves which sha each agent is pinned at right now.
|
resolves which sha each agent is pinned at right now.
|
||||||
|
|
@ -265,15 +274,15 @@ per container row.
|
||||||
## Two repos per agent
|
## Two repos per agent
|
||||||
|
|
||||||
```
|
```
|
||||||
/var/lib/hyperhive/agents/<name>/config/ proposed — manager RW
|
/var/lib/hyperhive/agents/<name>/config/ proposed — submitting agent RW
|
||||||
└── <anything> # any files the manager
|
└── <anything> # any files the submitting
|
||||||
# wants in the commit.
|
# agent wants in the commit.
|
||||||
# agent.nix is the
|
# agent.nix is the
|
||||||
# convention entry
|
# convention entry
|
||||||
# point; flake.nix is
|
# point; flake.nix is
|
||||||
# tracked boilerplate
|
# tracked boilerplate
|
||||||
# (manager doesn't edit
|
# (submitting agent doesn't
|
||||||
# it).
|
# edit it).
|
||||||
|
|
||||||
/var/lib/hyperhive/applied/<name>/ applied — core-only
|
/var/lib/hyperhive/applied/<name>/ applied — core-only
|
||||||
├── .git/ # tag-rich history
|
├── .git/ # tag-rich history
|
||||||
|
|
@ -281,7 +290,7 @@ per container row.
|
||||||
│ # boilerplate exporting
|
│ # boilerplate exporting
|
||||||
│ # nixosModules.default
|
│ # nixosModules.default
|
||||||
├── agent.nix # working tree of main
|
├── agent.nix # working tree of main
|
||||||
└── <other manager files> # also tracked
|
└── <other committed files> # also tracked
|
||||||
|
|
||||||
/var/lib/hyperhive/meta/ swarm-wide flake — core
|
/var/lib/hyperhive/meta/ swarm-wide flake — core
|
||||||
├── .git/ # one commit per successful
|
├── .git/ # one commit per successful
|
||||||
|
|
@ -290,7 +299,7 @@ per container row.
|
||||||
└── flake.lock # pins each agent's sha
|
└── flake.lock # pins each agent's sha
|
||||||
```
|
```
|
||||||
|
|
||||||
Why two physical repos: the manager's `/agents/<n>/config/` is
|
Why two physical repos: the submitting agent's `/agents/<n>/config/` is
|
||||||
RW — a buggy or hostile agent can `git clean -fdx` its own
|
RW — a buggy or hostile agent can `git clean -fdx` its own
|
||||||
proposed tree. The applied repo is never bind-mounted (except
|
proposed tree. The applied repo is never bind-mounted (except
|
||||||
the read-only `.git` exposure described below) so a destructive
|
the read-only `.git` exposure described below) so a destructive
|
||||||
|
|
@ -318,7 +327,7 @@ underlying commit inside the applied repo:
|
||||||
| `denied/<id>` | operator deny | yes (body = operator note) |
|
| `denied/<id>` | operator deny | yes (body = operator note) |
|
||||||
|
|
||||||
`applied/main` is always the latest `deployed/*`. `denied/` and
|
`applied/main` is always the latest `deployed/*`. `denied/` and
|
||||||
`failed/` are terminal; the manager submits a new commit + new
|
`failed/` are terminal; the submitting agent submits a new commit + new
|
||||||
approval id to retry. Because tags are first-class git objects,
|
approval id to retry. Because tags are first-class git objects,
|
||||||
rejected and failed trees stay browsable forever — `git log
|
rejected and failed trees stay browsable forever — `git log
|
||||||
--tags` in the applied repo is the audit trail.
|
--tags` in the applied repo is the audit trail.
|
||||||
|
|
@ -384,29 +393,33 @@ The org is private and agents are not members, so only the
|
||||||
reach another agent's config — or even its own — through the
|
reach another agent's config — or even its own — through the
|
||||||
forge. The tokenised push URL is passed inline to `git push`,
|
forge. The tokenised push URL is passed inline to `git push`,
|
||||||
never written into `applied/<n>/.git/config`; that repo is
|
never written into `applied/<n>/.git/config`; that repo is
|
||||||
RO-bind-mounted into the manager, and a stored token would leak
|
RO-bind-mounted into the root agent, and a stored token would leak
|
||||||
core's admin credential to an agent.
|
core's admin credential to an agent.
|
||||||
|
|
||||||
The dashboard deep-links into this org — a `config repo` link
|
The dashboard deep-links into this org — a `config repo` link
|
||||||
per container row and a `commit on forge` link per approval
|
per container row and a `commit on forge` link per approval
|
||||||
card. See `docs/web-ui.md`.
|
card. See `docs/web-ui.md`.
|
||||||
|
|
||||||
### Manager view of applied + meta
|
### Root-agent view of applied + meta
|
||||||
|
|
||||||
The manager container gets three host-side bind mounts via
|
The root agent container gets three host-side bind mounts via
|
||||||
`set_nspawn_flags`:
|
`set_nspawn_flags`:
|
||||||
|
|
||||||
- `/var/lib/hyperhive/agents/` → `/agents/` (RW) — proposed
|
- `/var/lib/hyperhive/agents/` → `/agents/` (RW) — proposed
|
||||||
repos. Manager edits + commits per-agent config here.
|
repos. The root agent edits + commits per-agent config here.
|
||||||
- `/var/lib/hyperhive/applied/` → `/applied/` (RO) — every
|
- `/var/lib/hyperhive/applied/` → `/applied/` (RO) — every
|
||||||
agent's authoritative applied repo, including `.git`.
|
agent's authoritative applied repo, including `.git`.
|
||||||
- `/var/lib/hyperhive/meta/` → `/meta/` (RO) — the swarm-wide
|
- `/var/lib/hyperhive/meta/` → `/meta/` (RO) — the swarm-wide
|
||||||
deploy flake.
|
deploy flake.
|
||||||
|
|
||||||
|
This is the **root agent's** view — RW over *every* agent's config. An
|
||||||
|
agent with the `approvals` group that owns a subtree (a sub-manager) has
|
||||||
|
the equivalent RW scoped to its own children's config repos.
|
||||||
|
|
||||||
Each proposed repo (`/agents/<n>/config/`) is pre-configured
|
Each proposed repo (`/agents/<n>/config/`) is pre-configured
|
||||||
with `applied` as a git remote pointing at
|
with `applied` as a git remote pointing at
|
||||||
`/applied/<n>/.git`. Useful incantations from inside the
|
`/applied/<n>/.git`. Useful incantations from inside the
|
||||||
manager:
|
root agent's container:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git -C /agents/<n>/config fetch applied
|
git -C /agents/<n>/config fetch applied
|
||||||
|
|
@ -420,7 +433,7 @@ git -C /meta log --oneline # swarm-wide deplo
|
||||||
cat /meta/flake.lock | jq '.nodes | with_entries(select(.key | startswith("agent-")))'
|
cat /meta/flake.lock | jq '.nodes | with_entries(select(.key | startswith("agent-")))'
|
||||||
```
|
```
|
||||||
|
|
||||||
The RO binds block push at the kernel level, so the manager
|
The RO binds block push at the kernel level, so the root agent
|
||||||
can only fetch / read — git plumbing inside the container
|
can only fetch / read — git plumbing inside the container
|
||||||
cannot corrupt either authoritative repo.
|
cannot corrupt either authoritative repo.
|
||||||
|
|
||||||
|
|
@ -446,16 +459,16 @@ each phase is a no-op once already applied. Behaviour:
|
||||||
|
|
||||||
No state loss in either migration. claude creds, /state/
|
No state loss in either migration. claude creds, /state/
|
||||||
notes, the events DB, proposed history, and applied history
|
notes, the events DB, proposed history, and applied history
|
||||||
all survive. The manager keeps its session; sub-agents stay
|
all survive. The root agent keeps its session; sub-agents stay
|
||||||
logged in.
|
logged in.
|
||||||
|
|
||||||
## Manager (`ruth`) is hive-c0re-managed
|
## The root/bootstrap container is hive-c0re-managed
|
||||||
|
|
||||||
The manager container runs through the **same lifecycle as
|
The root agent container runs through the **same lifecycle as
|
||||||
sub-agents**. On `hive-c0re serve` startup, if `ruth` is missing,
|
sub-agents**. On `hive-c0re serve` startup, if `ruth` is missing,
|
||||||
hive-c0re creates it. The manager's flake lives at
|
hive-c0re creates it. The root agent's flake lives at
|
||||||
`/var/lib/hyperhive/applied/ruth/`; its proposed config at
|
`/var/lib/hyperhive/applied/ruth/`; its proposed config at
|
||||||
`/var/lib/hyperhive/agents/ruth/config/`. Manager can edit its own
|
`/var/lib/hyperhive/agents/ruth/config/`. The root agent can edit its own
|
||||||
`agent.nix` (visible inside the container at `/agents/ruth/config/`)
|
`agent.nix` (visible inside the container at `/agents/ruth/config/`)
|
||||||
and submit `request_apply_commit("ruth", <sha>)` for operator
|
and submit `request_apply_commit("ruth", <sha>)` for operator
|
||||||
approval.
|
approval.
|
||||||
|
|
@ -467,29 +480,29 @@ Differences from sub-agents:
|
||||||
- Web UI port via `lifecycle::agent_web_port("ruth")` — same
|
- Web UI port via `lifecycle::agent_web_port("ruth")` — same
|
||||||
FNV-1a hash as every other agent (8100..8999 range).
|
FNV-1a hash as every other agent (8100..8999 range).
|
||||||
- `set_nspawn_flags` adds two extra binds: `/var/lib/hyperhive/agents`
|
- `set_nspawn_flags` adds two extra binds: `/var/lib/hyperhive/agents`
|
||||||
→ `/agents` (RW) so the manager can edit per-agent proposed repos,
|
→ `/agents` (RW) so the root agent can edit per-agent proposed repos,
|
||||||
and `/var/lib/hyperhive/applied` → `/applied` (RO) so the manager
|
and `/var/lib/hyperhive/applied` → `/applied` (RO) so the root agent
|
||||||
can `git fetch` deployed/failed/denied tags from any agent's
|
can `git fetch` deployed/failed/denied tags from any agent's
|
||||||
authoritative applied repo (see "Manager view of applied" below).
|
authoritative applied repo (see "Root-agent view of applied" below).
|
||||||
- First-deploy spawn bypasses the approval queue (manager is
|
- First-deploy spawn bypasses the approval queue (the root agent is
|
||||||
required infrastructure).
|
required infrastructure).
|
||||||
- Per-agent socket lives at `/run/hyperhive/manager/`, owned by
|
- Per-agent socket lives at `/run/hyperhive/manager/`, owned by
|
||||||
`manager_server::start`.
|
`manager_server::start`.
|
||||||
|
|
||||||
**Migration note** (for older hosts): drop any `containers.root =
|
**Migration note** (for older hosts): drop any `containers.root =
|
||||||
{ ... }` block from your host NixOS config. hyperhive creates and
|
{ ... }` block from your host NixOS config. hyperhive creates and
|
||||||
updates the manager itself.
|
updates the root agent itself.
|
||||||
|
|
||||||
## Manager policy
|
## Root-agent policy
|
||||||
|
|
||||||
From `hive-ag3nt/prompts/system.md` (`<!-- role:manager -->` block,
|
From `hive-ag3nt/prompts/system.md` (`<!-- role:manager -->` block,
|
||||||
rendered via `hive_ag3nt::prompt::render`): the manager does NOT
|
rendered via `hive_ag3nt::prompt::render`): the root agent does NOT
|
||||||
rubber-stamp sub-agent config requests. It verifies (role match,
|
rubber-stamp sub-agent config requests. It verifies (role match,
|
||||||
package legitimacy, cheaper alternative, blast radius) before
|
package legitimacy, cheaper alternative, blast radius) before
|
||||||
committing and calling `request_apply_commit`.
|
committing and calling `request_apply_commit`.
|
||||||
|
|
||||||
For ambiguous cases or anything that needs human signal, the
|
For ambiguous cases or anything that needs human signal, the
|
||||||
manager calls `ask(question, options?, multi?, ttl_seconds?, to?)` —
|
the root agent calls `ask(question, options?, multi?, ttl_seconds?, to?)` —
|
||||||
queues the question and returns the id immediately. When `to` is
|
queues the question and returns the id immediately. When `to` is
|
||||||
omitted (or `"operator"`) the question shows up on the dashboard;
|
omitted (or `"operator"`) the question shows up on the dashboard;
|
||||||
when `to` is a sub-agent's name, the recipient receives a
|
when `to` is a sub-agent's name, the recipient receives a
|
||||||
|
|
@ -500,7 +513,7 @@ in the asker's inbox. Storage is `hive-c0re::operator_questions`
|
||||||
(sqlite) — same table, with a nullable `target` column
|
(sqlite) — same table, with a nullable `target` column
|
||||||
(NULL = operator). Dispatch goes through
|
(NULL = operator). Dispatch goes through
|
||||||
`hive-c0re/src/questions.rs::{handle_ask, handle_answer}` so both
|
`hive-c0re/src/questions.rs::{handle_ask, handle_answer}` so both
|
||||||
the agent + manager surfaces stay aligned. The answer flow is:
|
the agent + root-agent surfaces stay aligned. The answer flow is:
|
||||||
|
|
||||||
```
|
```
|
||||||
POST /answer-question/{id} agent: Answer { id, answer }
|
POST /answer-question/{id} agent: Answer { id, answer }
|
||||||
|
|
@ -513,19 +526,21 @@ POST /answer-question/{id} agent: Answer { id, answer }
|
||||||
Two more paths resolve a pending question with a sentinel answer:
|
Two more paths resolve a pending question with a sentinel answer:
|
||||||
|
|
||||||
- `POST /cancel-question/{id}` (✗ CANC3L button on the dashboard)
|
- `POST /cancel-question/{id}` (✗ CANC3L button on the dashboard)
|
||||||
resolves with `[cancelled]`. The manager sees a terminal state
|
resolves with `[cancelled]`. The root agent sees a terminal state
|
||||||
and can fall back.
|
and can fall back.
|
||||||
- `ttl_seconds` deadline: a tokio watchdog spawned at submit time
|
- `ttl_seconds` deadline: a tokio watchdog spawned at submit time
|
||||||
fires `answer(id, "[expired]")` once the ttl runs out. Already-
|
fires `answer(id, "[expired]")` once the ttl runs out. Already-
|
||||||
resolved races no-op. The dashboard surfaces a `⏳ MM:SS` chip
|
resolved races no-op. The dashboard surfaces a `⏳ MM:SS` chip
|
||||||
on each pending question with a deadline.
|
on each pending question with a deadline.
|
||||||
|
|
||||||
## Helper events to the manager
|
## Helper events to the root agent
|
||||||
|
|
||||||
`Coordinator::notify_manager(&HelperEvent)` enqueues an inbox
|
`Coordinator::notify_manager(&HelperEvent)` enqueues an inbox
|
||||||
message from sender `system` with the event JSON in the body. The
|
message from sender `system` with the event JSON in the body. The
|
||||||
manager harness no longer short-circuits these — they drive a
|
root agent's harness no longer short-circuits these — they drive a
|
||||||
regular claude turn so the manager can react. Variants
|
regular claude turn so the root agent can react. (Today these go to the
|
||||||
|
root agent regardless of which agent submitted the approval — routing to
|
||||||
|
the submitting agent is tracked in #1953.) Variants
|
||||||
(`hive_sh4re::HelperEvent`):
|
(`hive_sh4re::HelperEvent`):
|
||||||
|
|
||||||
- `ApprovalResolved { id, agent, commit_ref, status, note }` —
|
- `ApprovalResolved { id, agent, commit_ref, status, note }` —
|
||||||
|
|
@ -537,7 +552,7 @@ regular claude turn so the manager can react. Variants
|
||||||
(covers startup scan + manual `/rebuild` from dashboard) +
|
(covers startup scan + manual `/rebuild` from dashboard) +
|
||||||
`actions::approve` (ApplyCommit).
|
`actions::approve` (ApplyCommit).
|
||||||
- `Killed { agent }` — admin `HostRequest::Kill` + dashboard
|
- `Killed { agent }` — admin `HostRequest::Kill` + dashboard
|
||||||
`/kill` + manager `Kill` MCP tool.
|
`/kill` + the `Kill` MCP tool.
|
||||||
- `Destroyed { agent }` — `actions::destroy`.
|
- `Destroyed { agent }` — `actions::destroy`.
|
||||||
- `ContainerCrash { agent, note }` — `crash_watch`: a previously-
|
- `ContainerCrash { agent, note }` — `crash_watch`: a previously-
|
||||||
running container went away with no operator-initiated transient
|
running container went away with no operator-initiated transient
|
||||||
|
|
@ -546,19 +561,19 @@ regular claude turn so the manager can react. Variants
|
||||||
tombstone, three `POLL_INTERVAL`s — closes the race where a
|
tombstone, three `POLL_INTERVAL`s — closes the race where a
|
||||||
lifecycle op finishes between two crash-watch polls and the
|
lifecycle op finishes between two crash-watch polls and the
|
||||||
container shows briefly as "stopped without transient" before
|
container shows briefly as "stopped without transient" before
|
||||||
the next start). Manager can `start` it again or escalate.
|
the next start). The root agent can `start` it again or escalate.
|
||||||
- `NeedsLogin { agent }` — sub-agent has no claude session yet.
|
- `NeedsLogin { agent }` — sub-agent has no claude session yet.
|
||||||
Manager can't act directly (interactive OAuth); typically flags
|
The root agent can't act directly (interactive OAuth); typically flags
|
||||||
the operator.
|
the operator.
|
||||||
- `LoggedIn { agent }` — sub-agent just completed login. Manager
|
- `LoggedIn { agent }` — sub-agent just completed login. The root agent
|
||||||
often greets the agent on this event.
|
often greets the agent on this event.
|
||||||
- `ConfigReady { agent }` — a new agent's proposed config repo was
|
- `ConfigReady { agent }` — a new agent's proposed config repo was
|
||||||
just seeded (post-`InitConfig` approval). The manager can now
|
just seeded (post-`InitConfig` approval). The root agent can now
|
||||||
edit `/agents/<agent>/config/agent.nix`, commit the changes,
|
edit `/agents/<agent>/config/agent.nix`, commit the changes,
|
||||||
and submit `request_apply_commit` with the commit sha to create
|
and submit `request_apply_commit` with the commit sha to create
|
||||||
the container (first ApplyCommit also triggers spawn bookkeeping).
|
the container (first ApplyCommit also triggers spawn bookkeeping).
|
||||||
- `NeedsUpdate { agent }` — sub-agent's recorded flake rev is
|
- `NeedsUpdate { agent }` — sub-agent's recorded flake rev is
|
||||||
stale. Manager calls `update(name)` to rebuild — idempotent,
|
stale. The root agent calls `update(name)` to rebuild — idempotent,
|
||||||
no approval required.
|
no approval required.
|
||||||
- `QuestionAnswered { id, question, answer, answerer }` —
|
- `QuestionAnswered { id, question, answer, answerer }` —
|
||||||
dashboard `/answer-question/{id}` (answerer = `"operator"`),
|
dashboard `/answer-question/{id}` (answerer = `"operator"`),
|
||||||
|
|
@ -581,18 +596,18 @@ that don't change the deployed commit (e.g.
|
||||||
`auto_update::rebuild_agent` reapplying the existing main, or the
|
`auto_update::rebuild_agent` reapplying the existing main, or the
|
||||||
dashboard `↻ R3BU1LD` button when the lock didn't move). When set,
|
dashboard `↻ R3BU1LD` button when the lock didn't move). When set,
|
||||||
`git show <sha>` against `/agents/<n>/applied.git` inside the
|
`git show <sha>` against `/agents/<n>/applied.git` inside the
|
||||||
manager container yields the exact tree that was referenced.
|
bootstrap container yields the exact tree that was referenced.
|
||||||
|
|
||||||
To add a new event: new `HelperEvent` variant + call sites + update
|
To add a new event: new `HelperEvent` variant + call sites + update
|
||||||
`prompts/system.md` (`<!-- role:manager -->` block, the lifecycle-
|
`prompts/system.md` (`<!-- role:manager -->` block, the lifecycle-
|
||||||
event list) so the manager knows the new shape.
|
event list) so the root agent knows the new shape.
|
||||||
|
|
||||||
## Auto-update on startup
|
## Auto-update on startup
|
||||||
|
|
||||||
`hive-c0re serve` runs `auto_update::run` in a background task right
|
`hive-c0re serve` runs `auto_update::run` in a background task right
|
||||||
after opening the coordinator. It enumerates managed containers and
|
after opening the coordinator. It enumerates managed containers and
|
||||||
rebuilds any whose recorded hyperhive rev differs from the current
|
rebuilds any whose recorded hyperhive rev differs from the current
|
||||||
one — sub-agents and manager go through the same `lifecycle::rebuild`
|
one — sub-agents and the root agent go through the same `lifecycle::rebuild`
|
||||||
path.
|
path.
|
||||||
|
|
||||||
"Rev" = canonical filesystem path of `cfg.hyperhiveFlake`. Marker
|
"Rev" = canonical filesystem path of `cfg.hyperhiveFlake`. Marker
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue