diff --git a/docs/approvals.md b/docs/approvals.md index 34645bb1..0f92ccfa 100644 --- a/docs/approvals.md +++ b/docs/approvals.md @@ -30,7 +30,7 @@ happens after a decision lands. applied. 3a. **Flake validation (ApplyCommit only):** after the proposal tag is planted, hive-c0re reads `proposal/:flake.lock` and - runs two checks (closes #317). If either check fails, no + runs two checks. If either check fails, no pending approval is created for the operator — the row is marked failed and surfaces on the dashboard with the validation message: @@ -66,7 +66,7 @@ happens after a decision lands. ### Withdrawing a pending approval The manager can call `cancel_loose_end(kind: "approval", id)` to -withdraw an approval that hasn't been acted on yet (closes #250). +withdraw an approval that hasn't been acted on yet. The row transitions to `ApprovalStatus::Cancelled` (distinct from `Denied`/`Failed`), the dashboard pulls the card out of the pending pane, and `ApprovalResolved { status: "cancelled" }` fires @@ -202,7 +202,7 @@ approval id to retry. Because tags are first-class git objects, rejected and failed trees stay browsable forever — `git log --tags` in the applied repo is the audit trail. -### Dispatch via `rebuild_queue` (#441) +### Dispatch via `rebuild_queue` Long-running approval work — `ApplyCommit`, `UpdateMetaInputs`, `Spawn` — no longer runs inline inside `actions::approve`. Instead @@ -344,7 +344,7 @@ Differences from sub-agents: (vs `agent-base`). - Container name is `hm1nd` (no `h-` prefix). - Web UI port via `lifecycle::agent_web_port("hm1nd")` — same - FNV-1a hash as every other agent (8100..8999 range) since #753. + FNV-1a hash as every other agent (8100..8999 range). - `set_nspawn_flags` adds two extra binds: `/var/lib/hyperhive/agents` → `/agents` (RW) so the manager can edit per-agent proposed repos, and `/var/lib/hyperhive/applied` → `/applied` (RO) so the manager @@ -424,8 +424,8 @@ regular claude turn so the manager can react. Variants such transient was cleared in the last 30s (`RECENT_TRANSIENT_GRACE` tombstone, three `POLL_INTERVAL`s — closes the race where a lifecycle op finishes between two crash-watch polls and the - container shows briefly as "stopped without transient" before the - next start, #425). Manager can `start` it again or escalate. + container shows briefly as "stopped without transient" before + the next start). Manager can `start` it again or escalate. - `NeedsLogin { agent }` — sub-agent has no claude session yet. Manager can't act directly (interactive OAuth); typically flags the operator. diff --git a/docs/conventions.md b/docs/conventions.md index 16c2bbb5..f5370548 100644 --- a/docs/conventions.md +++ b/docs/conventions.md @@ -10,7 +10,7 @@ exist because something already went wrong without them. - The manager is `hm1nd` (no `h-` prefix, fixed name). - `MAX_AGENT_NAME` in `lifecycle.rs` enforces the cap. - Per-agent web UI port = `WEB_PORT_BASE + FNV1a(name) % WEB_PORT_RANGE` - (8100..8999) for every agent including the manager (#753); dashboard + (8100..8999) for every agent including the manager; dashboard `cfg.dashboardPort` (default 7000). ## Identity = socket @@ -32,12 +32,12 @@ angle-bracket and asterisk shapes below are structurally safe. (`agent_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. -- `` — the sender's parent per `topology.json` (`#692`). - Rewritten at send time by `topology::resolve_recipient`: looks up +- `` — the sender's parent per `topology.json`. Rewritten at + send time by `topology::resolve_recipient`: looks up `parent_of(sender)` and falls back to `operator` when the sender is a root agent (or absent from topology entirely). Lets agents address their parent without learning the label, so runtime reparenting - (`#486`) propagates with zero agent-side restart. + propagates with zero agent-side restart. When the resolver rewrites ``, the broker stores the *resolved* label as the message's recipient — the dashboard and diff --git a/docs/security.md b/docs/security.md index 22e4ab64..831bed24 100644 --- a/docs/security.md +++ b/docs/security.md @@ -1,6 +1,6 @@ # Security model -## Nix builds and credential isolation (issue #240) +## Nix builds and credential isolation ### Background @@ -19,12 +19,11 @@ any file in the container that the nixbld user can read. **What is NOT exposed**: - `/home//.claude/` — mode `0700`, owned by the per-agent - user `` (post-#658 — was `/root/.claude` owned by root - pre-#658). nixbld users cannot read it. + user ``. nixbld users cannot read it. - `$HYPERHIVE_STATE_DIR/forge-token` (= `/agents//state/forge-token`) — written at mode `0600` by `hive-c0re/src/forge.rs` and chowned to the - per-agent uid:gid by `lifecycle::chown_to_agent` (post-#673/#678). - nixbld users cannot read it. + per-agent uid:gid by `lifecycle::chown_to_agent`. nixbld users + cannot read it. **Policy**: all credential files written to agent state directories MUST be mode `0600` or stricter. Do not create world-readable secret files in agent state dirs. diff --git a/docs/terminal-rendering.md b/docs/terminal-rendering.md index eed6948c..f62d5c47 100644 --- a/docs/terminal-rendering.md +++ b/docs/terminal-rendering.md @@ -43,7 +43,7 @@ parent's negative pull. | `.tool-use` (flat) | `→ Name args…` | cyan | tool_use w/o rich renderer | stream-json | | `.tool-use` `
` | `Write/Edit · +N` (no `→`) | cyan, body is +/- diff | `renderRichToolUse` Write/Edit | stream-json | | `.tool-use` `
` | `send → to · NL`, `ask → to`, `answer #id` | cyan, body is markdown | rich renderer for send / ask / answer | stream-json | -| `.tool-use .ask-answer-inline-slot` | (sub-block under `ask → operator`) | inherits row | inline answer form bound by `reconcileAskBinds` to the loose-end | #666 | +| `.tool-use .ask-answer-inline-slot` | (sub-block under `ask → operator`) | inherits row | inline answer form bound by `reconcileAskBinds` to the loose-end | rich renderer | | `.tool-result` (flat) | `← ` | muted | short `tool_result` (≤120c, non-recv) | stream-json | | `.tool-result-block` `
` | `Nl · headline` | muted, body is text | long generic `tool_result` | stream-json | | `.tool-result-block` `
` | `recv ← ` | muted, body is markdown | `tool_result` correlated to a prior `recv` tool_use via id | stream-json | @@ -105,7 +105,7 @@ isn't in the built-in `fmtToolUse` switch: This keeps `mcp__matrix__send_message` and similar from dumping raw JSON. -## Inline ask-operator answer (#666) +## Inline ask-operator answer When an agent calls `mcp__hyperhive__ask` with `to == "operator"` (default), the rich tool-use renderer mounts an empty diff --git a/docs/turn-loop.md b/docs/turn-loop.md index d80f27e7..a298ecd1 100644 --- a/docs/turn-loop.md +++ b/docs/turn-loop.md @@ -6,8 +6,8 @@ claude has access to in return. ## The loop Each agent harness (`hive serve`, with role picked from `$HIVE_ROLE` -— `"agent"` for sub-agents, `"manager"` for the manager; post-#598 -this is one binary not two) runs: +— `"agent"` for sub-agents, `"manager"` for the manager — one +binary, not two) runs: 1. Long-poll `Recv` on its socket. The host-side broker (`broker.rs::recv_blocking_batch`) returns immediately if there's @@ -31,8 +31,8 @@ this is one binary not two) runs: (`Bus::emit_status("rate_limited")`), sleeps `HIVE_RATE_LIMIT_SLEEP_SECS` (default 300), then retries. The dashboard and per-agent page show a `⊘ rate limited` badge - while the harness is parked. **Auth-failed detection** (closes - #419): both stdout and stderr pumps also match + while the harness is parked. **Auth-failed detection**: both + stdout and stderr pumps also match `AUTH_FAIL_MARKERS` (`"authentication_failed"`, `401`, etc.). On match the harness writes `{state_dir}/hyperhive-needs-login`, emits `needs_login_idle` status, requeues the inflight message @@ -40,7 +40,7 @@ this is one binary not two) runs: the same path used at boot. The operator re-authenticates via the per-agent web UI login flow; on success the sentinel is cleared and the queued message drives the next turn normally. - **Mtime-snapshot resumption** (closes #542): `wait_for_login` + **Mtime-snapshot resumption**: `wait_for_login` snapshots the `~/.claude/` dir (newest file mtime + file count) at entry and only resumes when that snapshot advances — not just when credentials exist on disk. This prevents a silent @@ -176,8 +176,8 @@ socket at `/run/hive/` once at startup: - `claude-system-prompt.md` — rendered from `hive-ag3nt/prompts/system.md` by `hive_ag3nt::prompt::render`: HTML-comment markers (`...`, - same for `role:manager`) gate the role-specific blocks (closes - #519); everything else is shared. Five placeholders are then + same for `role:manager`) gate the role-specific blocks; everything + else is shared. Five placeholders are then substituted: `{label}` (short agent name), `{qualified_label}` (hive-qualified `name@domain` form), `{operator_pronouns}`, `{hive_identity}` (e.g. `` on hive `pr1ma` ``; empty when @@ -350,10 +350,9 @@ meta's. `agent.nix`, commits the changes, and calls `request_apply_commit` with the commit sha — the first ApplyCommit on a freshly-init'd config creates the container. Fails if a proposed repo for this - name already exists. (The pre-#442 path through a separate - manager-side `request_spawn` was removed; operator can still - direct-spawn an empty agent from the dashboard's `◆ R3QU3ST SP4WN` - button which routes via `HostRequest::RequestSpawn`.) + name already exists. (The operator can also direct-spawn an empty + agent from the dashboard's `◆ R3QU3ST SP4WN` button, which routes + via `HostRequest::RequestSpawn`.) - `kill(name)` — graceful stop. No approval required. - `start(name)` — start a stopped sub-agent. No approval. - `restart(name)` — stop + start. No approval. @@ -398,7 +397,7 @@ meta's. approval (use `remind` for unapproved self-wake). Long downtime fires once per recurring row on resume (catch-up clamp). - `edit_schedule(id, body?, description?, interval_seconds?, next_fire_at_unix?, targets_add?, targets_remove?)` — - partial-update a schedule (#474/#478). Pass only the fields to + partial-update a schedule. Pass only the fields to change; absent fields are left alone. `targets_add` / `targets_remove` mutate the recipient list in the same transaction; re-adding a previously-cancelled target drops its tombstone + history (fresh