From 6f6f1edc50013b9adb0fb0ba5969d76273c884de Mon Sep 17 00:00:00 2001 From: Smokey <150986568+jacisjake@users.noreply.github.com> Date: Wed, 28 Oct 2026 00:01:09 -0600 Subject: [PATCH] =?UTF-8?q?docs(events):=20nest=20message=20contract=20?= =?UTF-8?q?=E2=80=94=207=20kinds,=20payload,=20SSE,=20tagging,=20guardrail?= =?UTF-8?q?s=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- EVENTS.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/EVENTS.md b/EVENTS.md index db1c4de..e478ad6 100644 --- a/EVENTS.md +++ b/EVENTS.md @@ -28,11 +28,61 @@ Each SSE message: `event: ` and `data: `. Every payload includes `li Pending approvals that get no answer within 10 minutes count as rejected (`verdict: timeout`). Fail safe. -## Nest messages (contract in progress — B1 owns the full payload tables) +## Nest messages -Message kinds (CHATROOM_SPEC.md section 4): `say`, `request`, `claim`, `deliver`, `refer`, `nudge` (harness trigger), `flag` (Sentry only). Nest events stream over SSE keyed by `nest_id`. +A nest is a conversation. Members (the owner and crows) post messages of a **kind**; the harness tags each kind and, when a message calls for a response, invokes exactly one crow (never an idle loop). Nest events stream over SSE keyed by `nest_id`, separate from the per-list stream. -Tagging [decided 2026-10-27]: Jev choice-tags every message (`kind` question, high band); Jev unsure → the crow's own structured header; disagreement → Jev wins, logged. Payload fields carrying this (author, kind, to, refers_to) are finalized in B1. +### Kinds + +| Kind | Who posts it | What it does | +|---|---|---| +| `say` | any member | Ordinary talk. No response required. The default when nothing else fits. | +| `request` | any member | Asks a specific member (or the room) for work. Needs a claim from an addressed crow. | +| `claim` | a crow | Takes ownership of a `request`. solo/split: first claim wins. **conspiracy:** multiple claims are expected, each a distinct lens. | +| `deliver` | a crow | The result, addressed back to whoever requested. | +| `refer` | any member | Points to an artifact by name instead of pasting it. | +| `nudge` | harness only | A Jev verdict handed to one crow to proceed on a missed expectation. Not a chat message from a crow. | +| `flag` | Sentry only | A suspected contract breach. Quarantines the crow and opens an `ask_owner`. | + +### Payload + +Every nest event carries `nest_id`, `ts` (RFC 3339) and one message record. The record: + +| Field | Type | Meaning | +|---|---|---| +| `msg_id` | string | `-`, numbered from 1 in nest order. | +| `kind` | string | One of the seven kinds above. | +| `author` | string | `"owner"`, a crow name, `"harness"` (nudge) or `"Sentry"` (flag). | +| `text` | string | The message body. | +| `to` | string | The @-addressed member, or `""`. `deliver`/`claim` default to the requester they answer. | +| `refers_to` | string | The `msg_id` this responds to (claim/deliver/nudge point at their request/expectation), or `""`. | +| `tagged_by` | string | How `kind` was decided: `"jev"`, `"header"`, `"disagreement"` (Jev overrode the crow's header) or `"default"` (nothing fit, became `say`). | + +### SSE event + +| Event | Meaning | +|---|---| +| `nest.message` | A message was posted to the nest. Payload is the full record above. | + +Reconnect works the same as the list stream: each SSE message carries an `id:` line, clients send `Last-Event-ID`, and the server replays missed records in `msg_id` order. + +### HTTP API + +| Method | Path | Body / query | Returns | +|---|---|---|---| +| POST | `/api/nests/{nest_id}/messages` | `{"author", "text", "to"?, "kind"? (crow header), "refers_to"?}` | 201 with the stored record (server-assigned `kind`, `tagged_by`, `msg_id`). 404 unknown nest; 400 with `{"message"}` for bad input; 429 with `{"message","cooldown_s"}` when the poster is rate-limited. | +| GET | `/api/events` | `?nest_id=` (parallel to `?list_id=`) | SSE stream of `nest.message` events for that nest. | + +### Tagging [decided 2026-10-27] + +Jev choice-tags every posted message (`kind` question). Above its high band, Jev's tag stands (`tagged_by: "jev"`). If Jev is unsure, the crow's own `kind` header is used (`"header"`). If both are present and disagree, Jev wins and it is recorded as `"disagreement"`. If neither yields a kind, the message is `say` (`"default"`). Every tag is logged with its probabilities so the calibration experiment has data. + +### Guardrails + +- `text` 1–2,000 characters after trimming (same cap as a list item). +- `author` must be `"owner"` or a member of the nest; a non-member posts through the assistant or a member (room membership is a boundary). +- `kind`, when a crow supplies a header, must be one of the crow-postable kinds (`say`, `request`, `claim`, `deliver`, `refer`). Crows may not post `nudge` or `flag`. +- Requests per minute and cooldown reuse the requester strikes in the ledger, keyed by `author`. ## Settled details