hive-ag3nt + docs: extract harness binary shape prose (#716 batch 5)
`bin/hive.rs` carries ~15 attribution cookies (#598, #676, #692, #693, #778, #788, #682, #688) plus structural prose about the single-binary architecture (role-from-env), the `Surface` trait pattern (zero-sized type tags + generic dispatch), boot wiring (label fallback, plugin install → send_to_parent failure routing), and turn-outcome branching (ack / requeue / wait_for_login / parent notify). Migrated to a new `docs/turn-loop.md::Harness binary shape` subsection covering: - Single-binary rationale (server-side privilege boundary on the broker socket means no escalation risk in shipping both wire surfaces in the same process). - Three subcommands (`serve` / `mcp` / `wake`). - Surface trait + zero-sized type tags (AgentSurface, ManagerSurface) — picks per-role FLAVOR / FORGE_IS_MANAGER and exposes the broker wire ops behind trait methods so the turn loop is written once. - Boot wiring (HIVE_PORT, HIVE_LABEL fallback, plugin failures routed via send_to_parent, web_ui + forge_notify spawn). - Turn outcomes table (Ok/Compacted/RateLimited/AuthFailed/Failed) + continue-sentinel pickup. In-code rustdocs reduced to 1-line summaries + doc pointers; inline cookies stripped from boot wiring + plugin install + sentinel cleanup paths. 15 → 0 cookies in bin/hive.rs; cargo check -p hive-ag3nt passes.
This commit is contained in:
parent
2ce8bb5b77
commit
94c110fd5f
2 changed files with 114 additions and 53 deletions
|
|
@ -52,6 +52,86 @@ binary, not two) runs:
|
|||
7. Emit `LiveEvent::TurnEnd { ok, note }`. Sleep `poll_ms` to avoid
|
||||
tight loops on transient failures.
|
||||
|
||||
## Harness binary shape
|
||||
|
||||
One `hive` binary serves both roles. The split into
|
||||
`hive-ag3nt` + `hive-m1nd` was collapsed because the privilege
|
||||
boundary lives server-side at the broker socket
|
||||
(`/run/hive/mcp.sock`): an agent-flavor socket refuses
|
||||
`ManagerRequest` calls regardless of who sends them, so there's no
|
||||
escalation risk in shipping the same code to both. `main()` reads
|
||||
`$HIVE_ROLE` (set by `harness-base.nix` from `hyperhive.role`;
|
||||
defaults to `"agent"` for standalone `nix run` invocations) and
|
||||
dispatches.
|
||||
|
||||
Three subcommands:
|
||||
|
||||
- `serve` — long-running harness loop (the inbox poll +
|
||||
claude-pump + ack/requeue cycle described above).
|
||||
- `mcp` — stdio MCP server claude spawns via `--mcp-config` per
|
||||
turn. Same binary, different mode.
|
||||
- `wake --from <name> --body <body>` — push a message into our own
|
||||
inbox so the next turn fires with the given body. Used by
|
||||
co-process daemons (matrix bridge, scraper, webhook listeners)
|
||||
to nudge claude on external events. `--body -` reads from stdin.
|
||||
|
||||
### `Surface` trait + zero-sized type tags
|
||||
|
||||
`AgentRequest` / `AgentResponse` and `ManagerRequest` /
|
||||
`ManagerResponse` are wire-disjoint, but the turn loop itself
|
||||
(boot → recv → drive → ack/requeue → stats → continue-sentinel)
|
||||
is identical regardless of role. `bin/hive.rs` factors that
|
||||
sameness through a `Surface` trait with two zero-sized impls
|
||||
(`AgentSurface`, `ManagerSurface`) wrapping:
|
||||
|
||||
- Per-role MCP `Flavor` constant (picks which system-prompt block
|
||||
+ tool registration goes into the spawned claude).
|
||||
- Per-role `forge_notify::run` flag (picks `AgentRequest::Wake`
|
||||
vs `ManagerRequest::Wake` so the broker socket accepts the
|
||||
push).
|
||||
- One async method per wire op: `ack_turn`, `requeue_inflight`,
|
||||
`inbox_unread`, `post_turn_counts`, `send_to_parent`,
|
||||
`self_wake`, `recv_next`, `wake_external`.
|
||||
|
||||
`main()`'s dispatch picks `serve_main::<AgentSurface>` vs
|
||||
`serve_main::<ManagerSurface>` and the turn logic stays in
|
||||
lockstep by construction — there's no separate per-role copy of
|
||||
`serve_loop` / `handle_turn` / `wake`.
|
||||
|
||||
### Boot wiring
|
||||
|
||||
`serve_main` reads `HIVE_PORT` (default `DEFAULT_WEB_PORT`) +
|
||||
`HIVE_LABEL` (default `"hive"` for standalone runs; the meta
|
||||
flake sets it unconditionally for any container-deployed agent;
|
||||
see `docs/conventions.md::Hive identity` for the env stack),
|
||||
opens turn-stats sqlite, prepares the on-boot files (see below),
|
||||
installs claude plugins, spawns `forge_notify::run` + `web_ui::serve`,
|
||||
and either drops into `serve_loop` directly (`Online`) or parks on
|
||||
the login flow first (`NeedsLogin`).
|
||||
|
||||
Plugin install failures are not fatal: each entry comes back as a
|
||||
human-readable failure string that gets routed via
|
||||
`Surface::send_to_parent` to the agent's topology parent (the
|
||||
broker resolves `<parent>` per `topology::parent_of`; root agents
|
||||
and the manager fall through to operator).
|
||||
|
||||
### Turn outcomes
|
||||
|
||||
`turn::TurnOutcome` drives the post-claude branch:
|
||||
|
||||
| Outcome | Action |
|
||||
| --- | --- |
|
||||
| `Ok` / `Compacted` | `ack_turn` |
|
||||
| `RateLimited` | sleep `HIVE_RATE_LIMIT_SLEEP_SECS` (default 300), requeue inflight, status back to `online` |
|
||||
| `AuthFailed` | emit `needs_login_idle` sentinel, requeue inflight, park in `wait_for_login` |
|
||||
| `Failed(err)` | route `[system] \`<qualified-label>\` claude turn failed:\n<err>` to `<parent>` via `send_to_parent` |
|
||||
|
||||
After the outcome handler, the stats sink records a row and the
|
||||
`hyperhive-continue` sentinel (dropped by the `request_next_turn`
|
||||
MCP tool) is consumed if present, firing `self_wake` so the next
|
||||
turn starts with `{ from: "self", body: "continue" }` even if the
|
||||
inbox is empty.
|
||||
|
||||
## The claude invocation
|
||||
|
||||
```
|
||||
|
|
|
|||
Loading…
Reference in a new issue