docs: refresh CLAUDE.md for system-prompt-file, helper events, dashboard buttons, ui shape; TODO.md drop operator-inbox (done)

This commit is contained in:
müde 2026-05-15 18:25:14 +02:00
parent ace13cd785
commit abfd2cce4b
2 changed files with 100 additions and 24 deletions

121
CLAUDE.md
View file

@ -35,7 +35,11 @@ hive-ag3nt/ in-container harness crate; produces TWO binaries
src/login_session.rs drives `claude auth login` over stdio pipes
src/bin/hive-ag3nt.rs sub-agent main
src/bin/hive-m1nd.rs manager main
assets/ CSS + JS for the per-agent UI
assets/ index.html, agent.css, app.js (include_str!)
prompts/ static role/tools/settings for claude (include_str!):
agent.md — sub-agent system prompt
manager.md — manager system prompt
claude-settings.json — --settings JSON
hive-sh4re/ wire types (HostRequest/Response, AgentRequest/Response,
ManagerRequest/Response, Message, Approval, HelperEvent)
@ -71,11 +75,11 @@ nix/
- **Actions are factored.** `approve` / `deny` / `destroy` live in
`actions.rs`; the admin socket and the dashboard POST handlers both call
into them so the two surfaces never drift.
- **Async forms.** Dashboard mutating forms carry `data-async`; the
`assets/async_forms.js` helper intercepts submit, shows a spinner, and
fetches with `application/x-www-form-urlencoded` (axum `Form` extractor
rejects multipart). New mutating forms should add `data-async` and
optionally `data-confirm`.
- **Async forms.** Dashboard + per-agent mutating forms carry `data-async`;
a delegated `submit` listener in `assets/app.js` intercepts, shows a
spinner, POSTs with `application/x-www-form-urlencoded` (axum `Form`
extractor rejects multipart), calls `refreshState()` on success. New
mutating forms should add `data-async` and optionally `data-confirm`.
## Gotchas / lessons learned
@ -167,17 +171,27 @@ Manager additionally:
config change for any agent (including `hm1nd` for self-mods).
The shared per-turn plumbing lives in `hive_ag3nt::turn::{write_mcp_config,
run_turn, drive_turn, emit_turn_end, wait_for_login}` so the two binaries
can't drift.
write_settings, write_system_prompt, run_turn, drive_turn, emit_turn_end,
wait_for_login}` so the two binaries can't drift.
On harness boot, three files get dropped next to the mcp socket at
`/run/hive/`:
- `claude-mcp-config.json` — re-invokes the running binary as `mcp` child.
- `claude-settings.json``--settings` blob (auto-compact/auto-memory
off, effortLevel medium).
- `claude-system-prompt.md` — rendered from `prompts/{agent,manager}.md`
with `{label}` substituted. Passed via `--system-prompt-file`.
Each turn:
```
claude --print --verbose --output-format stream-json --model haiku \
--continue --settings '{"autoCompactEnabled":false,"autoMemoryEnabled":false}' \
--mcp-config <path> --strict-mcp-config \
--continue --settings /run/hive/claude-settings.json \
--system-prompt-file /run/hive/claude-system-prompt.md \
--mcp-config /run/hive/claude-mcp-config.json --strict-mcp-config \
--tools <builtins> --allowedTools <builtins+mcp>
# prompt piped over stdin
# wake prompt piped over stdin — minimal, just from/body + optional unread hint
```
`--continue` keeps a persistent session per agent (claude stores sessions in
@ -187,14 +201,15 @@ overflow, retry once).
**Loop control.** The harness pops one inbox message per cycle (the wake
signal — Recv long-polls server-side for up to 30s waking instantly on a new
broker `Sent` event for this agent) and hands claude a prompt naming the
agent, the sender, the body, and the MCP tools. Claude drives any further
`recv`/`send` itself.
broker `Sent` event for this agent), peeks the remaining inbox depth with
`Status`, and emits `TurnStart { from, body, unread }`. The wake prompt
piped to claude includes a one-line `({unread} more pending — drain via …)`
hint when `unread > 0`. Claude drives any further `recv`/`send` itself.
**Tool envelope** (`mcp::run_tool_envelope`): every MCP tool handler logs
the request, runs the body, appends a status line (e.g.
`[status] 3 unread message(s) in inbox` from a non-mutating `Status` peek),
logs the result. New tools call this helper.
the request, runs the body, logs the result. Pre-/post-log only; the old
`[status] N unread message(s)` appendage was removed once unread moved
into the wake prompt + UI header. New tools call this helper.
**Tool whitelist** (`mcp::ALLOWED_BUILTIN_TOOLS`):
- Allowed built-ins: `Bash`, `Edit`, `Glob`, `Grep`, `Read`, `TodoWrite`,
@ -207,9 +222,27 @@ logs the result. New tools call this helper.
**Live view.** Each agent runs an `events::Bus` (a
`tokio::sync::broadcast<LiveEvent>` wrapper). The harness emits
`TurnStart`, `Stream(value)` (one per parsed stream-json line), `Note`,
`TurnEnd`. The web UI subscribes via `/events/stream` (SSE) and a JS panel
appends rows. No full-page reload — operator input stays put.
`TurnStart { from, body, unread }`, `Stream(value)` (one per parsed
stream-json line), `Note`, `TurnEnd { ok, note }`. The web UI subscribes
via `/events/stream` (SSE) and a JS panel (terminal-themed: Crust bg, inset
shadow, monospace) renders rows:
- `TurnStart``◆ TURN ← <from> · N unread` header + indented body.
- `Stream` `tool_use``→ Read /path` / `→ Bash $ cmd` /
`→ send → operator: "..."` etc., per-tool pretty rather than raw JSON.
- `Stream` `tool_result` short → flat `← ...`; long → collapsed
`<details>` `▸ ← Nl · headline` (click to expand full body).
- `Stream` `thinking` → shows the thinking text if claude provided one,
otherwise the bare `· thinking …` indicator.
- `Stream` `system init`, `result`, `rate_limit_event` are dropped — too
noisy and `TurnEnd` already says the turn finished.
- `Note``· text`.
- `TurnEnd``✓ turn ok` / `✗ turn fail — note` and triggers a
`refreshState()` so the page form view reflects state transitions
(e.g. login just landed).
The operator send form sits below the live panel, so the tail is what
you read first.
## Manager (hm1nd) is hive-c0re-managed
@ -237,6 +270,36 @@ Differences from sub-agents:
block from your host NixOS config. hyperhive creates and updates the
manager itself now.
**Manager policy** (from `prompts/manager.md`): the manager does NOT
rubber-stamp sub-agent config requests. It verifies (role match, package
legitimacy, cheaper alternative, blast radius) before committing +
calling `request_apply_commit`. For ambiguous cases or anything that
needs human signal, the manager forwards the question to the operator
via `send(to: "operator", ...)` — a dedicated `mcp__hyperhive__ask_operator`
tool with proper pause/resume semantics is in [TODO.md](TODO.md).
## Helper events to the manager
`Coordinator::notify_manager(&HelperEvent)` enqueues an inbox message
from sender `system` with the event JSON in the body. The manager
harness no longer short-circuits these — they drive a regular claude
turn so the manager can react. Variants
(`hive_sh4re::HelperEvent`):
- `ApprovalResolved { id, agent, commit_ref, status, note }` — fired by
`actions::approve` + `actions::deny` whenever an approval transitions
to its terminal state.
- `Spawned { agent, ok, note }``actions::approve` (Spawn-kind) +
admin `HostRequest::Spawn`.
- `Rebuilt { agent, ok, note }``auto_update::rebuild_agent` (covers
startup scan + manual `/rebuild` from dashboard) + `actions::approve`
(ApplyCommit).
- `Killed { agent }` — admin `HostRequest::Kill` + dashboard `/kill`.
- `Destroyed { agent }``actions::destroy`.
To add a new event: new `HelperEvent` variant + call sites + update
`prompts/manager.md` so the manager knows the new shape.
## Auto-update on startup
`hive-c0re serve` runs `auto_update::run` in a background task right after
@ -252,7 +315,23 @@ auto-update is a no-op — rebuild manually.
The dashboard surfaces pending updates per agent: a clickable "needs update
↻" badge appears whenever the marker differs from current rev. The badge
POSTs `/rebuild/<name>`, calling the same `auto_update::rebuild_agent`
path so manual triggers and the startup scan can't drift.
path so manual triggers and the startup scan can't drift. When at least
one container is stale, a top-level `↻ UPD4TE 4LL` button appears that
loops over every stale container.
## Dashboard action surface
Container row buttons (rendered per-state by `assets/app.js`):
- Always: `↻ R3BU1LD` (calls `lifecycle::rebuild`), and for sub-agents
`DESTR0Y` (container removed, state + creds kept).
- Running: `↺ R3ST4RT` + (sub-agents only) `■ ST0P`.
- Stopped: `▶ ST4RT`.
- Stale marker: clickable `needs update ↻` badge (same target as rebuild
but only shown when out of date).
Top of the containers list: `↻ UPD4TE 4LL` (when any stale) + the
"R3QU3ST SP4WN" form for queuing a new agent through the approval flow.
## Approval flow

View file

@ -25,9 +25,6 @@ Pick anything from here when relevant. Cross-cutting design notes live in
## UI / UX
- **Operator inbox view.** Drain replies addressed to `operator` and show
them on the dashboard. Today they accumulate in sqlite unread; you can
only see them by watching the live panel of the agent that sent them.
- **Per-agent UI substance.** Show last N inbox messages, last turn timing,
link back to dashboard.
- **xterm.js terminal** embedded per-agent, attached to a PTY exposed by