Watch
0
0
Fork
You've already forked hyperhive
0

docs(agents): drop change-log section and temporal wording

This commit is contained in:
atlas 2026-10-02 09:32:08 +02:00 • committed by mara
commit 967bd34622
3 changed files with 73 additions and 118 deletions

View file

@ -2,15 +2,13 @@
<!-- vale write-good.Passive = NO -->
Agents are a **flat set**, with no parent/child tree: `topology.json`
carries no `parent` field, and the capability store — not tree
position — scopes which agents can manage which others.
Agents are a **flat set**: the capability store scopes which agents can
manage which others.
<!-- vale write-good.Passive = YES -->
This doc covers what the roster file holds today, the map-shaped
alternative `topology::all_agents` still accepts, and where the manager
still gets special-cased, as a tracked cleanup.
This doc covers what the roster file holds, the map-shaped alternative
`topology::all_agents` accepts, and what's hard-coded for the manager.
## Where the roster lives
@ -34,10 +32,9 @@ This page is about a different, **hive-local** file: the hive-c0re-owned
["alice", "bob", "ruth"]
```
One entry per agent _this hive_ currently has state/config for, in name
order. Unlike the swarm roster, nothing writes this file directly —
it's a derived cache, rebuilt by the reconcile pass below from what the
hive observes locally (config repos cloned, containers spawned), and it
One entry per agent _this hive_ has state/config for, in name order.
Unlike the swarm roster, it's a derived cache: the reconcile pass below
rebuilds it from what the hive observes locally (config repos cloned, containers spawned), and it
answers a narrower question than "does this agent exist": _which of
this hive's local agents the `ManageRootAgent` capability's bind-mounts
should cover._ `topology::all_agents` is the only reader that matters.
@ -45,14 +42,15 @@ should cover._ `topology::all_agents` is the only reader that matters.
That reader is a permission boundary: the set it returns is what an agent
holding the `ManageRootAgent` capability gets bind-mounted (each other
agent's `state` read-write and `config` read-only; never `harness`). An
agent holding no capability sees its own dirs and nothing else. See
agent holding no capability sees its own dirs and nothing else:
`ManageRootAgent` is the only grant that reaches another agent's state
dir. See
[`persistence.md`](persistence.md)'s _Cross-agent access to state._
### Reading the map-shaped format
`topology.json` may also be a map of `name → parent | null`; the reader
accepts that shape too and keeps its keys, so a hive with a file in that
shape still reads the same roster rather than an empty one. An empty
accepts that shape and takes its keys as the roster. An empty
roster costs more than a cosmetic gap: every capability holder loses its
mounts until the next reconcile pass writes the array form.
@ -78,40 +76,15 @@ from which agents exist, so the next pass overwrites a hand edit.
See `hive-c0re/src/agent_config/topology.rs` and `hive-c0re/src/meta.rs`'s
module docs for the exact call chain.
## What replaced the parent field
## Hard-coded manager behaviour
Recorded so a reader who finds one of these in an old branch, an issue
thread or a stale comment knows each one went away rather than moved:
| gone | what replaced it |
| -------------------------------------------- | --------------------------------------------------- |
| `<parent>` recipient sentinel | address `operator` directly |
| `<children>` fan-out recipient | nothing — name the recipients, or broadcast to `*` |
| `hivectl agent <name> set-parent` | nothing |
| `POST /api/topology/set-parent{,-bulk}` | nothing |
| `HostRequest::SetParent` | nothing |
| `NodeKind::Reparent` and its DAG template | nothing |
| `HIVE_PARENT` on the container | nothing — no consumer ever read it |
| every agent's grant over its direct children | the `ManageRootAgent` capability, for every agent |
| rebuild ordering by topology depth | alphabetical, which the depth sort already produced |
<!-- vale write-good.Passive = NO -->
The last row is the one with teeth: reaching a child's state dir
requires holding `ManageRootAgent`; parentage grants nothing. That
narrowing is the intended consequence of removing the field, not a
side effect of it.
<!-- vale write-good.Passive = YES -->
## Manager special-casing today
Capability enforcement isn't fully wired yet, so the
**manager (`ruth`) still gets some hard-coded special treatment**
other agents don't. A hive can opt out of having one at all
(`services.hyperhive.ruthless = true` skips `hive-c0re`'s root-agent
create/start sweep entirely, `hive-c0re/src/workers/auto_update.rs`);
everything below applies only when it doesn't:
The **manager (`ruth`)** differs from other agents in naming/bootstrap,
its socket flavour and its default capability grant; its prompt, tool
allow-list and state dirs work as for every other agent. A hive with
`services.hyperhive.ruthless = true` runs no manager: `hive-c0re` skips
its root-agent create/start sweep
(`hive-c0re/src/workers/auto_update.rs`), and this section doesn't
apply.
<!-- vale write-good.Passive = NO -->
@ -121,44 +94,39 @@ everything below applies only when it doesn't:
approval step — every other agent is created at swarm level
(`swarmctl agent create`).
Roster-wise, `ruth` is just another entry.
- **Wire-protocol** — the only `*(privileged)*` `Request` variants left
in `hive-core-agent-sock`'s unified enum are the scheduling ops
- **Wire-protocol** — the `*(privileged)*` `Request` variants in
`hive-core-agent-sock`'s unified enum are the scheduling ops
(`RequestSchedulePrompt`, `CancelSchedule`, `FireScheduleNow`,
`EditSchedule`), reachable only from the manager's socket flavour
today, matching the `scheduling` tool group
`EditSchedule`), reachable only from the manager's socket flavour,
matching the `scheduling` tool group
([`docs/turn-loop/mcp.md`](../turn-loop/mcp.md)). Container lifecycle
ops (kill/start/restart/rebuild) never lived on this socket — they go
through the separate host-admin socket `hivectl` speaks. `Wake`
(inject a `from: <X>` message straight into the caller's own inbox)
is on this socket too but isn't privileged to either flavour; no
built-in in-container producer calls it today — matrix, bash and
forge notifications push a todo on the harness's
in-agent socket instead (see
ops (kill/start/restart/rebuild) go through the separate host-admin
socket `hivectl` speaks. `Wake` (inject a `from: <X>` message straight
into the caller's own inbox) is on this socket too, unprivileged on
both flavours; matrix, bash and forge don't call it — their
notifications push a todo on the harness's in-agent socket (see
[`docs/turn-loop/mcp.md`](../turn-loop/mcp.md#waking-the-agent-from-inside-the-container)).
- **Storage/mounts** — only the manager container gets
- **Storage/mounts** — the manager container gets
`/var/lib/hyperhive/agents` bind-mounted RW at `/agents` (so it can
manage any agent's state dir — config isn't authored there, since a
real config change is a PR from a clone), plus RO mounts for
`/applied` (diff against what's deployed) and `/meta` (system-wide
deploy log). That grant is the `ManageRootAgent` capability, which
ruth holds; nothing checks the agent's name. hive-c0re will
gate RO `/meta` access on a "meta read" capability; no agent-facing
path writes `flake.lock` — the operator dashboard's `POST
/api/meta-update` is the only entry point.
- **Prompt** — treated identically: `prompt::render` always filters for `agent`. `prompts/system.md`
still carries `<!-- role:agent -->` / `<!-- role:manager -->` marker
blocks, but `prompt::render` filters for `"agent"` unconditionally for
every container, manager included ("always `agent` role — there is
only one role", `hive-agent/src/prompt.rs`'s own module doc). The
`role:manager` blocks are dead in production, exercised only by a
unit test (`filter_role_blocks(SAMPLE, "manager")`).
- **Tool allow-list** — also not a flavour switch: the MCP tools claude
sees come from `HIVE_TOOL_GROUPS` alone, the same mechanism for every
agent ([`docs/turn-loop/mcp.md`](../turn-loop/mcp.md)). Ruth's wider
default surface is just a wider default grant
(`ToolGroup::MANAGER_DEFAULT`, seeded by `auto_update.rs` whenever its
groups aren't already set), not anything keyed off its name or
container.
deploy log). That grant is the `ManageRootAgent` capability: ruth
holds it by default, and any agent holding it gets the same mounts.
The operator dashboard's `POST /api/meta-update` is the only path
that writes `flake.lock`.
- **Prompt** — identical for every container: `prompts/system.md`
carries `<!-- role:agent -->` / `<!-- role:manager -->` marker
blocks, and `prompt::render` filters for `"agent"` unconditionally,
manager included ("always `agent` role — there is only one role",
`hive-agent/src/prompt.rs`'s own module doc). The `role:manager`
blocks are dead in production, exercised only by a unit test
(`filter_role_blocks(SAMPLE, "manager")`).
- **Tool allow-list** — the MCP tools claude sees come from
`HIVE_TOOL_GROUPS` alone, the same mechanism for every agent
([`docs/turn-loop/mcp.md`](../turn-loop/mcp.md)). Ruth's wider default
surface is a wider default grant (`ToolGroup::MANAGER_DEFAULT`, seeded
by `auto_update.rs` whenever its groups aren't already set).
- **State dirs** — _not_ special-cased: `HYPERHIVE_STATE_DIR` is
injected uniformly via `systemd.globalEnvironment` for every
container including the manager, so all token/state paths resolve