Compare commits

..

View file

@ -11,12 +11,9 @@ informed about what happens after a decision lands.
## End-to-end approval flow ## End-to-end approval flow
1. The submitting agent (the child's parent, holding the `approvals` 1. The submitting agent (the child's parent, holding the `approvals`
tool group) edits files in the child's proposed config repo tool group) edits files under the child's `/agents/<name>/config/`
(any tracked path, but `agent.nix` is the contract entry point) (any tracked path, but `agent.nix` is the contract entry point) and
and commits with its own git identity. The parent's container has commits with its own git identity.
the child's proposed config repo bind-mounted read-write at
`/agents/<name>/config/` (topology-driven via `set_nspawn_flags`;
the agent's *own* config at `/agents/<self>/config/` is read-only).
2. The submitting agent submits the commit sha via `request_apply_commit(agent, 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
@ -66,11 +63,11 @@ informed about what 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 **submitting agent's** inbox, carrying ApplyCommit kind) land in the root agent's inbox, carrying both
both the canonical sha and the terminal tag. Helper events route to the canonical sha and the terminal tag. (Helper events currently route
the submitting agent via `notify_submitter` (the approval row carries to the root agent via `notify_manager` regardless of which agent
a `submitter` column recording which agent called `request_apply_commit` submitted the approval; routing them to the submitting agent is tracked
or `request_init_config`). in #1953.)
### Withdrawing a pending approval ### Withdrawing a pending approval
@ -91,8 +88,7 @@ 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 `HelperEvent::ConfigReady { agent }` a default `agent.nix` template and sends `HelperEvent::ConfigReady { agent }`
to the submitting agent's inbox via `notify_submitter`. The submitting to the root agent. The submitting agent then reviews,
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.
@ -404,33 +400,26 @@ 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`.
### Submitting agent's view of config repos ### Root-agent view of applied + meta
Every parent agent's container has its **direct children's** proposed The root agent container gets three host-side bind mounts via
config repos bind-mounted read-write (topology-driven: `lifecycle.rs` `set_nspawn_flags`:
calls `bind_child_agent_dirs` for each entry in
`topology::children_of(agent_name)`). An agent with the `approvals`
tool group can therefore edit, commit, and submit changes for any of
its direct children directly inside its container at `/agents/<child>/config/`.
Agents holding the `can_manage_top_level_agents` topology role - `/var/lib/hyperhive/agents/``/agents/` (RW) — proposed
(defined as `ROLE_CAN_MANAGE_TOP_LEVEL_AGENTS` in `hive-c0re/src/topology.rs`) repos. The root agent edits + commits per-agent config here.
get additional host-side bind mounts via `set_nspawn_flags`: - `/var/lib/hyperhive/applied/``/applied/` (RO) — every
agent's authoritative applied repo, including `.git`.
- `/var/lib/hyperhive/agents/``/agents/` (RW) — all top-level
agents' proposed repos (not just direct children).
- `/var/lib/hyperhive/applied/``/applied/` (RO) — every 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.
The root agent holds this role; a sub-manager that only manages a This is the **root agent's** view — RW over *every* agent's config. An
subtree does not, and only has its direct children's config dirs. 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 an agent with `/applied/<n>/.git`. Useful incantations from inside the
the full `/applied` mount: root agent's container:
```sh ```sh
git -C /agents/<n>/config fetch applied git -C /agents/<n>/config fetch applied
@ -444,8 +433,9 @@ 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 — git plumbing inside the The RO binds block push at the kernel level, so the root agent
container cannot corrupt either authoritative repo. can only fetch / read — git plumbing inside the container
cannot corrupt either authoritative repo.
## Migration from the pre-tag / pre-meta schemes ## Migration from the pre-tag / pre-meta schemes
@ -505,25 +495,25 @@ updates the root agent itself.
## Root-agent policy ## Root-agent policy
The system prompt (`hive-ag3nt/prompts/system.md`, rendered via From `hive-ag3nt/prompts/system.md` (`<!-- role:manager -->` block,
`hive_ag3nt::prompt::render`) is the **same for every agent**; what rendered via `hive_ag3nt::prompt::render`): the root agent does NOT
varies is which MCP tools are surfaced (gated by tool groups and rubber-stamp sub-agent config requests. It verifies (role match,
capabilities in `agent.nix`). There is no `role:manager` block that package legitimacy, cheaper alternative, blast radius) before
renders only for the root agent. The root agent's approval-gating committing and calling `request_apply_commit`.
behaviour comes from its CLAUDE.md / agent-specific instructions, not
the system prompt template.
`ask(question, options?, multi?, ttl_seconds?, to?)` is available to For ambiguous cases or anything that needs human signal, the
**any agent** — it queues a question and returns the id immediately. the root agent calls `ask(question, options?, multi?, ttl_seconds?, to?)`
When `to` is omitted (or `"operator"`) the question shows up on the queues the question and returns the id immediately. When `to` is
dashboard; when `to` is another agent's name, the recipient receives a omitted (or `"operator"`) the question shows up on the dashboard;
when `to` is a sub-agent's name, the recipient receives a
`HelperEvent::QuestionAsked` and answers via their own `answer` `HelperEvent::QuestionAsked` and answers via their own `answer`
tool. Either way the answer arrives back as tool. Either way the answer arrives back as
`HelperEvent::QuestionAnswered { id, question, answer, answerer }` `HelperEvent::QuestionAnswered { id, question, answer, answerer }`
in the asker's inbox. Storage is `hive-c0re::operator_questions` 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}`. The answer flow is: `hive-c0re/src/questions.rs::{handle_ask, handle_answer}` so both
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 }
@ -536,21 +526,22 @@ 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 asking agent 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 submitting agent ## Helper events to the root agent
`Coordinator::notify_submitter(approval_id, &HelperEvent)` routes the `Coordinator::notify_manager(&HelperEvent)` enqueues an inbox
event to the agent that originally submitted the approval (looked up from message from sender `system` with the event JSON in the body. The
the `submitter` column on the `approvals` table). The harness delivers it root agent's harness no longer short-circuits these — they drive a
as a regular `system` inbox message so it drives a normal claude turn. regular claude turn so the root agent can react. (Today these go to the
Legacy approval rows that predate the submitter column fall back to the root agent regardless of which agent submitted the approval — routing to
root agent. Variants (`hive_sh4re::HelperEvent`): the submitting agent is tracked in #1953.) Variants
(`hive_sh4re::HelperEvent`):
- `ApprovalResolved { id, agent, commit_ref, status, note }` - `ApprovalResolved { id, agent, commit_ref, status, note }`
fired by `actions::approve` + `actions::deny` whenever an fired by `actions::approve` + `actions::deny` whenever an