hive-sh4re + docs: extract broker delivery/ack/requeue prose (#717 batch 1)
This commit is contained in:
parent
33683f2de2
commit
a443108be5
2 changed files with 55 additions and 47 deletions
|
|
@ -54,6 +54,43 @@ each frame carries a `seq` field for the snapshot-dedupe dance
|
|||
— change them in one place. The dashboard event vocabulary lives
|
||||
in `hive-c0re::dashboard_events::DashboardEvent`.
|
||||
|
||||
### Broker delivery + ack cycle
|
||||
|
||||
`AgentRequest::Recv` is the only path that delivers messages to an
|
||||
agent. Always returns a list (`Messages { messages }`) — empty when
|
||||
nothing's pending, single-pop when `max = None` (default 1, the
|
||||
single-message behaviour), batched up to `max` when caller asks for
|
||||
more (server-side cap is 32; values above clamp silently).
|
||||
`wait_seconds` long-polls for the first message; once one arrives —
|
||||
or one is already pending — the call drains up to `max` in total
|
||||
before returning, so a single `Recv` call coalesces a burst.
|
||||
|
||||
Per-row bookkeeping inside the broker:
|
||||
|
||||
- `delivered_at = NOW` set on every popped row.
|
||||
- Each recipient has an in-memory `unacked_ids` list of every row
|
||||
delivered since the last `AckTurn`.
|
||||
- `redelivered = true` on a row if `RequeueInflight` resurfaced it
|
||||
(the harness prepends a "may already be handled" hint when this
|
||||
flag is set so the per-message warning is visible).
|
||||
|
||||
`AgentRequest::AckTurn` closes out the in-memory list — the harness
|
||||
fires it after `TurnOutcome::Ok`, marking every message popped since
|
||||
the last ack as fully handled. Claude doesn't see this surface; it's
|
||||
strictly a harness↔broker pairing. On `TurnOutcome::Failed` the
|
||||
harness intentionally skips the ack so the unacked rows stay
|
||||
in-flight in the DB and get picked up by the next requeue sweep.
|
||||
|
||||
`AgentRequest::RequeueInflight` is the recovery pair: fired by the
|
||||
harness exactly once at boot, before the serve loop starts. Catches
|
||||
the crashed-mid-turn / OOM-killed / container-restarted cases where
|
||||
a previous harness session popped messages but never drove them to
|
||||
a clean turn-end. Resets `delivered_at` back to NULL on every
|
||||
unacked row (so the next `Recv` pops them again), and remembers
|
||||
each id in a per-recipient in-memory set so the next `Recv` can tag
|
||||
the row with `redelivered: true`. Idempotent + cheap when there's
|
||||
nothing in flight, so the at-boot fire is unconditional.
|
||||
|
||||
## Async forms
|
||||
|
||||
Dashboard + per-agent mutating forms carry `data-async`; a delegated
|
||||
|
|
|
|||
|
|
@ -241,26 +241,20 @@ pub struct InboxRow {
|
|||
pub in_reply_to: Option<i64>,
|
||||
}
|
||||
|
||||
/// One delivered message in a `Recv` response. The unified
|
||||
/// `Recv { max }` always returns a `Vec<DeliveredMessage>` — single
|
||||
/// pop = a one-element vec, batch = up to `max` elements, idle =
|
||||
/// empty. Each row carries the broker's id + redelivered flag so the
|
||||
/// harness can drive `AckTurn` and surface the "may already be
|
||||
/// handled" hint per-row.
|
||||
/// One delivered message in a `Recv` response.
|
||||
/// See `docs/conventions.md::Broker delivery + ack cycle` for the
|
||||
/// full delivery/ack/requeue story.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeliveredMessage {
|
||||
pub from: String,
|
||||
pub body: String,
|
||||
/// Broker row id, mirrored from the `Delivery` struct. Opaque to
|
||||
/// claude but tracked by the harness so the broker's in-memory
|
||||
/// unacked list can be drained on `AckTurn`. Marked `default` for
|
||||
/// wire backwards-compat — pre-feature peers parse to 0.
|
||||
/// Broker row id, tracked by the harness for `AckTurn`. Opaque to
|
||||
/// claude. `default` for wire backwards-compat.
|
||||
#[serde(default)]
|
||||
pub id: i64,
|
||||
/// `true` when this row was previously popped, never acked, and
|
||||
/// resurfaced by `RequeueInflight`. The format helper prepends the
|
||||
/// "may already be handled" hint to the rendered body so claude
|
||||
/// sees the warning per-message in the batch.
|
||||
/// `true` if this row was resurfaced by `RequeueInflight` (previously
|
||||
/// popped, never acked). Formatter prepends a "may already be handled"
|
||||
/// hint when set.
|
||||
#[serde(default)]
|
||||
pub redelivered: bool,
|
||||
/// Row-id of the message this is a reply to, if any.
|
||||
|
|
@ -371,22 +365,12 @@ pub enum AgentRequest {
|
|||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
in_reply_to: Option<i64>,
|
||||
},
|
||||
/// Pop pending messages from this agent's inbox. Always returns
|
||||
/// a list (`Messages { messages }`) — empty when nothing's
|
||||
/// pending. `max` caps the batch size (default 1 = single-message
|
||||
/// behaviour, server-side cap 32). `wait_seconds` long-polls for
|
||||
/// the first message; once one arrives (or one is already
|
||||
/// pending), the call drains up to `max` in total before
|
||||
/// returning. Same delivery + ack bookkeeping per row as before:
|
||||
/// `delivered_at = NOW`, tracked on the per-recipient
|
||||
/// `unacked_ids` list (the next `AckTurn` closes them out), and
|
||||
/// each row carries `redelivered = true` if `RequeueInflight`
|
||||
/// resurfaced it.
|
||||
/// Pop pending messages from this agent's inbox.
|
||||
/// Delivery + ack cycle: see
|
||||
/// `docs/conventions.md::Broker delivery + ack cycle`.
|
||||
Recv {
|
||||
#[serde(default)]
|
||||
wait_seconds: Option<u64>,
|
||||
/// Maximum number of messages to pop. None = 1 (single).
|
||||
/// Server-side cap is 32; values above clamp silently.
|
||||
#[serde(default)]
|
||||
max: Option<u32>,
|
||||
},
|
||||
|
|
@ -498,27 +482,14 @@ pub enum AgentRequest {
|
|||
/// row. The manager surface uses the same wire variant but
|
||||
/// accepts any id.
|
||||
CancelLooseEnd { kind: CancelLooseEndKind, id: i64 },
|
||||
/// Mark every message popped by this agent since the last `AckTurn`
|
||||
/// as fully handled. Fired by the harness after `TurnOutcome::Ok`
|
||||
/// — claude doesn't see this surface, it's harness↔broker only.
|
||||
/// On `TurnOutcome::Failed` the harness intentionally skips this
|
||||
/// call, so the unacked rows stay in-flight in the DB and get
|
||||
/// requeued by the next `RequeueInflight` on harness boot. Tracks
|
||||
/// the popped-id list in-memory on the broker side; no payload
|
||||
/// needed (the broker knows which ids it handed to this
|
||||
/// recipient).
|
||||
/// Mark every message popped since the last `AckTurn` as handled.
|
||||
/// Harness↔broker pairing fired after `TurnOutcome::Ok`. See
|
||||
/// `docs/conventions.md::Broker delivery + ack cycle`.
|
||||
AckTurn,
|
||||
/// Requeue every message the broker handed to this agent that
|
||||
/// never got acked. Fired by the harness exactly once at boot,
|
||||
/// before entering the serve loop — catches the
|
||||
/// crashed-mid-turn / OOM-killed / container-restarted cases
|
||||
/// where a previous harness session popped messages but never
|
||||
/// drove them to a clean turn-end. Resets `delivered_at` on each
|
||||
/// row back to NULL (so the next `Recv` pops it) and remembers
|
||||
/// the id in a per-recipient in-memory set so the next `Recv`
|
||||
/// can tag the message with `redelivered: true` (the harness
|
||||
/// then prepends a "may already be handled" hint to the wake
|
||||
/// prompt). Idempotent + cheap when there's nothing in flight.
|
||||
/// Requeue every popped-but-unacked message back into the inbox.
|
||||
/// Harness fires this once at boot to recover from
|
||||
/// crashed-mid-turn sessions. See
|
||||
/// `docs/conventions.md::Broker delivery + ack cycle`.
|
||||
RequeueInflight,
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue