Skip to content

Commit d952af7

Browse files
committed
Update docs: dispatch-frame threading and the mailbox fan-out's honest window (CL-7450)
Documents the dispatched frame's own Message-ID/In-Reply-To/References (finding 1) and rewrites the fan-out-failure paragraph that claimed "nothing has seen it yet" before the delete: chat and @corbits/mailbox are separate Postgres handles in the hub's composition, so a concurrent GET of the timeline in the window between the row insert and a fan-out failure's delete can read it, even though no chat.message publish (and so no live client) does. Notes the single refId a MailboxFanoutFailedError now carries through to the send route instead of a second report.
1 parent 8c756b5 commit d952af7

1 file changed

Lines changed: 27 additions & 6 deletions

File tree

‎docs/CHAT.md‎

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ platform's mail-send shape — a single text part rides as bare mail content;
6868
anything else becomes a list of `text/plain`/`application/json` MIME
6969
attachments — and that encoding is confined to the dispatch seam.
7070

71+
The dispatched frame itself carries RFC 5322 threading (CL-7450): its
72+
`Message-ID` is `mailMessageIdFor(sourceMessageId, domain)` — the same
73+
derivation `mailboxFanOutForSend` already stamped for that row, so the two
74+
always agree — and `In-Reply-To`/`References` name that row's own parent
75+
chain (`mailAncestryOf` in `./threads.ts`). This is what lets a reply's own
76+
`In-Reply-To` correlate back to the exact row that triggered the turn,
77+
independent of the process-local `turnMailCorrelation` bookkeeping
78+
`dispatchTurn` also does. Degrades to unthreaded, exactly as before, on a
79+
composition with no mailbox domain to derive it from.
80+
7181
## Every human's mailbox gets a copy (CL-7450)
7282

7383
`sendWorkbenchMessage` runs a strict order: store the row -> stamp its
@@ -76,12 +86,23 @@ RFC 5322 `Message-ID` -> fan the message into every human participant's
7686
live stream -> dispatch to whichever agents the message names. The mailbox
7787
fan-out runs BEFORE the row is published and BEFORE any agent is asked for
7888
a turn: a fan-out failure fails the send outright — the just-inserted row is
79-
deleted (nothing has been published yet, so no client has seen it, and a
80-
retry of the same send does not then duplicate it) and the failure is
81-
rethrown to the caller, never swallowed into a send that looks fully
82-
delivered when it wasn't. Only once the fan-out batch has committed does the
83-
row publish, giving a client-visible bubble and a durable mailbox copy the
84-
same all-or-nothing guarantee.
89+
deleted and the failure is rethrown to the caller, never swallowed into a
90+
send that looks fully delivered when it wasn't. Only once the fan-out batch
91+
has committed does the row publish, giving a client-visible bubble and a
92+
durable mailbox copy the same all-or-nothing guarantee.
93+
94+
This is not transactional across the row insert and the delete: the row
95+
write and the mailbox batch are two separate commits (chat and
96+
`@corbits/mailbox` are separate Postgres handles in the hub's own
97+
composition), so a concurrent `GET` of the timeline in the window between
98+
the insert and a fan-out failure's delete CAN read the row before it is
99+
removed — no `chat.message` publish has happened yet (so no live client
100+
sees it), but a poller hitting the REST list endpoint in that same window
101+
can. The window is real and bounded to that one gap, not claimed away. A
102+
fan-out failure is reported once, under one `refId`, as
103+
`MailboxFanoutFailedError` (`./mailbox-fanout.ts`) — the send route
104+
answers with that same ref rather than letting the hub's generic
105+
unhandled-route-error handler report it again under a second one.
85106

86107
The fan-out itself writes an "outbound" row in the sender's own mailbox and
87108
an "inbound" row in every other human participant's, all sharing the

0 commit comments

Comments
 (0)