docs: scrub self-ref cookies across 5 files (#719 batch 2)

All five files scrubbed to 0 issue-ref cookies. Most refs were
'(closes #NNN)' style attribution after closed work landed.

- docs/turn-loop.md: 6 → 0 (#598 unified binary, #419 auth-fail,
  #542 mtime resumption, #519 role markers, #442 spawn path,
  #474/#478 partial edit)
- docs/approvals.md: 5 → 0 (#317 flake validation, #250 withdraw,
  #441 rebuild-queue dispatch, #753 manager port hash, #425
  crash-watch grace)
- docs/terminal-rendering.md: 2 → 0 (#666 ask-operator inline)
- docs/conventions.md: 3 → 0 (#753 port hash, #692 parent
  sentinel, #486 reparenting)
- docs/security.md: 4 → 0 (#240 + #658 ×2 + #673/#678 attribution)

Combined with PR #810 (docs/web-ui.md 14→0), this PR closes the
small / medium files. Remaining: agent-hierarchy.md (13),
gotchas.md (13), persistence.md (13). gateway.md is atlas's lane.
This commit is contained in:
iris 2026-05-31 15:36:03 +02:00 committed by mara
commit 5ca96b8c85
5 changed files with 27 additions and 29 deletions

View file

@ -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/<id>: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.

View file

@ -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.
- `<parent>` — the sender's parent per `topology.json` (`#692`).
Rewritten at send time by `topology::resolve_recipient`: looks up
- `<parent>` — 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 `<parent>`, the broker stores the
*resolved* label as the message's recipient — the dashboard and

View file

@ -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/<name>/.claude/` — mode `0700`, owned by the per-agent
user `<name>` (post-#658 — was `/root/.claude` owned by root
pre-#658). nixbld users cannot read it.
user `<name>`. nixbld users cannot read it.
- `$HYPERHIVE_STATE_DIR/forge-token` (= `/agents/<name>/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.

View file

@ -43,7 +43,7 @@ parent's negative pull.
| `.tool-use` (flat) | `→ Name args…` | cyan | tool_use w/o rich renderer | stream-json |
| `.tool-use` `<details>` | `Write/Edit <path> · +N` (no `→`) | cyan, body is +/- diff | `renderRichToolUse` Write/Edit | stream-json |
| `.tool-use` `<details open>` | `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) | `← <txt>` | muted | short `tool_result` (≤120c, non-recv) | stream-json |
| `.tool-result-block` `<details>` | `Nl · headline` | muted, body is text | long generic `tool_result` | stream-json |
| `.tool-result-block` `<details open>` | `recv ← <txt>` | 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

View file

@ -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 (`<!-- role:agent -->...<!-- /role:agent -->`,
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