From 37fbce9b0ec215692814b527ae8895cce4635c14 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Thu, 24 Sep 2026 23:44:47 -0700 Subject: [PATCH] docs: keep README to the seam; persist narrative moves to CONTRIBUTING The README says what authorizeSender and upstream are; how the two writes interact is contributor material. Source comments describe current behavior only. Closes CL-9216, CL-9220, CL-9238. --- CONTRIBUTING.md | 17 ++++++++++++++--- README.md | 2 +- src/frame.test.ts | 7 ++----- src/migrations.ts | 2 +- src/mount.ts | 5 +---- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1bddeb2..9d2d1ba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,9 +51,20 @@ Shipped migrations are immutable. Each ledger row carries a checksum of the migr statements, so editing one that has already been applied fails loudly on the next boot rather than letting fresh and existing databases diverge. Add a new migration instead. -`schema.ts` and `migrations.ts` must agree statement for statement — the drizzle table -object is a public export, and `src/schema-ddl-parity.test.ts` diffs the two against a -live database. Change one, change the other, in the same commit. +`schema.ts` and `migrations.ts` must agree statement for statement — the runtime +queries read through the drizzle table object, and `src/schema-ddl-parity.test.ts` +diffs the two against a live database. Change one, change the other, in the same commit. + +## Agent-originated mail + +`createMailboxPersist` wraps the host's own persist function. `authorizeSender` is the +host's check that a sender address is one it recognizes right now: a hub answers by +looking up the tenant a mailbox-routable address (a person, or a live agent run) +currently resolves to, and refuses anything else. `upstream` is the host's existing +mail-persist path. The wrapper calls it unconditionally and layers the durable inbox +write on top, so a transport failure never costs a recipient the copy that makes the +message readable later. The host calls the wrapped function wherever it delegates an +outbound frame; it does both writes. ## Pull requests diff --git a/README.md b/README.md index 91104e8..82ef2e2 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ export function wrapPersistMail( } ``` -`authorizeSender` is the host's own check that a sender address is one it recognizes right now — a hub answers by looking up the tenant a mailbox-routable address (a person, or a live agent run) currently resolves to, and refusing anything else. `upstream` is the host's own pre-existing mail-persist path — the write it already made before this package existed; `createMailboxPersist` calls it unconditionally and layers the durable inbox write on top, so a transport failure never costs a recipient the copy that makes the message readable later. Call the wrapped `persistMail` wherever the host currently delegates an outbound frame; it does both writes. +`authorizeSender` says whether a sender address is one the host recognizes right now, and which tenant it belongs to. `upstream` is the host's existing persist path; it always runs. See [CONTRIBUTING.md](./CONTRIBUTING.md#agent-originated-mail) for how the two writes interact. ## How it works diff --git a/src/frame.test.ts b/src/frame.test.ts index a6e8cfb..eef7f91 100644 --- a/src/frame.test.ts +++ b/src/frame.test.ts @@ -26,8 +26,6 @@ function headers(raw: Uint8Array) { } describe("buildMailFrame headers", () => { - // Nothing used to assert a single header value this function produces, - // which is how a doubly-bracketed Message-ID shipped unnoticed. test("emits every header it promises, verbatim", () => { const h = headers(frame()); expect(h.get("from")).toBe("bot@example.com"); @@ -45,9 +43,8 @@ describe("buildMailFrame headers", () => { }); test("a Message-ID from @intx/mime survives the frame unchanged", () => { - // Regression: generateMessageId already returns ``. The frame - // builder used to wrap it a second time, writing `<>` into - // every `raw` — frozen and corrupt at rest, and unthreadable by any MTA. + // generateMessageId already returns ``; wrapping it again + // would write `<>`, which no MTA can thread. const generated = generateMessageId("bot@example.com"); expect(generated).toMatch(MESSAGE_ID); expect(headers(frame({ messageId: generated })).get("message-id")).toBe( diff --git a/src/migrations.ts b/src/migrations.ts index c9e77ff..4747938 100644 --- a/src/migrations.ts +++ b/src/migrations.ts @@ -445,7 +445,7 @@ export const MIGRATIONS: Migration[] = [ // // uid/modseq become NOT NULL: every remaining write path is // `NativeMailboxStore.append`, which always sets both. The backfill below - // is defense in depth for a row inserted by the pre-cutover write paths + // is defense in depth for a row inserted by a non-native write path // between `0004` running and this migration — same per-(tenant, // principal, folder) row_number() `0004` used, guarded by "uid" IS NULL // so an already-backfilled row is left alone. diff --git a/src/mount.ts b/src/mount.ts index 011b5d8..715bf59 100644 --- a/src/mount.ts +++ b/src/mount.ts @@ -70,8 +70,7 @@ const DEFAULT_HEARTBEAT_INTERVAL_MS = 25_000; /** * Ceiling on SSE events queued for one connection whose client has stopped - * reading — see the identical rationale this carried before the native-store - * cutover: an event is a nudge, never the data, so a stalled consumer is + * reading. An event is a nudge, never the data, so a stalled consumer is * disconnected rather than buffered for. */ export const MAX_PENDING_SSE_EVENTS = 100; @@ -144,8 +143,6 @@ type MailboxListItem = { raw: string; }; -// The five single-message mutations that move or flag a message. `op` is the -// event op published on success. /** * One node of `GET /me/inbox/threads(/:rootUid)`: `@intx/mailbox`'s * `executeThread`'s ref (recursively, as `children`) plus the same envelope