From 645887d87c65069efaff8ea0bac93d332023a179 Mon Sep 17 00:00:00 2001 From: iris Date: Tue, 8 Sep 2026 12:37:41 +0200 Subject: [PATCH] docs: fix genuine passive-voice hits in docs/turn-loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fifth batch of the ongoing write-good.Passive pass (hyperhive#4042): read all 42 hits across the four docs/turn-loop files in context and rewrote the 36 with a clearly nameable actor, usually one already sitting in the same sentence or established a sentence or two earlier (the broker, the harness, drive_turn, the renderer, hive-c0re, or a specific fn/type named right there). Left 6 alone: predicate-adjective copulas that only look passive ("is gone", "Bash is disallowed", "is documented behavior" — nothing actually acts on the subject), a config-conditional idiom with no in-file inconsistency to fix ("when hyperhive.docs.enable is set"), and a paraphrase of what a rendered message tells the agent ("the docs are mounted at that path"). Verified via vale before/after: 42 -> 6 write-good.Passive hits in docs/turn-loop, exactly the 6 left alone; error count and other warning categories unchanged. Re-read every changed line in its full surrounding context after editing (not just the vale count) per the lesson from the docs/process batch. --- docs/turn-loop/README.md | 16 ++++----- docs/turn-loop/claude-invocation.md | 50 ++++++++++++++--------------- docs/turn-loop/config.md | 36 ++++++++++----------- docs/turn-loop/mcp.md | 28 ++++++++-------- 4 files changed, 65 insertions(+), 65 deletions(-) diff --git a/docs/turn-loop/README.md b/docs/turn-loop/README.md index 811e9d0b..0cdf347b 100644 --- a/docs/turn-loop/README.md +++ b/docs/turn-loop/README.md @@ -29,10 +29,10 @@ agents) runs: 6. Wait for claude to exit and classify the turn's outcome from the stream + exit — success, compaction, rate-limit, auth-failure, or hard failure. The outcome drives the post-turn action (see - [Turn outcomes](#turn-outcomes)); compaction is handled inside the - session (see - [Compaction](claude-invocation.md#compaction)). Rate-limit - and auth-failure detection is described [below](#failure-detection-and-login). + [Turn outcomes](#turn-outcomes)); the session handles compaction + internally (see + [Compaction](claude-invocation.md#compaction)). This page describes + rate-limit and auth-failure detection [below](#failure-detection-and-login). 7. Emit `LiveEvent::TurnEnd { ok, note }`. Sleep `poll_ms` to avoid tight loops on transient failures. @@ -79,10 +79,10 @@ regardless of who sends them): transport — no per-turn stdio child (eliminates the re-registration race). +A small `Surface` trait, with one zero-sized impl, factors `hive-agent`'s wire types (`hive_core_agent_sock::{Request, Response}` — one unified enum shared by the agent and manager sockets) and its turn -loop are factored through a small `Surface` trait with one zero-sized -impl, so the loop itself has no per-role branches. See +loop, so the loop itself has no per-role branches. See `hive-agent/src/main.rs`'s module doc for the trait shape. @@ -96,8 +96,8 @@ opens turn-stats sqlite, prepares the on-boot files (see [claude-invocation](claude-invocation.md#on-boot-files)), installs claude plugins, spawns `web_ui::serve` + `vacuum::run`, and either drops into `serve_loop` directly (`Online`) or parks on -the login flow first (`NeedsLogin`). Forge notifications are polled by -their own process, not this loop — see `hive-forge-notify` in +the login flow first (`NeedsLogin`). Their own process polls forge +notifications, not this loop — see `hive-forge-notify` in [`forge.md`](../integrations/forge.md). Boot also opens the todos store and the socket in-container producers diff --git a/docs/turn-loop/claude-invocation.md b/docs/turn-loop/claude-invocation.md index b0dc311a..dfc62b5b 100644 --- a/docs/turn-loop/claude-invocation.md +++ b/docs/turn-loop/claude-invocation.md @@ -27,8 +27,8 @@ harness unit's PATH. By default that's the `claude-code` in the agent's own nixpkgs (the meta flake's `nixpkgs` input) via `environment.systemPackages`. Since that's typically a release channel and this package moves fast, the operator can pin one hive-wide with -`services.hyperhive.c0re.claudeCodePackage`: its store path is written -into each agent's flake, and `claude` on PATH becomes a symlink to it +`services.hyperhive.c0re.claudeCodePackage`: hive-c0re writes its +store path into each agent's flake, and `claude` on PATH becomes a symlink to it instead of the container's own `claude-code` — so there's only ever one `claude` in the container. Agents pick up a new build on their next rebuild, not live. See docs/process/gotchas.md::`claude-code` is unfree. @@ -37,21 +37,21 @@ Hive-enforced settings ship at `/etc/claude-code/managed-settings.json` (claude-code's canonical managed-settings path — precedence #1, read-only, un-overridable), wired in `nix/agent-modules/claude-settings.nix` from the `prompts/claude-settings.json` asset. `effortLevel` is -deliberately not in that file — effort is controlled live via the -`--effort` flag (`HIVE_DEFAULT_EFFORT` / the per-agent UI slider), which -managed scope would otherwise lock. +deliberately not in that file — the `--effort` flag +(`HIVE_DEFAULT_EFFORT` / the per-agent UI slider) controls effort +live, which managed scope would otherwise lock. -`` is read from `Bus::model()` on each turn. The initial -default is set by `hyperhive.model` in the agent's `agent.nix` -(NixOS option; propagates via `HIVE_DEFAULT_MODEL` env var; falls +The harness reads `` from `Bus::model()` on each turn. +`hyperhive.model` in the agent's `agent.nix` sets the initial +default (NixOS option; propagates via `HIVE_DEFAULT_MODEL` env var; falls back to `"haiku"` if unset). The operator can flip it at runtime with `/model ` in the web terminal — the next turn picks it -up. The choice is persisted to `/harness/hyperhive-model` so it +up. The harness persists the choice to `/harness/hyperhive-model` so it survives restart; override path: `HYPERHIVE_MODEL_FILE` env var for tests. -Context-window size is looked up per-model via -`harness_state::context_window_tokens(model)`. Resolution order (first +`harness_state::context_window_tokens(model)` looks up +context-window size per-model. Resolution order (first match wins): 1. `HIVE_CONTEXT_WINDOW_TOKENS_` env var, where `KEY` @@ -65,8 +65,8 @@ match wins): 3. Hard fallback: `200_000` (conservative; only reached outside NixOS where the env vars aren't set). -The effective window drives watermarks and is exposed at runtime -via `/api/state.context_window_tokens` so the UI can show a +The effective window drives watermarks; the harness exposes it at +runtime via `/api/state.context_window_tokens` so the UI can show a percentage-of-window ctx badge. **Session identity — a constant title.** Every turn keys on one fixed, @@ -86,8 +86,8 @@ context (it won't carry our title). claude stores sessions in `--name` writes the title into the file as a `custom-title` event, which is what `--resume ` resolves against. We never pass bare `--continue` (it resumes the _latest_ session in the cwd — the hijack -vector). Automemory and dynamic workflows (the `/workflows` feature) are -disabled via the managed settings at `/etc/claude-code/managed-settings.json`: +vector). The managed settings at `/etc/claude-code/managed-settings.json` +disable automemory and dynamic workflows (the `/workflows` feature): `disableWorkflows` keeps the `/workflows` machinery from spawning sub-runs that burn usage on the harness's autonomous turns. Claude's own native autocompact is **on**, at the widest window claude-code allows — see @@ -98,10 +98,10 @@ primary mechanism; hyperhive still owns that). `/new-session` slash command). It does _not_ touch the session inline — that would race a mid-write claude process. Instead `Bus::request_session_reset()` sets a one-shot flag consumed at the next turn boundary by `drive_turn`, -which **archives** the current session: the backing `<uuid>.jsonl` is -renamed to `<uuid>.jsonl.archived` (dropped out of claude's `*.jsonl` +which **archives** the current session: it renames the backing +`<uuid>.jsonl` to `<uuid>.jsonl.archived` (dropped out of claude's `*.jsonl` resolution glob, history preserved on disk, only the file carrying _our_ -title — any `choom` session sharing the cwd is left alone). The next +title — it leaves any `choom` session sharing the cwd alone). The next turn's `--resume <title>` then misses and self-heals into a fresh session. ## Compaction @@ -126,9 +126,9 @@ into its `run`: _then_ `/compact`s, so the agent can persist in-flight state before the detail collapses into a summary. -**Both triggers above are checked between hive-agent's own turns** — the -percent watermark is read from the last completed turn's telemetry -(`PercentPolicy::should_compact`, checked after `attempt()` returns), and +**hive-agent checks both triggers above between its own turns** — +`PercentPolicy::should_compact` reads the percent watermark from the +last completed turn's telemetry (checked after `attempt()` returns), and the reactive path only fires once claude-code has already refused a whole turn as too long. Neither can see context growth happening _inside_ a single long turn's own tool-calling before it returns — a turn whose tool @@ -188,8 +188,8 @@ still applies. benefit over starting fresh. `drive_turn` **archives** the current session (same mechanism as the operator reset — rename `<uuid>.jsonl` → `.archived`) so the next turn's `--resume <title>` misses and starts - fresh. Unlike proactive compaction the session is dropped entirely, not - compacted — and _no_ preceding checkpoint turn runs, because any turn + fresh. Unlike proactive compaction, `drive_turn` drops the session + entirely rather than compacting it — and _no_ preceding checkpoint turn runs, because any turn before the reset would just re-warm the cache and defeat the purpose. Set `HIVE_AUTO_RESET_WATERMARK_TOKENS=0` to disable. Autoreset and the operator reset are mutually exclusive per turn (both archive → fresh @@ -250,9 +250,9 @@ needs to `chown` a bind mount), once at startup: **Marker grammar.** `<!-- role:X -->` opens a block; any `<!-- /role:X -->` closes the current block. The renderer always uses - role `agent`, so blocks with other role tags are elided. Nesting is NOT + role `agent`, so it elides blocks with other role tags. Nesting is NOT supported — a stray opener with no closer runs until end of file. - Whitespace inside markers is tolerated (`<!--role:foo-->` parses the + The renderer tolerates whitespace inside markers (`<!--role:foo-->` parses the same as `<!-- role:foo -->`). Content outside any marker is always included. Today's `system.md` carries no markers (single agent role) — the grammar stays wired for a future manager / multi-role prompt. diff --git a/docs/turn-loop/config.md b/docs/turn-loop/config.md index a1129cbb..363d2b13 100644 --- a/docs/turn-loop/config.md +++ b/docs/turn-loop/config.md @@ -14,7 +14,7 @@ hyperhive.docs.enable = true; # default: false (true for the manager agent) Makes the hyperhive `docs/` tree available inside the container at a nix store path read from `$HIVE_DOCS_DIR`, and injects a single pointer sentence into the agent's system prompt so it knows the docs exist and -where to find them. The tree is served by `claude --add-dir` so the full +where to find them. `claude --add-dir` serves the tree so the full markdown is readable during every turn. Enabled by default only for the root/manager agent (`nix/templates/ruth.nix`). Any @@ -81,8 +81,8 @@ entry has: | `url` | yes | Absolute URL — may include a different port (the dashboard renders it as a plain anchor). | | `icon` | no | Emoji or short glyph prefix. Defaults to empty string. | -The list is written to `<state>/hyperhive-dashboard-links.json` by a -one-shot systemd unit at container boot. The harness's own web UI +A one-shot systemd unit writes the list to +`<state>/hyperhive-dashboard-links.json` at container boot. The harness's own web UI (`agent_links` in `hive-agent/src/web_ui/state.rs`) reads the file on each `/api/state` snapshot and appends the entries to the per-agent nav as `kind = External` links — no `hive-c0re` / operator-dashboard @@ -116,8 +116,8 @@ attribute defines one overlay entry: (`"games/bitburner"` serves at `/games/bitburner/…`). Constraints: `target` must start with an alphanumeric or `_` and -contain only alphanumerics, `_`, `.`, `/`, `-`. `..` segments are -rejected by a config assertion. The merge step refuses to overwrite +contain only alphanumerics, `_`, `.`, `/`, `-`. A config assertion +rejects `..` segments. The merge step refuses to overwrite files already present in the default dist — pick a target name that doesn't collide with existing paths (`static/`, `index.html`, etc.). @@ -153,8 +153,8 @@ to the wrong machine. With `null` the `tea-login` and `forge-avatar-sync` units aren't generated at all: an absent integration rather than a misdirected one. You don't normally set this — hive-c0re renders the host's real forge URL into every agent, and refuses to write a meta -flake without one, so `null` only survives where the agent modules are -evaluated outside a hive. +flake without one, so `null` only survives where nix evaluates the +agent modules outside a hive. **`hyperhive.matrix.url`** — homeserver URL used by `hive-matrix-daemon` when connecting via the matrix-sdk. hive-c0re @@ -170,7 +170,7 @@ agent's own netns to the agent itself, not the homeserver). With `null` the daemon has no homeserver and no-ops exactly as it does without a token. The hive only forwards `HIVE_MATRIX_URL` when it actually has a matrix vhost to name, so `null` survives where a hive -runs no homeserver, or where the agent modules are evaluated outside a +runs no homeserver, or where nix evaluates the agent modules outside a hive. ## Claude Code plugins @@ -199,9 +199,9 @@ hyperhive.claudePluginsAutoUpdate = false; # default to add custom marketplaces. Idempotent — re-adding an existing source is a no-op. - **`claudePlugins`** — list of plugin specs passed to - `claude plugin install <spec>`. Each spec is installed on every boot - (`install` is expected to be idempotent); failures log a warning but - don't abort boot. Defaults to Anthropic's `skill-creator` (so every + `claude plugin install <spec>`. The harness installs each spec on + every boot, assuming `install` is idempotent; failures log a warning + but don't abort boot. Defaults to Anthropic's `skill-creator` (so every agent can author, refine, and evaluate its own skills) plus hyperhive's own `base` plugin — skills that apply to every agent regardless of role (currently just `state-hygiene`) — all without @@ -234,8 +234,8 @@ window, leaving only warnings and errors. The function handles `+toolchain` selectors (`cargo +nightly build`) and passes through cleanly when `--message-format` is already present. -Non-compile subcommands (`new`, `add`, third-party `cargo-*`) are -left untouched. +It leaves non-compile subcommands (`new`, `add`, third-party +`cargo-*`) untouched. Set to `false` for agents that parse cargo's JSON output programmatically and don't pass `--message-format json` themselves. @@ -256,12 +256,12 @@ is a no-op without the other: - **`useApiKey`** tells the harness itself not to wait for a Claude OAuth session: at boot, `LoginState::from_dir` reports `Online` without checking `~/.claude/` (`hive_agent::login::using_api_key`, reads - `HIVE_USE_API_KEY`), and the fact is stamped into the consolidated - harness state file so the operator dashboard also stops reading this + `HIVE_USE_API_KEY`), and the harness stamps the fact into the + consolidated harness state file so the operator dashboard also stops reading this agent's empty `~/.claude/` as "needs login." An api-key agent that hits - a real 401 (the key itself is bad) still surfaces `needs_login` — only - the boot-time "have I ever logged in" check is bypassed, not the - auth-failure path. + a real 401 (the key itself is bad) still surfaces `needs_login` — the + harness skips only the boot-time "have I ever logged in" check, not + the auth-failure path. - **`backendEnvironmentFile`** points at an operator-managed file (outside the nix store, one `KEY=value` per line, systemd `EnvironmentFile` syntax) supplying the credentials `claude` itself diff --git a/docs/turn-loop/mcp.md b/docs/turn-loop/mcp.md index 2a140b08..145c3a6f 100644 --- a/docs/turn-loop/mcp.md +++ b/docs/turn-loop/mcp.md @@ -1,8 +1,8 @@ # MCP surface -The harness ships an embedded MCP server (rmcp 2). The built-in -`hyperhive` surface is served over streamable HTTP by a persistent -`hive-mcp-http` daemon (loopback, `127.0.0.1:<hyperhive.mcp.httpPort>`, +The harness ships an embedded MCP server (rmcp 2). A persistent +`hive-mcp-http` daemon serves the built-in `hyperhive` surface over +streamable HTTP (loopback, `127.0.0.1:<hyperhive.mcp.httpPort>`, per-container private netns). Claude connects to its stable URL via `--mcp-config` rather than respawning a stdio child each turn, so the URL survives the per-turn claude re-spawn (and a host-side hive-c0re @@ -15,7 +15,7 @@ stdio bridge, `bash` runs its own persistent streamable-http listener server name is `hyperhive`, so the tools land in claude as `mcp__hyperhive__<tool>`. -Tool access is gated by tool groups (`HIVE_TOOL_GROUPS`). The default +Tool groups (`HIVE_TOOL_GROUPS`) gate tool access. The default preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and `execution`. Privileged groups (`lifecycle`, `approvals`, `scheduling`, `diagnostics`) are opt-in via the P3RM1SS10NS tab. @@ -36,8 +36,8 @@ preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and (the topology parent is always reachable regardless of this list — that carve-out is structural, keyed on parent relationship, not name). - `recv` — drain inbox. Always an immediate peek, never blocks. `max` - (default 1, cap 5) drains up to N rows. Each returned row is prefixed with - `[msg #<id>]` (broker row id; note the highest id seen, then pass + (default 1, cap 5) drains up to N rows. `recv` prefixes each returned row + with `[msg #<id>]` (broker row id; note the highest id seen, then pass it to `ack_until` to bulk-triage the batch). **Graceful shutdown**: when the harness receives a stop signal, the inbox becomes fenced and `recv` returns an explicit `from: "graceful-stop"` message instead of an empty inbox. @@ -47,17 +47,17 @@ preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and other turn: if the context crossed the watermark the harness runs a notes-checkpoint turn and then `/compact`. Compacting before shutdown keeps a later cold start cheap instead of re-uploading a large transcript. -- `ack_until(up_to)` — bulk-mark inbox rows handled: every row with - broker id `<= up_to` is stamped as acked in a single UPDATE. +- `ack_until(up_to)` — bulk-mark inbox rows handled: it stamps every row + with broker id `<= up_to` as acked in a single UPDATE. Recipient-scoped (agents can only ack their own rows). Use when a restart redelivers a large backlog of already-handled messages: read the highest `[msg #N]` from the set you've actually processed, then `ack_until(N)` to prevent re-pop. Acked rows never redeliver. Transient pings (sentinel id 0) have nothing to ack and show no marker. -**System messages** (from sender `system`): the higher-urgency -lifecycle events (`hive_sh4re::manager::HelperEvent`) are -delivered as regular inbox messages (same `recv` path; body is a JSON +**System messages** (from sender `system`): the broker delivers the +higher-urgency lifecycle events (`hive_sh4re::manager::HelperEvent`) +as regular inbox messages (same `recv` path; body is a JSON object with an `event` discriminant field). The **submitting agent** (the root agent for top-level containers; an agent with the `approvals` tool group for its own subtree) receives `container_crash`, @@ -136,7 +136,7 @@ hive_name?, swarm_name?, matrix_accounts? }`. `matrix_accounts` is a [`docs/tools/scheduling.md`](../tools/scheduling.md). - **Forge repos** (`forge`) — `create_repo` — the only agent path to create a repo under the `agents/` org (direct forge token creation is - disabled for agents). The repo is created in the c0re-owned `agents` + disabled for agents). The repo lands in the c0re-owned `agents` org; the calling agent gets write collaborator access; the default branch is branch-protected (operator-team must approve merges, so the agent can't self-merge). Opt-in; not in any default preset. @@ -202,8 +202,8 @@ status hint lives in the wake prompt + UI header, not here. `Skill` is what makes an installed plugin's `SKILL.md` invokable — without it, a skill's `description` frontmatter never gets seen by the model no matter how well it matches the task. -- Tool-group-gated built-ins: `WebFetch`, `WebSearch` (added when the - `web_tools` tool group is enabled — see P3RM1SS10NS tab). +- Tool-group-gated built-ins: `WebFetch`, `WebSearch` (added once the + operator enables the `web_tools` tool group — see P3RM1SS10NS tab). - Denied by omission (absent from the harness `--tools` / `--allowedTools`, so they "literally don't exist" in a harness turn): `Bash`, `Task`, `NotebookEdit`, `TodoWrite`.