Merge pull request #15 from jacisjake/task/8-events-nest-contract

(#8) EVENTS.md nest message contract
This commit was merged in pull request #15.
This commit is contained in:
jacisjake authored and GitHub committed 2026-10-28 00:09:36 -06:00
commit 6e8b7764d2
1 file changed
+53 -3
+53 -3
View File
@@ -28,11 +28,61 @@ Each SSE message: `event: <name>` and `data: <JSON>`. Every payload includes `li
Pending approvals that get no answer within 10 minutes count as rejected (`verdict: timeout`). Fail safe. 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 | `<nest_id>-<n>`, 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=<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 ## Settled details