docs(agents): drop change-log section and temporal wording
This commit is contained in:
parent
78aacf13ce
commit
967bd34622
3 changed files with 73 additions and 118 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue