docs/hive-c0re: fix ask/answer removal doc gaps argus caught on #3741

This commit is contained in:
damocles 2026-08-30 03:02:31 +02:00
commit 16ac84ca63
10 changed files with 37 additions and 49 deletions

View file

@ -32,7 +32,7 @@ hand-maintained per-file tree drifts out of sync with the code.
user). `src/main.rs` is the `hive-c0re` binary — **daemon-only** user). `src/main.rs` is the `hive-c0re` binary — **daemon-only**
(`serve` + the periodic vacuum/sweep loops); the operator CLI lives in (`serve` + the periodic vacuum/sweep loops); the operator CLI lives in
the separate `hivectl` crate, which talks to the daemon over the host the separate `hivectl` crate, which talks to the daemon over the host
admin socket. Owns the sqlite broker, approval + question + reminder + admin socket. Owns the sqlite broker, approval + reminder +
schedule queues, the meta flake, lifecycle (`nixos-container` schedule queues, the meta flake, lifecycle (`nixos-container`
shellouts), gateway / forge / matrix provisioning, per-container stats, shellouts), gateway / forge / matrix provisioning, per-container stats,
and the axum operator dashboard (`dashboard.rs`). Largest crate. and the axum operator dashboard (`dashboard.rs`). Largest crate.
@ -120,7 +120,7 @@ hand-maintained per-file tree drifts out of sync with the code.
`forge_notify` are the built-in ones, but any user-configured MCP server `forge_notify` are the built-in ones, but any user-configured MCP server
can push todos here too, nothing restricts the `subsystem` set. Carries can push todos here too, nothing restricts the `subsystem` set. Carries
the loose-ends-v2 **todo** ops plus the loose-ends-v2 **todo** ops plus
harness-local reminders + questions. ⚠️ Distinct from harness-local reminders. ⚠️ Distinct from
`hive-core-agent-sock` above: **this socket never leaves the container** `hive-core-agent-sock` above: **this socket never leaves the container**
and `hive-c0re` is not in the path at all — no broker round-trip, no and `hive-c0re` is not in the path at all — no broker round-trip, no
long-poll, no marker files. long-poll, no marker files.

View file

@ -11,8 +11,7 @@ durable identity, and stop them from eating production. hyperhive is
the substrate. the substrate.
- identity = unix socket - identity = unix socket
- communication = sqlite-backed broker (`send` / `recv` / `ask` / - communication = sqlite-backed broker (`send` / `recv` / `remind`)
`answer` / `remind`)
- config = git (manager proposes, operator approves, deploys land as - config = git (manager proposes, operator approves, deploys land as
tagged commits) tagged commits)
- blast radius = container - blast radius = container

View file

@ -629,6 +629,7 @@ root agent. Variants (`hive_sh4re::manager::HelperEvent`):
- `NeedsUpdate { agent }` — sub-agent's recorded flake rev is - `NeedsUpdate { agent }` — sub-agent's recorded flake rev is
stale. The root agent calls `update(name)` to rebuild — idempotent, stale. The root agent calls `update(name)` to rebuild — idempotent,
no approval required. no approval required.
The remaining lower-urgency lifecycle notices — `Rebuilt`, `Killed`, The remaining lower-urgency lifecycle notices — `Rebuilt`, `Killed`,
`Destroyed`, `NeedsLogin`, `LoggedIn`, `ConfigReady` — are "FYI, check `Destroyed`, `NeedsLogin`, `LoggedIn`, `ConfigReady` — are "FYI, check
when convenient" events with no reason to drive an immediate turn, so when convenient" events with no reason to drive an immediate turn, so

View file

@ -23,8 +23,7 @@ preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and
## Core tools (always available) ## Core tools (always available)
**Messaging** (`messaging` group): `send(to, body, in_reply_to?)`, **Messaging** (`messaging` group): `send(to, body, in_reply_to?)`,
`recv(max?)`, `ask(question, options?, multi?, `recv(max?)`, `ack_until(up_to)`.
ttl_seconds?, to?)`, `answer(id, answer)`, `ack_until(up_to)`.
- `send` — message a peer (logical name) or the operator - `send` — message a peer (logical name) or the operator
(`to: "operator"`). Use `to: "<parent>"` to address the topology (`to: "operator"`). Use `to: "<parent>"` to address the topology
@ -48,13 +47,6 @@ ttl_seconds?, to?)`, `answer(id, answer)`, `ack_until(up_to)`.
other turn: if the context crossed the watermark the harness runs a other turn: if the context crossed the watermark the harness runs a
notes-checkpoint turn and then `/compact`. Compacting before shutdown notes-checkpoint turn and then `/compact`. Compacting before shutdown
keeps a later cold start cheap instead of re-uploading a huge transcript. keeps a later cold start cheap instead of re-uploading a huge transcript.
- `ask` — surface a structured question to the operator (default) or
a peer agent (`to: "<agent>"`). Non-blocking — returns a question
id; the answer arrives as a `question_answered` system event in the
asker's inbox. `options` is advisory; `multi=true` renders as
checkboxes; `ttl_seconds` auto-cancels with answer `[expired]`.
- `answer` — respond to a `question_asked` event routed to this
agent. Strict authorisation: only the declared target can answer.
- `ack_until(up_to)` — bulk-mark inbox rows handled: every row with - `ack_until(up_to)` — bulk-mark inbox rows handled: every row with
broker id `<= up_to` is stamped as acked in a single UPDATE. broker id `<= up_to` is stamped as acked in a single UPDATE.
Recipient-scoped (agents can only ack their own rows). Use when a Recipient-scoped (agents can only ack their own rows). Use when a
@ -64,14 +56,12 @@ ttl_seconds?, to?)`, `answer(id, answer)`, `ack_until(up_to)`.
Transient pings (sentinel id 0) have nothing to ack and show no marker. Transient pings (sentinel id 0) have nothing to ack and show no marker.
**System messages** (from sender `system`): the higher-urgency **System messages** (from sender `system`): the higher-urgency
lifecycle + Q&A events (`hive_sh4re::manager::HelperEvent`) are lifecycle events (`hive_sh4re::manager::HelperEvent`) are
delivered as regular inbox messages (same `recv` path; body is a JSON delivered as regular inbox messages (same `recv` path; body is a JSON
object with an `event` discriminant field). The **submitting agent** object with an `event` discriminant field). The **submitting agent**
(the root agent for top-level containers; an agent with the `approvals` (the root agent for top-level containers; an agent with the `approvals`
tool group for its own subtree) receives `container_crash`, tool group for its own subtree) receives `container_crash`,
`needs_update`, and `approval_resolved` this way. Any agent receives `needs_update`, and `approval_resolved` this way. The remaining, lower-urgency lifecycle
Q&A events when it is the declared target (`question_asked`) or the
asker (`question_answered`). The remaining, lower-urgency lifecycle
notices — `spawned`, `rebuilt`, `killed`, `destroyed`, `needs_login`, notices — `spawned`, `rebuilt`, `killed`, `destroyed`, `needs_login`,
`logged_in`, `config_ready` — skip the inbox entirely: they land as `logged_in`, `config_ready` — skip the inbox entirely: they land as
todos on the submitting agent's in-container todo socket instead todos on the submitting agent's in-container todo socket instead
@ -86,18 +76,19 @@ payload shapes and routing logic in
`cancel_loose_end(kind, id)`, `remind(message, delay_seconds? | `cancel_loose_end(kind, id)`, `remind(message, delay_seconds? |
at_unix_timestamp?)`. at_unix_timestamp?)`.
- `get_loose_ends(agent?)` — list pending questions (asked/owed), - `get_loose_ends(agent?)` — list scheduled reminders, pending
scheduled reminders, and active local tasks published by external MCP approvals you submitted, and active local tasks published by
daemons (e.g. running bash tasks from `hive-bash-daemon`). Each row external MCP daemons (e.g. running bash tasks from
carries an id + kind for `cancel_loose_end`. Omit `agent` to list `hive-bash-daemon`). Each row carries an id + kind for
your own threads. Pass `agent: "<name>"` to inspect a direct child `cancel_loose_end`. Omit `agent` to list your own threads. Pass
agent (always accessible per topology enforcement); non-children `agent: "<name>"` to inspect a direct child agent (always accessible
require the `query_agent_state` capability. The `"*"` hive-wide per topology enforcement); non-children require the
query is not available on the agent socket. `query_agent_state` capability. The `"*"` hive-wide query is not
- `cancel_loose_end` — withdraw a `question` (posts `[cancelled by available on the agent socket.
<self>]`), hard-delete a `reminder`, or cancel a pending `approval` - `cancel_loose_end` — hard-delete a `reminder`, cancel a pending
row. Agents may only cancel rows they own; the `approval` kind is `approval` row, or clear a `todo` row (loose-ends-v2). Agents may
further restricted to the root agent (`ruth`) server-side. only cancel rows they own; the `approval` kind is further restricted
to the root agent (`ruth`) server-side.
- `remind` — schedule a reminder in this agent's own inbox. Large - `remind` — schedule a reminder in this agent's own inbox. Large
payloads spill to `/agents/<self>/state/reminders/`. Pending count payloads spill to `/agents/<self>/state/reminders/`. Pending count
capped at 50 per agent (`HIVE_REMIND_MAX_PENDING_PER_AGENT`). capped at 50 per agent (`HIVE_REMIND_MAX_PENDING_PER_AGENT`).

View file

@ -152,7 +152,7 @@ fn msg_id_tag(id: i64) -> String {
} }
/// Hard cap on how many individual `Todo` lines `render_loose_ends` will /// Hard cap on how many individual `Todo` lines `render_loose_ends` will
/// emit. Approvals/questions/reminders stay naturally bounded (they're /// emit. Approvals/reminders stay naturally bounded (they're
/// triaged interactively and don't self-multiply), but todos are pushed by /// triaged interactively and don't self-multiply), but todos are pushed by
/// unattended producers — matrix/bash/forge are the built-in ones, but any /// unattended producers — matrix/bash/forge are the built-in ones, but any
/// user-configured MCP server can push its own too — an agent that goes a long /// user-configured MCP server can push its own too — an agent that goes a long

View file

@ -33,7 +33,7 @@ const REMINDER_BATCH_LIMIT: u64 = 100;
/// Poll interval — matches the old c0re `POLL_INTERVAL`. /// Poll interval — matches the old c0re `POLL_INTERVAL`.
const POLL_INTERVAL: Duration = Duration::from_secs(5); const POLL_INTERVAL: Duration = Duration::from_secs(5);
/// Same cap the broker used to enforce on `send`/`ask`/`remind` bodies /// Same cap the broker enforces on `send`/`remind` bodies
/// (`hive-c0re`'s `agent_config::limits::MESSAGE_MAX_BYTES`, not /// (`hive-c0re`'s `agent_config::limits::MESSAGE_MAX_BYTES`, not
/// reachable from here — hive-agent doesn't depend on hive-c0re). /// reachable from here — hive-agent doesn't depend on hive-c0re).
/// Duplicated rather than shared: this is the last remaining reminder /// Duplicated rather than shared: this is the last remaining reminder

View file

@ -1,7 +1,7 @@
# hive-c0re # hive-c0re
The unprivileged host daemon (runs as `hive-core`). Owns the sqlite The unprivileged host daemon (runs as `hive-core`). Owns the sqlite
broker, the approval/question/schedule queues, the generic job-DAG broker, the approval/reminder/schedule queues, the generic job-DAG
queue, container lifecycle, gateway/forge/matrix provisioning, queue, container lifecycle, gateway/forge/matrix provisioning,
per-container stats, and the axum operator dashboard. Largest crate in per-container stats, and the axum operator dashboard. Largest crate in
the workspace — bin-only, no separate lib. the workspace — bin-only, no separate lib.
@ -24,7 +24,7 @@ module's own `//!` doc-comment for real detail, don't expect this file
to track it: to track it:
- **`dashboard/`** — the operator dashboard (containers, approvals, - **`dashboard/`** — the operator dashboard (containers, approvals,
schedules, questions, logs, topology). schedules, logs, topology).
- **`job_queue/`** — the job-DAG queue + desired-state reconciliation - **`job_queue/`** — the job-DAG queue + desired-state reconciliation
(`docs/coordinator.md`). (`docs/coordinator.md`).
- **`lifecycle/`** — `nixos-container` lifecycle + per-agent config - **`lifecycle/`** — `nixos-container` lifecycle + per-agent config

View file

@ -10,8 +10,8 @@
//! about it — oversized reminder bodies get persisted to disk //! about it — oversized reminder bodies get persisted to disk
//! transparently and the inbox sees a pointer. //! transparently and the inbox sees a pointer.
/// Per-message body cap. Applies to `send`, `ask` question text, /// Per-message body cap. Applies to `send` bodies and the stored
/// `answer` body, and the stored inline form of a reminder. 4 KiB /// inline form of a reminder. 4 KiB
/// catches the bulk of conversational overflow (status reports, /// catches the bulk of conversational overflow (status reports,
/// bullet-list summaries, short proposals) while staying small /// bullet-list summaries, short proposals) while staying small
/// enough that a backed-up inbox of ~10 unread messages only adds /// enough that a backed-up inbox of ~10 unread messages only adds
@ -112,8 +112,8 @@ mod tests {
#[test] #[test]
fn label_threads_through() { fn label_threads_through() {
let err = check_size("question", &"x".repeat(MESSAGE_MAX_BYTES + 1)).unwrap_err(); let err = check_size("broadcast", &"x".repeat(MESSAGE_MAX_BYTES + 1)).unwrap_err();
assert!(err.starts_with("question body too long")); assert!(err.starts_with("broadcast body too long"));
} }
#[test] #[test]

View file

@ -2,17 +2,14 @@
//! assembles a `Vec<LooseEnd>` for either a single agent (`for_agent`) or //! assembles a `Vec<LooseEnd>` for either a single agent (`for_agent`) or
//! the whole hive (`hive_wide`). `Request::GetLooseEnds` from either the //! the whole hive (`hive_wide`). `Request::GetLooseEnds` from either the
//! agent or manager socket lands here so the routing logic + age-seconds //! agent or manager socket lands here so the routing logic + age-seconds
//! derivation stay in one place. Reminders AND questions are agent-local //! derivation stay in one place. Reminders are agent-local (in-container
//! (in-container stores, `hive-agent::reminders` / `hive-agent::questions`) //! `hive-agent::reminders` store) and never sourced from here. The
//! and no longer sourced from here (loose-ends-v2's questions phase) — //! `ask`/`answer` MCP tools, their wire protocol (`hive-c0re::questions`,
//! c0re remains the `Ask`/`Answer` routing + delivery rendezvous //! `stores::operator_questions`), and the operator dashboard's questions
//! (`coord.questions`), it just isn't asked for the *pending-view* //! pane have all been removed entirely — this file never had a
//! rendering anymore. ⚠️ The operator dashboard no longer has any //! `Question` loose-end path to begin with (it only ever emitted
//! question-pending view either — its whole questions pane, the //! `PendingMessages`/`Approval`), so nothing here changed shape when
//! `pending_all()`/`recent_answered_all()` reads that fed it, and the //! that removal landed.
//! `/api/answer-question`/`/api/cancel-question` endpoints are gone
//! (removed along with the rest of the dashboard's ask/answer surface).
//! An operator-targeted `ask()` now has no reader anywhere.
//! //!
//! Call frequency is low (an agent doing self-introspection between //! Call frequency is low (an agent doing self-introspection between
//! turns), so the sweep happens fresh every time — no caching, no //! turns), so the sweep happens fresh every time — no caching, no

View file

@ -15,7 +15,7 @@ for this socket.
## Not to be confused with `hive-agent-sock` ## Not to be confused with `hive-agent-sock`
This is the **host-served** protocol: the harness talks *out* to `hive-c0re` This is the **host-served** protocol: the harness talks *out* to `hive-c0re`
over `/run/hive/mcp.sock` (broker sends, approvals, questions, lifecycle). over `/run/hive/mcp.sock` (broker sends, approvals, lifecycle).
`hive-agent-sock` is the separate *in-container* socket the harness serves to `hive-agent-sock` is the separate *in-container* socket the harness serves to
its own local producers — that one never leaves the container. See its own local producers — that one never leaves the container. See
`docs/boundary.md` for the socket topology. `docs/boundary.md` for the socket topology.