docs(turn-loop): document ack_until tool + [msg #id] markers in recv/wake prompt

ack_until was added to the messaging group by the ack_until feature
but was missing from the turn-loop.md MCP surface documentation
entirely. Add it to:

- Messaging group signature list
- recv bullet: mention that each row is prefixed with [msg #<id>]
  so agents know what id to pass to ack_until
- New ack_until bullet explaining recipient-scoped bulk-triage use case
- Wake prompt section: mention the [msg #<id>] prefix and its purpose
This commit is contained in:
iris 2026-07-02 14:43:12 +02:00 committed by mara
commit 0de8c37233

View file

@ -253,10 +253,13 @@ agent's durable bind-mounted dir. CLAUDE.md auto-load walks
upward from `/state` — drop a per-agent CLAUDE.md there if you
want long-term hints that survive destroy/recreate.
The wake prompt is intentionally minimal: just the popped message's
`from`/`body`, plus an inline `({unread} more pending — drain via
…)` hint when `unread > 0`. Claude drives any further `recv`/`send`
itself via the embedded MCP server.
The wake prompt is intentionally minimal: the popped message's
`from`/`body`, prefixed with a `[msg #<id>]` broker-row-id marker
(so the agent knows what id to pass to `ack_until` for bulk triage;
transient pings show no marker because their sentinel id 0 has nothing
to ack), plus an inline `({unread} more pending — drain via …)` hint
when `unread > 0`. Claude drives any further `recv`/`send` itself via
the embedded MCP server.
Whenever hive-c0re starts / restarts / rebuilds a container, it
also drops a `system` message into the agent's inbox via
@ -502,7 +505,7 @@ preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and
**Messaging** (`messaging` group): `send(to, body, in_reply_to?)`,
`recv(wait_seconds?, max?)`, `ask(question, options?, multi?,
ttl_seconds?, to?)`, `answer(id, answer)`.
ttl_seconds?, to?)`, `answer(id, answer)`, `ack_until(up_to)`.
- `send` — message a peer (logical name) or the operator
(`to: "operator"`). Use `to: "<parent>"` to address the topology
@ -518,7 +521,9 @@ ttl_seconds?, to?)`, `answer(id, answer)`.
immediately. Positive value parks the turn up to that many seconds
(cap 180) — incoming messages wake instantly. `max` (default 1, cap
32) drains up to N rows; `wait_seconds` applies to the first, then
drains up to `max` total. **Graceful shutdown**: when the harness
drains up to `max` total. Each returned row is prefixed 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.
This unmissably directs the agent to flush durable state (`/state`
@ -534,6 +539,13 @@ ttl_seconds?, to?)`, `answer(id, answer)`.
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
broker id `<= up_to` is stamped 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.
**Inbox** (`inbox` group): `get_loose_ends(agent?)`,
`cancel_loose_end(kind, id)`, `remind(message, delay_seconds? |