docs(conventions): fix stale internal refs verified against source

- MAX_AGENT_NAME lives in lifecycle/mod.rs, not bare lifecycle.rs
- agent_server::handle_send -> socket_server::handle_send (no agent_server
  module exists)
- AgentMeta response is missing matrix_accounts in the field list
- tool-group table missing list_containers/ack_until (added since written)
  and the forge/web_tools groups entirely
- #[tool] fns live on AgentServer in hive-agent-mcp/src/mcp/mod.rs, not a
  HiveServer in a nonexistent hive-ag3nt/src/mcp.rs; ToolGroup::tools()
  lives in hive-sh4re/src/permissions.rs, not lib.rs
- Capability + as_str also moved to permissions.rs; capability-check
  handler list was stale (agent_server.rs/mcp.rs don't exist)
- async-forms listener is bindAsyncForms in frontend/packages/shared,
  not assets/tabs.js + assets/app.js
- 'rebuild is the reconcile verb' described a monolithic lifecycle::rebuild
  that no longer exists — it's job_queue::templates::rebuild building a DAG,
  and flake.nix is no longer regenerated host-side on rebuild at all
This commit is contained in:
iris 2026-08-15 12:27:08 +02:00 committed by mara
commit c870e230bf

View file

@ -8,7 +8,7 @@ exist because something already went wrong without them.
- Containers are length-bounded by `nixos-container` (≤ 11 chars).
- Sub-agents are `h-<name>` with `<name>` ≤ 9 chars.
- One agent is the bootstrap/root container, with a fixed name (`ruth` today).
- `MAX_AGENT_NAME` in `lifecycle.rs` enforces the cap.
- `MAX_AGENT_NAME` in `hive-c0re/src/lifecycle/mod.rs` enforces the cap.
- Per-agent web UI port = `WEB_PORT_BASE + FNV1a(name) % WEB_PORT_RANGE`
(8100..8999) for every agent; dashboard
`cfg.dashboardPort` (default 7000).
@ -83,7 +83,7 @@ name validation rejects any character outside `[a-z0-9_-]`, so the
angle-bracket and asterisk shapes below are structurally safe.
- `*` — broadcast: deliver to every running agent except the sender
(`agent_server::handle_send` fans out via `Coordinator::broadcast_send`).
(`socket_server::handle_send` fans out via `Coordinator::broadcast_send`).
- `operator` — the human at the dashboard. Messages accumulate in the
inbox view; no agent ever `recv`'s them.
- `<parent>` — the sender's parent per `topology.json`. Rewritten at
@ -93,7 +93,7 @@ angle-bracket and asterisk shapes below are structurally safe.
their parent without learning the label, so runtime reparenting
propagates with zero agent-side restart.
- `<children>` — fan-out to every direct descendant of the sender per
`topology.json`. Resolved in `agent_server::handle_send` via
`topology.json`. Resolved in `socket_server::handle_send` via
`topology::children_of(sender)`: one message is delivered to each
child, bypassing the allow-list check (structural fan-out targets are
never user-listed peers). No-op for leaf agents (returns `Ok` when the
@ -279,7 +279,7 @@ an agent. Self-introspection when `name = None` (replaces the older
`Whoami` request); target query when `name = Some`.
Response is `AgentMeta { name, running, hyperhive_rev,
status_text, status_set_at, hive_name, swarm_name }`:
status_text, status_set_at, hive_name, swarm_name, matrix_accounts }`:
- `hyperhive_rev`: `None` only when the configured flake URL has
no canonical path. Otherwise carries the rev the target is
@ -299,6 +299,9 @@ status_text, status_set_at, hive_name, swarm_name }`:
`HYPERHIVE_HIVE_NAME` / `HYPERHIVE_SWARM_NAME` env (sourced from
`services.hyperhive.hiveName` / `services.hyperhive.swarm.name`).
Both `None` when the options aren't configured.
- `matrix_accounts`: one `MatrixIdentity` per configured + live matrix
account the agent can act as. Empty for agents with no matrix
provisioning.
### Timestamps on the wire
@ -323,14 +326,16 @@ binary flavor.
| Group | Tools |
|---|---|
| `messaging` | `send`, `recv`, `ask`, `answer` |
| `messaging` | `send`, `recv`, `ack_until`, `ask`, `answer` |
| `meta` | `get_agent_meta` (`set_status` is always-on, see below) |
| `inbox` | `get_loose_ends`, `cancel_loose_end`, `remind` |
| `execution` | vestigial — `mcp__bash__run` / `mcp__bash__status` are always available unconditionally via `extraMcpServers`; this group's entries expand to non-existent `mcp__hyperhive__run` / `mcp__hyperhive__status` and have no effect. See `docs/tools/bash.md`. |
| `lifecycle` | `kill`, `start`, `restart`, `update` *(privileged)* |
| `lifecycle` | `kill`, `start`, `restart`, `update`, `list_containers` *(privileged)* |
| `approvals` | `request_init_config`, `request_update_meta_inputs` *(privileged)* |
| `scheduling` | `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`, `edit_schedule`, `list_schedules` *(privileged)* |
| `diagnostics` | `get_logs` *(privileged)* |
| `forge` | `create_repo` — create git repos through hive-c0re (operator-gated merge) |
| `web_tools` | none (gates the Claude built-ins `WebFetch`/`WebSearch`, not an MCP tool) |
**Always-on tools** — `set_status`, `compact`, and `mark_todos_done` are
exposed to every agent regardless of which groups it holds
@ -363,10 +368,10 @@ from `tool-groups.json`). Unrecognised tokens are logged and skipped. Falls back
to `ToolGroup::AGENT_DEFAULT` (`messaging`, `meta`, `inbox`, `execution`) when
the var is absent or empty.
**Updating the surface** — when a new `#[tool]` fn is added to `HiveServer`
in `hive-ag3nt/src/mcp.rs`, add its name to the matching `ToolGroup::tools()`
slice in `hive-sh4re/src/lib.rs`. That's the single source of truth;
`mcp_config::allowed_mcp_tools` (in `hive-ag3nt/src/mcp_config.rs`) reads it at
**Updating the surface** — when a new `#[tool]` fn is added to `AgentServer`
in `hive-agent-mcp/src/mcp/mod.rs`, add its name to the matching `ToolGroup::tools()`
slice in `hive-sh4re/src/permissions.rs`. That's the single source of truth;
`mcp_config::allowed_mcp_tools` (in `hive-agent/src/mcp_config.rs`) reads it at
session start.
## Capabilities
@ -406,15 +411,18 @@ groups: an agent that could grant its own capabilities via a config commit would
bypass the operator approval gate.
**Adding a new capability** — add a variant to `Capability` in
`hive-sh4re/src/lib.rs` + an arm to `as_str`. Add it to `Capability::ALL` (the
source of truth for the permissions UI columns). Implement the access check in
the relevant handler (`agent_server.rs`, `mcp.rs`, or a handler under
`hive-c0re/src/dashboard/`).
`hive-sh4re/src/permissions.rs` + an arm to `as_str`. Add it to `Capability::ALL`
(the source of truth for the permissions UI columns). Implement the access
check in the relevant handler (`hive-c0re/src/socket_server/mod.rs`,
`hive-c0re/src/socket_server/lifecycle_handlers.rs`, `coordinator.rs`, or a
handler under `hive-c0re/src/dashboard/`).
## Async forms
Dashboard + per-agent mutating forms carry `data-async`; a delegated
`submit` listener in `assets/tabs.js` (+ `assets/app.js` for the per-agent UI) intercepts, shows a spinner,
Dashboard + per-agent mutating forms carry `data-async`; the shared
`bindAsyncForms` `submit` listener (`frontend/packages/shared/src/forms.js`,
imported as `@hive/shared/forms.js` and wired up from `tabs.js` on the
dashboard and `app.js` on the per-agent UI) intercepts, shows a spinner,
POSTs `application/x-www-form-urlencoded` (axum's `Form` extractor
rejects multipart), calls `refreshState()` on success. New mutating
forms should add `data-async` and optionally `data-confirm` (for a
@ -429,11 +437,16 @@ via `snapshotOpenDetails` / `restoreOpenDetails`.
## `rebuild` is the reconcile verb
`lifecycle::rebuild` idempotently rewrites
`/etc/nixos-containers/<C>.conf` (`PRIVATE_NETWORK=0`, clears
`HOST_ADDRESS` / `LOCAL_ADDRESS`, sets `EXTRA_NSPAWN_FLAGS`),
regenerates `applied/<name>/flake.nix`, writes the systemd limits
drop-in, then `nixos-container update` + stop + start.
`job_queue::templates::rebuild` builds the DAG that reconciles a
container to its wanted state: `write_dropins` (the nspawn-conf
rewrite — `PRIVATE_NETWORK=0`, clears `HOST_ADDRESS` / `LOCAL_ADDRESS`,
sets `EXTRA_NSPAWN_FLAGS` — plus the systemd resource-limits drop-in)
is folded into the `Swap` node, then `nixos-container update` + stop +
start runs across the `StopForUpdate → Swap → RebuildBookkeeping`
brace and the tail `Reconcile` node. `flake.nix` itself is no longer
regenerated host-side on rebuild — it's tracked in the agent's
proposed/applied repos and rides along on every fetch (see
`docs/approvals.md::Two repos per agent`).
Anything that changes per-container state on the host should be
re-applied here so a manual `↻ R3BU1LD` from the dashboard is