Watch
0
0
Fork
You've already forked hyperhive
0

docs: state current behaviour, drop change-log wording

Refs #3902
This commit is contained in:
atlas 2026-10-02 08:22:09 +02:00 • committed by mara
commit 9545151b8d
10 changed files with 62 additions and 112 deletions

View file

@ -52,11 +52,10 @@ power-intent registry:
- `kv` — small persistent key/value store (`key PK / value`) for - `kv` — small persistent key/value store (`key PK / value`) for
host-side bookkeeping that doesn't warrant its own table. host-side bookkeeping that doesn't warrant its own table.
⚠️ The `mcp__hyperhive__remind` queue is **not** here any more: it ⚠️ The `mcp__hyperhive__remind` queue lives in a harness-local,
moved to a harness-local, per-agent store as part of the per-agent store — see [`/harness/` contents
loose-ends-v2 migration — see [`/harness/` contents
below](#state-dirs-per-agent) for where reminders (and todos) below](#state-dirs-per-agent) for where reminders (and todos)
actually live now. live.
- `approvals` — the queue. `agent / kind (merge_config_pr | spawn | - `approvals` — the queue. `agent / kind (merge_config_pr | spawn |
update_meta_inputs | schedule_prompt) / update_meta_inputs | schedule_prompt) /
commit_ref / requested_at / status / resolved_at / note`. commit_ref / requested_at / status / resolved_at / note`.
@ -109,11 +108,11 @@ One table:
harness emits during turn loop execution. harness emits during turn loop execution.
<!-- vale write-good.Passive = NO --> <!-- vale write-good.Passive = NO -->
The harness both writes and vacuums it — this used to be a host-side The harness both writes and vacuums it. Cleanup runs in-container
sweep, but hive-c0re runs as the unprivileged `hive-core` user under because hive-c0re runs as the unprivileged `hive-core` user under
privsep and can't delete agent-owned files (host-side deletes hit privsep and can't delete agent-owned files (host-side deletes hit
`PermissionDenied` on the bash-task trio and a readonly database error `PermissionDenied` on the bash-task trio and a readonly database error
here), so cleanup moved in-container. `hive-agent`'s `vacuum::run` here). `hive-agent`'s `vacuum::run`
(`hive-agent/src/vacuum.rs`) sweeps hourly. Retention is (`hive-agent/src/vacuum.rs`) sweeps hourly. Retention is
**type-scoped**: it deletes only the verbose `stream` rows (the raw **type-scoped**: it deletes only the verbose `stream` rows (the raw
claude `stream-json` deltas — one per text chunk / tool use, the bulk claude `stream-json` deltas — one per text chunk / tool use, the bulk
@ -192,10 +191,9 @@ is in-process only, so it wouldn't serialise a writer running as a
separate process; none of today's writers are. separate process; none of today's writers are.
hive-c0re reads this file on each `build_all` sweep (~10s) via hive-c0re reads this file on each `build_all` sweep (~10s) via
`container_view::read_harness_flags`. Falls back to the legacy individual `container_view::read_harness_flags`. Falls back to the individual
sentinel files (`hyperhive-rate-limited`, `hyperhive-needs-login`) if the sentinel files (`hyperhive-rate-limited`, `hyperhive-needs-login`) when the
JSON is absent, so existing containers keep working through the transition JSON is absent.
window before their next rebuild.
### `/var/lib/hyperhive/db/build_logs.sqlite` (host) ### `/var/lib/hyperhive/db/build_logs.sqlite` (host)
@ -218,7 +216,7 @@ Three indices:
- `(node_id)` — added by a later migration so a build log row can be - `(node_id)` — added by a later migration so a build log row can be
looked up by the job-queue node it belongs to (a `hive_jobq` node is looked up by the job-queue node it belongs to (a `hive_jobq` node is
immutable after insert, so hive-c0re records the link on the log row immutable after insert, so hive-c0re records the link on the log row
instead); legacy rows predating the column keep `node_id IS NULL`. instead); rows predating the column have `node_id IS NULL`.
Writes are best-effort: `append_stdout` / `append_stderr` / `finish` Writes are best-effort: `append_stdout` / `append_stderr` / `finish`
log a warning on sqlite error and let the build continue. A failed log a warning on sqlite error and let the build continue. A failed
@ -293,22 +291,19 @@ Under `/var/lib/hyperhive/agents/<name>/`:
(`hive-agent`'s `vacuum::run`, same one that ages out `stream` (`hive-agent`'s `vacuum::run`, same one that ages out `stream`
event rows above) deletes terminal task trios older than 48 event rows above) deletes terminal task trios older than 48
hours; non-terminal (still-running) tasks are never deleted. This hours; non-terminal (still-running) tasks are never deleted. This
used to be a host-side `hive-c0re` vacuum, moved in-container for sweep runs in-container, for the same privsep-ownership reason as
the same privsep-ownership reason as the events vacuum above. the events vacuum above.
- `hyperhive-state.sqlite` — consolidated loose-ends-v2 store: todos - `hyperhive-state.sqlite` — consolidated loose-ends-v2 store: todos
and reminders, one small table each in a single file (in-container and reminders, one small table each in a single file (in-container
daemons — `hive-bash-daemon`, `hive-matrix-daemon`, daemons — `hive-bash-daemon`, `hive-matrix-daemon`,
`hive-forge-notify` — upsert keyed todos here over the harness's `hive-forge-notify` — upsert keyed todos here over the harness's
in-agent socket, `HIVE_AGENT_SOCKET`; the harness merges them into in-agent socket, `HIVE_AGENT_SOCKET`; the harness merges them into
`get_loose_ends` output and clears a row on `mark_todo_done`). `get_loose_ends` output and clears a row on `mark_todo_done`).
Replaces three formerly separate files Consolidates todos and reminders into one file. A one-time boot
(`hyperhive-todos.sqlite`, `hyperhive-reminders.sqlite`, and the migration (`db_migrate::run`) folds any pre-existing files
old file-based `mcp-loose-ends/` scanner before that) — a one-time (`hyperhive-todos.sqlite`, `hyperhive-reminders.sqlite`, the older
boot migration (`db_migrate::run`) folds the legacy files into this file-based `mcp-loose-ends/` scanner) into this path the first time
path the first time a harness boots after the upgrade. Also backs a harness boots. Also backs the `mcp__hyperhive__remind` queue.
the `mcp__hyperhive__remind` queue, which moved from a host-side
`broker.sqlite` table to this per-agent store as part of the same
migration.
The harness itself is also a producer, not just the socket server: The harness itself is also a producer, not just the socket server:
boot wiring's `spawn_todo_socket` starts `todo_server::run` (the boot wiring's `spawn_todo_socket` starts `todo_server::run` (the
@ -344,9 +339,7 @@ The RW on `state` is deliberate, not an oversight: the holder recovers
other agents, which includes writing into their state (for example other agents, which includes writing into their state (for example
seeding notes, clearing a stuck sentinel) as well as reading it. seeding notes, clearing a stuck sentinel) as well as reading it.
This is the **only** cross-agent mount. Dropping the topology parent This is the **only** cross-agent mount. An agent holding no capability
field took with it the unconditional grant every agent used to
get over its own direct children — an agent holding no capability now
sees its own dirs and nothing else. sees its own dirs and nothing else.
**`harness` isn't mounted at all.** It holds that agent's own runtime **`harness` isn't mounted at all.** It holds that agent's own runtime
@ -395,8 +388,8 @@ Contents:
- `topology.json` — the agent roster (`["alice", "bob", "ruth"]`). - `topology.json` — the agent roster (`["alice", "bob", "ruth"]`).
Written by `topology::reconcile` on every meta sync; read by Written by `topology::reconcile` on every meta sync; read by
`topology::all_agents`, which is the set the `ManageRootAgent` `topology::all_agents`, which is the set the `ManageRootAgent`
capability grants mounts over. Carried a `parent` per agent in the capability grants mounts over. The reader also accepts a map-shaped
legacy format; the reader still accepts that shape and keeps its keys. file carrying a `parent` per agent, and keeps its keys.
- `tool-groups.json` — per-agent MCP tool group grants - `tool-groups.json` — per-agent MCP tool group grants
(`{ "alice": ["messaging", "inbox", "execution"] }`). Written by (`{ "alice": ["messaging", "inbox", "execution"] }`). Written by
`tool_groups::set_groups`; injected as `HIVE_TOOL_GROUPS` env `tool_groups::set_groups`; injected as `HIVE_TOOL_GROUPS` env
@ -420,9 +413,8 @@ Contents:
The root agent has the meta dir RO-mounted at `/meta/`. The root agent has the meta dir RO-mounted at `/meta/`.
The `.meta-migration-done` marker no longer exists: the hive-c0re renders containers onto `meta#<n>` at creation, so there is
one-shot container repoint it guarded no longer exists either, since no `.meta-migration-done` marker to guard a one-shot repoint. A stale
hive-c0re renders containers onto `meta#<n>` at creation. A stale
marker file left over from an older hive is inert and the operator can marker file left over from an older hive is inert and the operator can
delete it. delete it.
@ -519,7 +511,7 @@ reinstall.
The harness runs as a per-agent unix user inside the container The harness runs as a per-agent unix user inside the container
(`services.hyperhive.agent.user.name`, defaults to the agent's logical label so each (`services.hyperhive.agent.user.name`, defaults to the agent's logical label so each
container has a uniquely named user). Operators with legacy root-owned container has a uniquely named user). Operators with root-owned
state dirs need a one-time data shuffle so they don't lose their claude state dirs need a one-time data shuffle so they don't lose their claude
session. session.
@ -533,8 +525,7 @@ container lifetime:
chance to chown. Also re-applies on every rebuild in case the chance to chown. Also re-applies on every rebuild in case the
meta-flake's per-agent name evolves (rare). meta-flake's per-agent name evolves (rare).
2. **Migrate any leftover `/root/.claude` content into 2. **Migrate any leftover `/root/.claude` content into
`${homeDir}/.claude`** — legacy `claude` wrote to root's `${homeDir}/.claude`.** Marker
empty home; the bind mount didn't exist yet. Marker
(`/var/lib/hive-agent-user-migrated`) guards single-shot; it's (`/var/lib/hive-agent-user-migrated`) guards single-shot; it's
only written once there's nothing left to migrate, so a `cp` only written once there's nothing left to migrate, so a `cp`
failure leaves it absent and the next boot retries. failure leaves it absent and the next boot retries.
@ -557,9 +548,7 @@ container lifetime:
there as the agent user. Why the mode matters: there as the agent user. Why the mode matters:
[`boundary.md`](../trust-boundary/boundary.md#the-per-agent-socket-dir). [`boundary.md`](../trust-boundary/boundary.md#the-per-agent-socket-dir).
The activation script will eventually become unnecessary once no The activation script migrates any root-owned state dir still present.
operators have legacy root-owned state dirs left to migrate; drop
the body + marker check at that point.
## Matrix per-agent daemon + token-arrival trigger ## Matrix per-agent daemon + token-arrival trigger

View file

@ -259,11 +259,11 @@ Two consequences worth knowing:
<!-- vale write-good.Passive = NO --> <!-- vale write-good.Passive = NO -->
- **Flattening a chain under a brace is safe.** The stop chain used to nest - **Flattening a chain under a brace is safe.** A brace (`AgentWindow`)
`Signal` over `Drain` over `StopForUpdate` specifically so the lease stayed holds the lease continuity for the stop chain's subtree: as independent
continuous — as independent siblings each would acquire it separately and siblings, `Signal`, `Drain`, and `StopForUpdate` would each acquire the
leave a gap another DAG could claim the agent in, mid-bounce. A brace supplies lease separately and leave a gap another DAG could claim the agent in,
that continuity directly, so the nesting is no longer load-bearing. mid-bounce.
- **Observability is unaffected.** `running_transients` keys off a node's - **Observability is unaffected.** `running_transients` keys off a node's
_payload_ agent, not off a declared lease edge, so every child still lights its _payload_ agent, not off a declared lease edge, so every child still lights its
own dashboard pill and still reports its own `takes_container_down` to the own dashboard pill and still reports its own `takes_container_down` to the
@ -284,8 +284,8 @@ reconcile_), so there is no durable-recovery machinery to go wrong.
### Cancel, history ### Cancel, history
The agent-per-node move removed submit-time dedup (a multi-agent DAG Every submit enqueues a fresh DAG; a multi-agent DAG has no single agent
has no single agent to key a dedup on), so every submit enqueues a fresh DAG. to key a dedup on.
Cancel only applies to DAGs that are still fully queued (an in-flight nix build isn't Cancel only applies to DAGs that are still fully queued (an in-flight nix build isn't
interruptible) — each op is one DAG now, so there are no child DAGs to cascade to. interruptible) — each op is one DAG now, so there are no child DAGs to cascade to.

View file

@ -53,19 +53,6 @@ wrapper carries an address, a CA and a client certificate but deliberately
**no token**, so that read answers `403` whether or not the role exists. Read **no token**, so that read answers `403` whether or not the role exists. Read
the unit's journal instead. the unit's journal instead.
<details><summary>Upgrading a swarm set up with the older swarm-bootstrap policy</summary>
A store set up before the granter existed has every grant, but no `bao-granter`
policy or role. After the deploy that introduces it, each `swarm-bao-*-policy`
unit fails and logs the one-time step. Run the setup step as it stands. The
old policy can go, with the root token again:
```bash
bao policy delete swarm-bootstrap
```
</details>
### Residual risk ### Residual risk
The granter is root-equivalent. It may write any `swarm-*` policy with any The granter is root-equivalent. It may write any `swarm-*` policy with any

View file

@ -129,9 +129,8 @@ loose-end follows.
controls whether the `bash` MCP server reaches the agent's claude controls whether the `bash` MCP server reaches the agent's claude
config at all, suppressing every `mcp__bash__*` tool config at all, suppressing every `mcp__bash__*` tool
when absent — not just leaving them unused. Its own `tools()` returns when absent — not just leaving them unused. Its own `tools()` returns
`&[]` and stays that way: `mcp__bash__run`/`status`/`kill` belong to a `&[]`: `mcp__bash__run`/`status`/`kill` belong to a
different, out-of-process MCP server (`bash`, not `hyperhive`), so they different, out-of-process MCP server (`bash`, not `hyperhive`), so they
were never real entries in the `mcp__hyperhive__*` allowlist `tools()` aren't entries in the `mcp__hyperhive__*` allowlist `tools()`
builds — the two dead `["run", "status"]` strings it used to return builds. Removing `execution` from an
matched nothing, so they're gone now. Removing `execution` from an
agent's groups **does** remove bash availability. agent's groups **does** remove bash availability.

View file

@ -281,8 +281,7 @@ to discover valid label names before triaging or to audit the label set.
falling back. `--job` selects the job (0-based, default 0); falling back. `--job` selects the job (0-based, default 0);
`--attempt` picks the run attempt for the durable path (default 1; `--attempt` picks the run attempt for the durable path (default 1;
re-runs increment it). `--json` wraps the output. re-runs increment it). `--json` wraps the output.
- `ci-rerun` re-runs CI without pushing an empty commit (the old - `ci-rerun` re-runs CI without pushing an empty commit. Forgejo has no token-usable
retrigger path, which littered PR history). Forgejo has no token-usable
REST endpoint to re-run an _existing_ run (the run-page rerun buttons REST endpoint to re-run an _existing_ run (the run-page rerun buttons
are CSRF-gated web routes a token POST 404s), so this dispatches a are CSRF-gated web routes a token POST 404s), so this dispatches a
**fresh** run of the workflow via the workflow-dispatch API **fresh** run of the workflow via the workflow-dispatch API

View file

@ -98,9 +98,9 @@ route refuses to create an account by that name.
mechanism behind "accounts are the enable signal": an agent with no mechanism behind "accounts are the enable signal": an agent with no
homeserver and no account of its own has an empty set, so it gets no homeserver and no account of its own has an empty set, so it gets no
daemon, no path watcher and no injected MCP entry. That's how you give daemon, no path watcher and no injected MCP entry. That's how you give
an agent no matrix tools — it replaces the removed an agent no matrix tools. Setting
`services.hyperhive.agent.matrix.enable = false`, which now fails `services.hyperhive.agent.matrix.enable` fails evaluation with a
evaluation with a message saying so. Declaring an external account with message saying so. Declaring an external account with
its own `homeserver` is enough on its own; a hive homeserver isn't its own `homeserver` is enough on its own; a hive homeserver isn't
required. required.

View file

@ -103,9 +103,7 @@ there is no such identity to hold a file open for, so the daemon omits
anything task-shaped. anything task-shaped.
⚠️ This applies to every dispatch, not just role-bearing ones — no agent ⚠️ This applies to every dispatch, not just role-bearing ones — no agent
ships roles yet, so today it's the only path in real use. Before this fix ships roles yet, so today it's the only path in real use.
the no-role path put the task straight into the system prompt, same as
every `start` before roles existed at all.
### A role with no file refuses the call ### A role with no file refuses the call
@ -356,10 +354,8 @@ Everything else in claude's built-in set is absent, in particular the
tools that let a session act outside the run for which it began: peer and tools that let a session act outside the run for which it began: peer and
operator messaging, nested agents (including the stop verb, which takes operator messaging, nested agents (including the stop verb, which takes
an _agent_ id rather than a session), schedule and webhook creation, and an _agent_ id rather than a session), schedule and webhook creation, and
worktree switching. Before the harness passed this flag, a subagent worktree switching. `--allowedTools` would not achieve this exclusion on
reached all of them — `--dangerously-skip-permissions` had removed the its own: it approves prompts in advance rather than restricting anything.
only thing that would have asked, and `--allowedTools` would not have
helped: it approves prompts in advance rather than restricting anything.
One rule worth knowing before editing any of this: **the daemon never One rule worth knowing before editing any of this: **the daemon never
emits an empty `--tools` value**, and asserts rather than doing so. Not emits an empty `--tools` value**, and asserts rather than doing so. Not

View file

@ -123,7 +123,7 @@ the agent user and sets `0751`. The container has no user namespace, so
that uid is the host inode's owner. Don't add a host-side chown or chmod: that uid is the host inode's owner. Don't add a host-side chown or chmod:
two owners of one path revert each other. Until the container activates, two owners of one path revert each other. Until the container activates,
the dir is `0751 root`: nothing but root can plant a socket in it, and a the dir is `0751 root`: nothing but root can plant a socket in it, and a
legacy root-run harness can still bind. root-run harness can still bind.
<!-- vale write-good.Passive = YES --> <!-- vale write-good.Passive = YES -->

View file

@ -100,10 +100,7 @@ its own store identity so credentials never pass through a hive at all.
agent and service stanzas above) because a hive's own path names it, so agent and service stanzas above) because a hive's own path names it, so
scoping to the reader's own name costs nothing and drifts nowhere. That only scoping to the reader's own name costs nothing and drifts nowhere. That only
holds if a credential that must be one-per-hive is actually stored under the holds if a credential that must be one-per-hive is actually stored under the
hive kind — the matrix sender token was originally published to hive kind.
`swarm/services/matrix/sender-token`, under the **service** kind, and so was
readable by every hive though it belonged to only one. Making it per-hive meant
moving its path under the hive kind, not narrowing the service stanza's grant.
**A credential that must be one-per-hive goes under `Kind::Hive`**; putting it **A credential that must be one-per-hive goes under `Kind::Hive`**; putting it
under `Kind::Service` and expecting the grant to scope it — that's the mistake under `Kind::Service` and expecting the grant to scope it — that's the mistake
this note exists to stop. this note exists to stop.
@ -296,7 +293,7 @@ known operations; there is no arbitrary command pass-through:
| `DaemonReload` | `systemctl daemon-reload` | | `DaemonReload` | `systemctl daemon-reload` |
| `RunForgeAdmin` | `nixos-container run hive-forge -- runuser -u forgejo -- forgejo admin <args>` | | `RunForgeAdmin` | `nixos-container run hive-forge -- runuser -u forgejo -- forgejo admin <args>` |
| `ControlInfraContainer` | `systemctl <action> container@<container>.service` — the `InfraContainer` enum is the allowlist, and serde rejects unknown names at the wire boundary (`hive-c0re` has no variant, so no request can name it) | | `ControlInfraContainer` | `systemctl <action> container@<container>.service` — the `InfraContainer` enum is the allowlist, and serde rejects unknown names at the wire boundary (`hive-c0re` has no variant, so no request can name it) |
| `SyncAgentTmpfiles` | legacy: unlink `/etc/tmpfiles.d/hyperhive-agents.conf` and return `Ok`; kept one release for an older hive-c0re | | `SyncAgentTmpfiles` | unlink `/etc/tmpfiles.d/hyperhive-agents.conf` and return `Ok` |
| `SetAgentPaused` | create / remove the `<state>/<name>/harness/paused` marker that parks an agent's turn loop | | `SetAgentPaused` | create / remove the `<state>/<name>/harness/paused` marker that parks an agent's turn loop |
| `WriteAgentGithubToken` | write `0600` `github-token` into agent state dir | | `WriteAgentGithubToken` | write `0600` `github-token` into agent state dir |
| `RegisterCiRunner` | write `/run/hive-ci/runner-token` (host path, root-owned) then `systemctl --machine=hive-ci restart gitea-runner-hive.service`. Only the registration token crosses; the forge admin token never enters the container | | `RegisterCiRunner` | write `/run/hive-ci/runner-token` (host path, root-owned) then `systemctl --machine=hive-ci restart gitea-runner-hive.service`. Only the registration token crosses; the forge admin token never enters the container |

View file

@ -13,19 +13,16 @@ panel for flyouts and long content.
Preact component tree (`Header.tsx` + `StatusChips.tsx` + Preact component tree (`Header.tsx` + `StatusChips.tsx` +
`MetaNav.tsx` + `HeaderPill.tsx`, wired together in `Root.tsx`) — see `MetaNav.tsx` + `HeaderPill.tsx`, wired together in `Root.tsx`) — see
`frontend/packages/agent/src/components/`. This `frontend/packages/agent/src/components/`. This
section describes the rendered result, not the DOM ids the pre-Preact section describes the rendered result, not DOM ids — every element is
page used (there are none any more — every element is component Preact component output, not something a selector reaches by id.
output, not something a selector reaches by id).
**Fixed-overlay header** (`<header class="agent-header">`): frosted **Fixed-overlay header** (`<header class="agent-header">`): frosted
glass — `backdrop-filter: blur` lets scrolled terminal rows show glass — `backdrop-filter: blur` lets scrolled terminal rows show
through. Measures its own rendered height via `ResizeObserver` through. Measures its own rendered height via `ResizeObserver`
(`Header.tsx`) and writes it to a CSS custom property the content (`Header.tsx`) and writes it to a CSS custom property the content
below reads for its offset — `agent.css` used to bake in a fixed `6em` below reads for its offset, so content never overlaps the header
guess, which silently broke (content overlapping the header) the regardless of how many rows of badges/pills wrap onto an extra line
moment any row of badges/pills wrapped onto an extra line at some at a given viewport width. Two columns:
viewport width; measuring instead of guessing closes that bug class
structurally rather than for one specific trigger. Two columns:
<!-- vale write-good.Passive = NO --> <!-- vale write-good.Passive = NO -->
- **Agent icon** (`<img class="agent-icon">`): fixed-size square - **Agent icon** (`<img class="agent-icon">`): fixed-size square
@ -45,10 +42,9 @@ structurally rather than for one specific trigger. Two columns:
`ResizeObserver` measurement above exists to catch even when a `ResizeObserver` measurement above exists to catch even when a
layout choice reintroduces it). layout choice reintroduces it).
- **Pills cluster** (`.agent-header-pills`, right-aligned): status - **Pills cluster** (`.agent-header-pills`, right-aligned): status
badges (`StatusChips.tsx`) and flyout triggers together in one row — badges (`StatusChips.tsx`) and flyout triggers together in one row,
a deliberate choice (not the historical default) so the header reads so the header reads as one identity zone + one status/actions zone
as one identity zone + one status/actions zone rather than multiple rather than multiple separate clusters:
separate clusters:
- **Alive badge**: `● alive` (green) / `⊘ rate limited` (red) / - **Alive badge**: `● alive` (green) / `⊘ rate limited` (red) /
`◌ needs login` / `◌ logging in` / `○ offline` / `… connecting`. `◌ needs login` / `◌ logging in` / `○ offline` / `… connecting`.
- **State badge**: `💤 idle` / `🧠 thinking` / `📦 compacting` / - **State badge**: `💤 idle` / `🧠 thinking` / `📦 compacting` /
@ -89,23 +85,14 @@ structurally rather than for one specific trigger. Two columns:
(`GET /api/state`'s `links` field) also feeds (`GET /api/state`'s `links` field) also feeds
`DashboardState.links` for the dashboard card's icon strip — `DashboardState.links` for the dashboard card's icon strip —
`agent_links()` in hive-agent is the single source of truth for `agent_links()` in hive-agent is the single source of truth for
both. No separate overflow (`⋯`) menu exists any more — it used both. No overflow (`⋯`) menu exists: the dashboard link lives in
to hold exactly this dashboard link plus a rebuild-container action this 🔗 popover; the model/effort pickers are real badges above;
(mara: "remove rebuild button, move link to and `/new-session` / `/logout` are typed slash commands with their
dashboards into links menu") — rebuild had no real discoverability own type-twice confirm (see below) — a modal doesn't fit a
need of its own (the dashboard's own R3BU1LD button already covers text-input flow.
it) so it's gone outright, and the dashboard link moved here, - `/cancel` (slash command, below) is the only cancel-turn path; the
leaving nothing to justify a separate menu. Everything else that turn-loop state badge already shows `thinking` as the
used to live in the old overflow menu (model/effort pickers, discoverability cue.
new-session, logout) already had a better home before this: pickers
are real badges above, and `/new-session` / `/logout` are typed
slash commands with their own type-twice confirm (see below) — a
modal doesn't fit a text-input flow, and burying rare-but-important
actions in one flat menu was the design guide's own named
anti-example.
- No header cancel-turn button any more — `/cancel` (slash command,
below) is the only path; the turn-loop state badge already shows
`thinking` as the discoverability cue.
<!-- vale write-good.Passive = YES --> <!-- vale write-good.Passive = YES -->
Values throughout come from `GET /api/state`'s cold-load snapshot, Values throughout come from `GET /api/state`'s cold-load snapshot,
@ -179,11 +166,7 @@ from the harness-local store (same effect as `cancel_loose_end(kind:
"todo")`, just from the web UI instead of the agent's own tool calls). "todo")`, just from the web UI instead of the agent's own tool calls).
The todos flyout is the only per-agent flyout — there is no separate The todos flyout is the only per-agent flyout — there is no separate
"loose-ends" or "tasks" list. There used to be a note here about the "loose-ends" or "tasks" list.
`ask`/`answer` MCP tools having no inline answer form in this
terminal — that whole mechanism (the tools, the dashboard's questions
pane, the wire protocol) no longer exists, so there's
nothing left to render a form for.
## Live view ## Live view