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

View file

@ -44,7 +44,7 @@ its leftmost label by convention, but the convention isn't
machine-readable, and federated hives at different DNS domains machine-readable, and federated hives at different DNS domains
can share a swarm name. Humans want both: the address can share a swarm name. Humans want both: the address
(`@darkest.space`) AND the prose name (`pr1ma`). Matrix MXIDs (`@darkest.space`) AND the prose name (`pr1ma`). Matrix MXIDs
still use the domain-based convention untouched. use the domain-based convention.
`qualify(label)` is the same shape as `qualified_label()` but `qualify(label)` is the same shape as `qualified_label()` but
applies to an arbitrary label the caller already has (for example a peer applies to an arbitrary label the caller already has (for example a peer
@ -65,11 +65,10 @@ wake-event-injection surface on the host-served per-agent socket.
Recipient is implicit — the agent the socket belongs to — and `from` Recipient is implicit — the agent the socket belongs to — and `from`
is caller-chosen so the wake prompt can label the source verbatim is caller-chosen so the wake prompt can label the source verbatim
(`"matrix: new message in #general"`, `"forge: PR #42 opened"`, etc.). <!-- lint:allow: example strings, not real tags --> (`"matrix: new message in #general"`, `"forge: PR #42 opened"`, etc.). <!-- lint:allow: example strings, not real tags -->
`hive-c0re` still handles it, but no built-in `hive-c0re` handles it; matrix, bash and forge don't call it — their
in-container producer calls it today — matrix, bash and forge notifications upsert a todo on the harness's in-agent socket
notifications all upsert a todo on the harness's in-agent socket (`HIVE_AGENT_SOCKET`), which signals the same turn loop without a
(`HIVE_AGENT_SOCKET`) instead, which signals the same turn loop without hive-c0re round-trip. See
a hive-c0re round-trip. See
[`docs/turn-loop/mcp.md` § Waking the agent from inside the [`docs/turn-loop/mcp.md` § Waking the agent from inside the
container](../turn-loop/mcp.md#waking-the-agent-from-inside-the-container). container](../turn-loop/mcp.md#waking-the-agent-from-inside-the-container).
@ -91,11 +90,7 @@ angle-bracket and asterisk shapes below are structurally safe.
- `operator` — the human at the dashboard. Messages accumulate in the - `operator` — the human at the dashboard. Messages accumulate in the
inbox view; no agent ever `recv`'s them. inbox view; no agent ever `recv`'s them.
`<parent>` and `<children>` aren't valid recipients: address The broker stores the recipient exactly as the agent passed it.
`operator` directly instead of `<parent>`, and name the recipients (or
broadcast to `*`) instead of `<children>`. Nothing rewrites a
recipient at send time — what an agent passes is what the broker
stores.
## Wire protocol ## Wire protocol
@ -237,10 +232,8 @@ status_text, status_set_at, hive_name, swarm_name, matrix_accounts }`:
`false`, the host clears `status_text` / `status_set_at` — `false`, the host clears `status_text` / `status_set_at` —
on-disk values from before the stop are stale snapshots, not on-disk values from before the stop are stale snapshots, not
live status. Defaults to `true` on the live status. Defaults to `true` on the
wire: the deserializer treats a payload lacking the field — from a wire: the deserializer treats a payload without the field as
harness that never serialises it, or a host that only knows how to running.
ask about live containers — as running, keeping compatibility with
pre-running-field payloads.
- `status_text` / `status_set_at`: last value written via - `status_text` / `status_set_at`: last value written via
`SetStatus`, plus its unix timestamp. Both `None` when the `SetStatus`, plus its unix timestamp. Both `None` when the
target has never set a status, when the agent name is unknown, target has never set a status, when the agent name is unknown,
@ -302,10 +295,10 @@ binary flavor.
| `meta` | `get_agent_meta` (`set_status` is always-on, see below) | | `meta` | `get_agent_meta` (`set_status` is always-on, see below) |
| `inbox` | `get_loose_ends`, `cancel_loose_end`, `remind` | | `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`. | | `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` | none — `list_containers` isn't a tool; the variant survives only so existing grants parse. | | `lifecycle` | none |
| `approvals` | none — `request_update_meta_inputs` isn't a tool. Still a live server-side gate: `cancel_loose_end`'s approval-cancel arm requires it. | | `approvals` | none — gates `cancel_loose_end`'s approval-cancel arm server-side. |
| `scheduling` | `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`, `edit_schedule`, `list_schedules` *(privileged)* | | `scheduling` | `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`, `edit_schedule`, `list_schedules` *(privileged)* |
| `forge` | none — `create_repo` isn't a tool; the variant survives only so existing grants parse. | | `forge` | none |
| `web_tools` | none (gates the Claude built-ins `WebFetch`/`WebSearch`, not an MCP tool) | | `web_tools` | none (gates the Claude built-ins `WebFetch`/`WebSearch`, not an MCP tool) |
**Always-on tools** — `ToolGroup::ALWAYS_ON_TOOLS` exposes `set_status`, **Always-on tools** — `ToolGroup::ALWAYS_ON_TOOLS` exposes `set_status`,
@ -411,8 +404,7 @@ rewrite — `PRIVATE_NETWORK=1`, `HOST_ADDRESS` = the bridge gateway IP,
sets `EXTRA_NSPAWN_FLAGS` — plus the systemd resource-limits drop-in) sets `EXTRA_NSPAWN_FLAGS` — plus the systemd resource-limits drop-in)
into the `Swap` node, then runs `nixos-container update` + stop + into the `Swap` node, then runs `nixos-container update` + stop +
start across the `StopForUpdate → Swap → RebuildBookkeeping` start across the `StopForUpdate → Swap → RebuildBookkeeping`
brace and the tail `Reconcile` node. `flake.nix` itself isn't brace and the tail `Reconcile` node. `flake.nix` itself lives in the agent's
regenerated host-side on rebuild — it's tracked in the agent's
proposed/applied repos and rides along on every fetch (see proposed/applied repos and rides along on every fetch (see
`docs/agent-lifecycle/approvals.md::Two repos per agent`). `docs/agent-lifecycle/approvals.md::Two repos per agent`).

View file

@ -14,7 +14,7 @@ entry (`type = "stdio" | "http"`, default `"stdio"`): `matrix` and
(`hive-matrix-daemon`, `hive-subagent-daemon`), same reasoning as the (`hive-matrix-daemon`, `hive-subagent-daemon`), same reasoning as the
built-in surface and `bash`'s `hive-bash-daemon`. The GUI bridge built-in surface and `bash`'s `hive-bash-daemon`. The GUI bridge
(`screen`, gated on `services.hyperhive.agent.gui.enable`) is the one (`screen`, gated on `services.hyperhive.agent.gui.enable`) is the one
entry that still takes the schema's stdio default — it spawns entry on the schema's stdio default — it spawns
`hive-screen-mcp` fresh each turn. The server name is `hyperhive`, so `hive-screen-mcp` fresh each turn. The server name is `hyperhive`, so
the tools land in claude as the tools land in claude as
`mcp__hyperhive__<tool>`. Each entry also has an `availableToSubagents` `mcp__hyperhive__<tool>`. Each entry also has an `availableToSubagents`
@ -62,8 +62,8 @@ are opt-in via the P3RM1SS10NS tab.
**System messages** (from sender `system`): the broker delivers **System messages** (from sender `system`): the broker delivers
`hive_sh4re::manager::HelperEvent` (three variants: `ApprovalResolved`, `hive_sh4re::manager::HelperEvent` (three variants: `ApprovalResolved`,
`ContainerCrash`, `NeedsUpdate` — the last declared but constructed by `ContainerCrash`, `NeedsUpdate` — declared, but no call site
no call site today) as regular inbox messages (same `recv` constructs it) as regular inbox messages (same `recv`
path; body is a JSON object with an `event` discriminant field). path; body is a JSON object with an `event` discriminant field).
`ApprovalResolved` goes to whichever agent actually submitted the `ApprovalResolved` goes to whichever agent actually submitted the
approval (`Coordinator::notify_submitter`, looked up from the approval (`Coordinator::notify_submitter`, looked up from the
@ -73,7 +73,7 @@ recorded submitter falls back to the manager, `ruth`).
hardcoded — `hive-c0re/src/workers/crash_watch.rs`). A `MergeConfigPr` hardcoded — `hive-c0re/src/workers/crash_watch.rs`). A `MergeConfigPr`
approval's rebuild additionally pushes a `rebuilt:<agent>` todo to that approval's rebuild additionally pushes a `rebuilt:<agent>` todo to that
same submitter (`Coordinator::push_todo_submitter`, `subsystem = same submitter (`Coordinator::push_todo_submitter`, `subsystem =
"core"`), which still wakes a turn (the todo-wake path — see [Turn "core"`), which wakes a turn (the todo-wake path — see [Turn
outcomes](README.md#turn-outcomes)) via a generic "call outcomes](README.md#turn-outcomes)) via a generic "call
`get_loose_ends`" prompt rather than the event body itself. Lifecycle `get_loose_ends`" prompt rather than the event body itself. Lifecycle
transitions the job-queue scheduler or crash watcher drive directly — transitions the job-queue scheduler or crash watcher drive directly —
@ -91,8 +91,7 @@ at_unix_timestamp?)`.
bash, forge, or any user-configured MCP server), an undelivered-inbox bash, forge, or any user-configured MCP server), an undelivered-inbox
count, and unread matrix notifications; `approval` rows only appear count, and unread matrix notifications; `approval` rows only appear
when the caller is the manager (`ruth`) — sub-agents don't submit when the caller is the manager (`ruth`) — sub-agents don't submit
approvals. Always the caller's own rows — there is no way to target approvals. Always the caller's own rows. Full per-variant wire shape in
another agent. Full per-variant wire shape in
[`docs/process/conventions.md` § Loose-ends wire [`docs/process/conventions.md` § Loose-ends wire
shape](../process/conventions.md#loose-ends-wire-shape). shape](../process/conventions.md#loose-ends-wire-shape).
- `cancel_loose_end` — hard-delete a `reminder`, cancel a pending - `cancel_loose_end` — hard-delete a `reminder`, cancel a pending
@ -138,8 +137,7 @@ hive_name?, swarm_name?, matrix_accounts? }`. `matrix_accounts` is a
broker — see `hive-agent/src/todo_server.rs`. broker — see `hive-agent/src/todo_server.rs`.
- `mark_todos_done(ids)` — bulk-clear specific loose-ends-v2 todo rows - `mark_todos_done(ids)` — bulk-clear specific loose-ends-v2 todo rows
by id in one call, instead of `cancel_loose_end`ing each one. by id in one call, instead of `cancel_loose_end`ing each one.
List-based, not range-based (no `ack_until`-style "clear below id List-based: pass the ids `get_loose_ends` actually showed you reviewed;
N"): pass the ids `get_loose_ends` actually showed you reviewed;
unknown or already-done ids are silently skipped. Dials the same unknown or already-done ids are silently skipped. Dials the same
in-agent todo socket as `cancel_loose_end`'s `todo` kind. in-agent todo socket as `cancel_loose_end`'s `todo` kind.
@ -149,16 +147,16 @@ hive_name?, swarm_name?, matrix_accounts? }`. `matrix_accounts` is a
[`docs/tools/bash.md`](../tools/bash.md). [`docs/tools/bash.md`](../tools/bash.md).
- **Subagent spawning** — nested sessions on the agent's own runtime - **Subagent spawning** — nested sessions on the agent's own runtime
(claude or ACP, via `hive-runtime`) as background tasks, shipped (claude or ACP, via `hive-runtime`) as background tasks, shipped
default-on like bash execution (no tool group gates it yet). See default-on like bash execution; no tool group gates it. See
[`docs/tools/subagent.md`](../tools/subagent.md). [`docs/tools/subagent.md`](../tools/subagent.md).
- **Lifecycle + config** (`lifecycle`, `approvals`) — neither group - **Lifecycle + config** (`lifecycle`, `approvals`) — `approvals`
carries an MCP tool. `approvals` survives as a server-side gate on gates `cancel_loose_end`'s approval-cancel arm server-side;
`cancel_loose_end`'s approval-cancel arm; `lifecycle` gates nothing. `lifecycle` gates nothing.
Config changes go through a forge PR on `agent-configs/<name>` — see Config changes go through a forge PR on `agent-configs/<name>` — see
[`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md). [`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md).
- **Scheduling** (`scheduling`) — scheduled prompts. See - **Scheduling** (`scheduling`) — scheduled prompts. See
[`docs/tools/scheduling.md`](../tools/scheduling.md). [`docs/tools/scheduling.md`](../tools/scheduling.md).
- **Forge repos** (`forge`) — carries no MCP tool; `forge` gates nothing. - **Forge repos** (`forge`) — gates nothing.
- **Web egress** (`web_tools`) — enables Claude's built-in `WebFetch` - **Web egress** (`web_tools`) — enables Claude's built-in `WebFetch`
and `WebSearch` tools (not MCP tools; added directly to the and `WebSearch` tools (not MCP tools; added directly to the
`--allowedTools` list). Off by default; add the group in the `--allowedTools` list). Off by default; add the group in the
@ -177,20 +175,17 @@ The built-in producers (matrix, bash, forge) upsert a todo on the
harness's in-agent socket (`HIVE_AGENT_SOCKET`, `UpsertTodo` — see harness's in-agent socket (`HIVE_AGENT_SOCKET`, `UpsertTodo` — see
[`docs/tools/bash.md` § Completion as a todo [`docs/tools/bash.md` § Completion as a todo
(loose-ends v2)](../tools/bash.md#completion-as-a-todo-loose-ends-v2) (loose-ends v2)](../tools/bash.md#completion-as-a-todo-loose-ends-v2)
for the full upsert/signal/clear mechanism); none dials a direct for the full upsert/signal/clear mechanism): a new or changed summary
wake: a new or changed summary
makes the harness signal its own turn loop, with no hive-c0re makes the harness signal its own turn loop, with no hive-c0re
round-trip. An external MCP server (or any other in-container process) round-trip. An external MCP server (or any other in-container process)
can push its own todo the same way — `subsystem` is a plain string, can push its own todo the same way — `subsystem` is any string.
not a closed set.
The host-served per-agent socket at `/run/hive/mcp.sock` still carries The host-served per-agent socket at `/run/hive/mcp.sock` carries a
a lower-level `Wake` request (`hive_core_agent_sock::Request::Wake { lower-level `Wake` request (`hive_core_agent_sock::Request::Wake {
from, body }` — JSON-line, `{"cmd":"wake","from":"matrix","body":"new dm from, body }` — JSON-line, `{"cmd":"wake","from":"matrix","body":"new dm
from @alice"}\n`) that drops the body straight into the broker inbox as from @alice"}\n`) that drops the body straight into the broker inbox as
`{from:<label>, to:<agent>, body}`, bypassing the todo layer entirely. `{from:<label>, to:<agent>, body}`, bypassing the todo layer entirely.
`hive-c0re` still handles it, but no built-in producer `hive-c0re` handles it; matrix, bash and forge don't call it. Identity = socket: anything that can connect to
calls it today. Identity = socket: anything that can connect to
`/run/hive/mcp.sock` is implicitly trusted to inject one — the `/run/hive/mcp.sock` is implicitly trusted to inject one — the
bind-mount is the agent's own container only. bind-mount is the agent's own container only.