docs(approvals): trim historical framing, dedupe ask/answer with conventions.md

This commit is contained in:
iris 2026-08-15 11:46:46 +02:00 committed by mara
commit f29df9cadb

View file

@ -212,10 +212,9 @@ An agent target's delivery is `push_todo` (`Coordinator::push_todo`,
`docs/coordinator.md` covers the mechanism generally), not a broker `docs/coordinator.md` covers the mechanism generally), not a broker
`Message` — a scheduled prompt wakes its target with a todo instead of `Message` — a scheduled prompt wakes its target with a todo instead of
driving an immediate turn, by design. `key = "schedule:<id>"` per driving an immediate turn, by design. `key = "schedule:<id>"` per
target gives `push_todo`'s own upsert-by-key dedup the job a target drives `push_todo`'s own upsert-by-key dedup: a re-fire of the
now-removed `has_pending_with_body` broker check used to do: a re-fire *same schedule* against a target that hasn't reviewed the last one
of the *same schedule* against a target that hasn't reviewed the last collapses into that one todo instead of stacking up.
one collapses into that one todo instead of stacking up.
**`operator` is the one exception** — it's a valid schedule target but **`operator` is the one exception** — it's a valid schedule target but
has no in-container todo inbox, so it keeps the original broker has no in-container todo inbox, so it keeps the original broker
@ -290,8 +289,7 @@ declares one flake input per agent (`agent-<n>.url =
"git+http://<forge>/agent-configs/<n>.git"`) and one "git+http://<forge>/agent-configs/<n>.git"`) and one
`nixosConfigurations.<n>` output per agent. Each output wraps `nixosConfigurations.<n>` output per agent. Each output wraps
`inputs.agent-<n>.nixosModules.default` with the identity + `inputs.agent-<n>.nixosModules.default` with the identity +
`HIVE_PORT` / `HIVE_LABEL` / `HIVE_DASHBOARD_PORT` injection `HIVE_PORT` / `HIVE_LABEL` / `HIVE_DASHBOARD_PORT` injection module.
module that `setup_applied` used to generate inline.
Containers run against `--flake /var/lib/hyperhive/meta#<n>`. Containers run against `--flake /var/lib/hyperhive/meta#<n>`.
The declared input url is the agent's **forge config repo** (the The declared input url is the agent's **forge config repo** (the
@ -419,9 +417,9 @@ submitter pushes again (or closes it) to retry.
### Dispatch via the job queue ### Dispatch via the job queue
Long-running approval work — `MergeConfigPr`, `UpdateMetaInputs`, Long-running approval work — `MergeConfigPr`, `UpdateMetaInputs`,
`Spawn`no longer runs inline inside `actions::approve`. Instead `Spawn`runs as a DAG on the global job queue
the approval handler submits a DAG to the global job queue (`docs/coordinator.md::Job queue`), submitted by the approval handler
(`docs/coordinator.md::Job queue`): rather than run inline:
| `ApprovalKind` | DAG submitted | source | | `ApprovalKind` | DAG submitted | source |
|---|---|---| |---|---|---|
@ -502,9 +500,9 @@ forge into its own state dir, commit on a branch, open a PR**, and let
the operator review and approve it. There is deliberately no second, the operator review and approve it. There is deliberately no second,
mount-shaped path that reaches the same file without the review. mount-shaped path that reaches the same file without the review.
Agents holding the `can_manage_top_level_agents` topology role Agents holding the `can_manage_top_level_agents` topology role (see
(defined as `ROLE_CAN_MANAGE_TOP_LEVEL_AGENTS` in `hive-c0re/src/agent_config/topology.rs`) `hive-c0re/src/agent_config/topology.rs`) get additional host-side
get additional host-side bind mounts via `set_nspawn_flags`: bind mounts via `set_nspawn_flags`:
- `/var/lib/hyperhive/agents/``/agents/` (RW) — all top-level - `/var/lib/hyperhive/agents/``/agents/` (RW) — all top-level
agents' proposed repos (not just direct children). agents' proposed repos (not just direct children).
@ -536,34 +534,24 @@ 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 — git plumbing inside the
container cannot corrupt either authoritative repo. container cannot corrupt either authoritative repo.
## Migration from the pre-tag / pre-meta schemes ## Startup migrations (older hosts)
Both overhauls (tag-driven flow + meta flake) ship in-place hive-c0re runs a couple of idempotent migrations on every startup so a
migrations that run on every hive-c0re startup. Idempotent; host set up before the tag-driven-deploy + meta-flake scheme (both
each phase is a no-op once already applied. Behaviour: described above) converges to it automatically. Each phase is a no-op
once already applied:
- Tag-driven phase: assumes the operator ran the one-shot - **Tags**: agents from before the tag-driven scheme are tagged
`git tag deployed/0 main` script (see commit history / `deployed/0` on `main` once. Non-destructive — it doesn't touch live
earlier docs revisions) once per agent. Tagging is containers, state dirs, or claude creds.
non-destructive: it doesn't touch live containers, state - **Meta flake**: rewrites each `applied/<n>/flake.nix` to the
dirs, or claude creds. module-only boilerplate, wires the `applied` remote in each proposed
- Meta-flake phase: rewrites each `applied/<n>/flake.nix` to repo, and bootstraps the meta repo from the current agent list. Set
the module-only boilerplate, wires the `applied` remote in `HIVE_SKIP_META_MIGRATION=1` on the service to defer this phase.
each proposed repo, and bootstraps the meta repo from the
current agent list. Set `HIVE_SKIP_META_MIGRATION=1` on the
service to defer.
A further step used to `nixos-container update` every No state loss in either migration: claude creds, `/state/` notes, the
container onto `meta#<n>`, guarded by a marker file so it events DB, and both proposed + applied history all survive. The root
ran once per hive. It is gone: containers have been rendered agent keeps its session; sub-agents stay logged in.
onto `meta#<n>` at creation for long enough that no live hive
needs the repoint, and a one-shot nobody can still trigger is
dead weight. Same for the `root``h-root` container rename.
No state loss in either migration. claude creds, /state/
notes, the events DB, proposed history, and applied history
all survive. The root agent keeps its session; sub-agents stay
logged in.
## The root/bootstrap container is hive-c0re-managed ## The root/bootstrap container is hive-c0re-managed
@ -606,35 +594,11 @@ renders only for the root agent. The root agent's approval-gating
behaviour comes from its CLAUDE.md / agent-specific instructions, not behaviour comes from its CLAUDE.md / agent-specific instructions, not
the system prompt template. the system prompt template.
`ask(question, options?, multi?, ttl_seconds?, to?)` is available to Any agent (root or not) can also ask a structured question of the
**any agent** — it queues a question and returns the id immediately. operator or a peer agent via the `ask`/`answer` MCP tools, independent
When `to` is omitted (or `"operator"`) the question shows up on the of the approval flow above — see
dashboard; when `to` is another agent's name, the recipient receives a `docs/conventions.md#question-routing-ask--answer` for the routing
`HelperEvent::QuestionAsked` and answers via their own `answer` rules, `ttl_seconds` expiry, and cancellation.
tool. Either way the answer arrives back as
`HelperEvent::QuestionAnswered { id, question, answer, answerer }`
in the asker's inbox. Storage is `hive-c0re::operator_questions`
(sqlite) — same table, with a nullable `target` column
(NULL = operator). Dispatch goes through
`hive-c0re/src/questions.rs::{handle_ask, handle_answer}`. The answer flow is:
```
POST /answer-question/{id} agent: Answer { id, answer }
→ OperatorQuestions::answer(_, _, "operator") → questions::handle_answer
→ notify_agent(asker, QuestionAnswered { → OperatorQuestions::answer(_, _, agent)
answerer: "operator", ... }) → notify_agent(asker, QuestionAnswered {
answerer: agent, ... })
```
Two more paths resolve a pending question with a sentinel answer:
- `POST /cancel-question/{id}` (✗ CANC3L button on the dashboard)
resolves with `[cancelled]`. The asking agent sees a terminal state
and can fall back.
- `ttl_seconds` deadline: a tokio watchdog spawned at submit time
fires `answer(id, "[expired]")` once the ttl runs out. Already-
resolved races no-op. The dashboard surfaces a `⏳ MM:SS` chip
on each pending question with a deadline.
## Helper events to the submitting agent ## Helper events to the submitting agent
@ -673,15 +637,14 @@ root agent. Variants (`hive_sh4re::manager::HelperEvent`):
The recipient responds via `Answer { id, answer }` and the The recipient responds via `Answer { id, answer }` and the
asker sees the matching `QuestionAnswered`. asker sees the matching `QuestionAnswered`.
The rest of the original lifecycle notices — `Rebuilt`, `Killed`, The remaining lower-urgency lifecycle notices — `Rebuilt`, `Killed`,
`Destroyed`, `NeedsLogin`, `LoggedIn`, `ConfigReady` — were pure "FYI, `Destroyed`, `NeedsLogin`, `LoggedIn`, `ConfigReady` — are "FYI, check
check when convenient" events with no reason to drive an immediate when convenient" events with no reason to drive an immediate turn, so
turn, so they've been migrated off `HelperEvent` onto they deliver via `push_todo`/`push_todo_submitter` (see above) instead
`push_todo`/`push_todo_submitter` (see above): `agent_todo_socket` of `HelperEvent`: an `agent_todo_socket` push instead of a broker
push instead of a broker message, `subsystem = "core"`, message, `subsystem = "core"`, `key = "<event>:<agent>"` for dedup,
`key = "<event>:<agent>"` for dedup, one free-text `summary` in place and a single free-text `summary` (`rebuilt_todo_summary` renders
of the old structured fields (`rebuilt_todo_summary` renders `Rebuilt`'s `ok`/`note`/`sha`/`tag` fields into that string).
`Rebuilt`'s former `ok`/`note`/`sha`/`tag` into that string).
Optional `sha` field on `ApprovalResolved` carries the canonical Optional `sha` field on `ApprovalResolved` carries the canonical
hive-c0re-vouched commit sha. Optional `tag` carries the deploy hive-c0re-vouched commit sha. Optional `tag` carries the deploy