Skip to content

feat(email): move inbound authority to Mailbox - #1157

Merged
kody-bot merged 75 commits into
mainfrom
cursor/mailbox-do-810a
Aug 2, 2026
Merged

kody-bot merged 75 commits into
mainfrom
cursor/mailbox-do-810a

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Aug 2, 2026 •

Copy link
Copy Markdown
Owner

Summary

Flips USER inbound delivery/effect authority from D1 to owner-bound Mailbox CAS RPCs while preserving retry safety, compatibility projections, legacy cutover, and the D1-only system:email exception.

Migration 0129 is additive. No user metadata rows/tables are dropped. Usage-effect replay records cascade with the 90-day delivery-event lifecycle.

Conductor findings

  1. Legacy parity blocker — fixed and verified. Normal upsertDeliveryEvent(s) still rejects USER lifecycle authority overwrites. The parity mirror now partitions mixed batches: validated lifecycle/dedupe history goes through owner-bound bootstrapDeliveryEvents (missing-row-only); pre-claim audit/non-authority rows use normal upsert; malformed legacy rows are skipped/count-visible rather than rolling back audit backfills. Tests seed production-shaped receive_started (pending/storing), received with finalization/effects, dedupe pointer, and bounded rejection audit rows, then run the actual reconciliation lane from an empty Mailbox. They prove first-pass convergence, second-pass idempotency, exact schedule preservation, malformed-row isolation, and that stale D1 cannot overwrite an existing newer Mailbox row.
  2. Rollback → roll-forward divergence — accepted caveat, documented repair. Pre-updated_at rollback writes are not automatically ordered against existing Mailbox rows. data-storage.md and disaster-recovery.md now state this explicitly and provide the backup-gated repair: verify sealed backup SHA-256 prefix 7787f8c9…, quiesce ingress/queues/schedules, inspect each owner, purge only the affected owner Mailbox metadata, rebuild from inspected D1 through missing-row bootstrap/parity, verify lifecycle/effect/finalization/message counts, redeploy, canary, then resume. The docs explicitly say code does not infer that rollback D1 is newer.
  3. Cross-DO charging window — acknowledged. The UserMeter consume/Mailbox insert sequence now documents that exhausting Email Routing retries in the non-atomic gap can burn one daily receive unit without a message; retries self-heal by winner ID, the unit resets daily, and no message can duplicate.
System recap — extends Email, Mailbox, and D1 (medium structural / high operational risk)

Mode: recap · Base: main @ 7d762dae · Head: cfe74bf3

Classification: extends — changes USER inbound authority and compatibility contracts across existing primitives; no new primitive is added.

Primitives touched

Primitive Group Impact
email assistant extends — USER inbound orchestration and effects use Mailbox authority
mailbox storage extends — live authority, cleanup CAS, guards, and missing-only legacy bootstrap
d1-app-db storage extends — promoted compatibility projection and usage-effect replay ledger

System map

USER inbound mail selects a dedupe winner, consumes UserMeter quota, commits Mailbox CAS state, and projects snapshots to D1 where the existing message graph still uses storage fences; system mail remains D1-only.

Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).

flowchart LR
  routing["email<br/>Email Routing"]:::extended
  meter["user-meter<br/>User Meter"]:::untouched
  mailbox["mailbox<br/>Mailbox"]:::extended
  d1["d1-app-db<br/>D1 app database"]:::extended
  r2["email-blobs<br/>Email blobs"]:::untouched
  routing -->|"dedupe winner + consume delivery"| meter
  routing -->|"owner-bound delivery/effect CAS"| mailbox
  mailbox -->|"compatibility snapshot + graph fence"| d1
  d1 -->|"missing-only validated legacy bootstrap"| mailbox
  routing -->|"raw MIME / attachment durability"| r2
  classDef touched fill:#1a7f37,color:#fff
  classDef extended fill:#9a6700,color:#fff
  classDef added fill:#cf222e,color:#fff
  classDef untouched fill:#57606a,color:#fff
Loading

Invariants

  • UserMeter consume precedes charged pending insertion and is idempotent by final dedupe winner ID.
  • D1 projection succeeds before graph writes or cleanup blob deletion; failed claims are compensated.
  • Rejection remains permanent even if its compatibility projection needs read-repair.
  • Existing Mailbox authority rows are never overwritten by legacy parity bootstrap.
  • system:email never enters a per-user Mailbox object.

Verification

  • Focused legacy parity/bootstrap tests: 31 passed locally; authority/migration/inbound/effect/system-isolation suites also pass
  • Production-shape reconcile test covers pending, storing, received, dedupe, audit, valid future schedules, malformed schedule isolation, idempotency, and stale-D1/no-overwrite behavior
  • Typecheck and migration ledger pass; next migration prefix is 0130
  • CI at cfe74bf3: all required checks pass, including Workers, Node, MCP, Static, and E2E
  • Bugbot passed with no findings; CodeRabbit completed with no substantive unresolved finding
  • Fresh independent whole-diff review found no blocker in the bootstrap partition or rollback procedure

Conductor report

  • STATUS: needs-review
  • Sequence step: 2b USER inbound ledger/effect authority flip
  • Backup gate: recorded backup sha256 7787f8c9…
  • Risk: high — changes inbound durability/retry authority and therefore remains ready but unmerged for conductor merge
  • Merged/deployed: no — PR #1157
  • Next: conductor merge, deploy, production parity verification; then step 3 from a fresh branch off current origin/main
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Improved inbound email processing with stronger deduplication, quota handling, retries, and effect tracking.
    • Added safer cleanup and recovery for incomplete deliveries and orphaned attachments.
    • Added reliable synchronization of delivery status and lifecycle information.
    • Preserved separate handling for system-generated email.
  • Bug Fixes

    • Prevented stale updates, duplicate charges, and lease conflicts during delivery processing.
    • Improved recovery after failed persistence or reconciliation operations.
  • Documentation

    • Updated architecture and disaster recovery documentation for delivery authority, synchronization, migration, and rollback procedures.

cursoragent and others added 30 commits August 1, 2026 07:25
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Restore deleteEmailMessageById to D1 batch then immediate R2 cleanup with
no Mailbox env/waitUntil/mirror. Restore insertEmailMessageWithAttachments
signature without mirror forwarding. Drop PR-only delete mirror tests and
update data-storage.md: live explicit/retention deletes are repaired by
parity purge/rebuild; direct delete wiring remains pending.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
cursoragent and others added 8 commits August 2, 2026 14:07
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

USER inbound delivery state and effects now use Mailbox CAS as the authority. D1 stores synchronous compatibility snapshots. The change adds migration and bootstrap support, authority-based processing, reconciliation, cleanup leases, effect handling, and integration tests.

Changes

USER inbound delivery authority

Layer / File(s) Summary
Authority schema and compatibility migration
packages/worker/migrations/..., packages/worker/src/email/test-schema.ts, packages/worker/src/account/data-targets.ts
D1 gains lifecycle and effect fields, indexes, an idempotency ledger, legacy-row backfill, and account-data handling.
Mailbox authority and D1 projection
packages/worker/src/email/inbound-delivery-authority.ts, packages/worker/src/email/inbound-delivery-projection.ts, packages/worker/src/email/mailbox-inbound-*.ts
The authority handles deduplication, quota charging, delivery transitions, leases, effects, bootstrap, and synchronous D1 snapshot projection.
Cleanup and stale-delivery reconciliation
packages/worker/src/email/inbound-delivery-reconciliation-authority.ts, packages/worker/src/email/mailbox-inbound-cleanup-ledger.ts, packages/worker/src/email/reconcile-inbound-deliveries.ts
Mailbox cleanup leases support orphan deletion and retry outcomes. Reconciliation recovers committed deliveries, restores legacy rows, and prunes expired deduplication pointers.
Inbound processing and terminal wiring
packages/worker/src/email/inbound.ts, packages/worker/src/email/service.ts, packages/worker/src/email/inbound-effects.ts, packages/worker/src/email/inbound-mailbox.ts
User inbound processing uses Mailbox authority for charging, storage, finalization, effects, and terminal repair. system:email retains the D1 workflow.
Mirror, parity, recovery, and validation
packages/worker/src/email/mailbox-*.ts, packages/worker/src/email/*.test.ts, docs/contributing/...
Mailbox mirror batches separate bootstrap snapshots from normal events. Tests cover races, fencing, retries, migration backfill, terminal ordering, and rollback repair.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Worker
  participant Authority as UserInboundDeliveryAuthority
  participant Mailbox
  participant UserMeter as USER_METER
  participant D1
  Worker->>Authority: charge inbound delivery
  Authority->>Mailbox: claim dedupe window and create pending delivery
  Authority->>UserMeter: charge quota
  Authority->>D1: mirror delivery snapshot
  Worker->>Authority: claim and finalize storage
  Authority->>Mailbox: apply delivery CAS transition
  Authority->>D1: mirror finalized snapshot
  Worker->>Authority: claim and complete effects
  Authority->>Mailbox: apply effect lease transition
  Authority->>D1: mirror effect state
Loading

Possibly related PRs

  • kentcdodds/kody#891: Introduced the inbound delivery lifecycle extended by this PR.
  • kentcdodds/kody#1156: Added the Mailbox inbound ledger and effect CAS RPCs used by this implementation.
  • kentcdodds/kody#1127: Modified the inbound mailbox terminal coordination that this PR rewires through Mailbox authority.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the primary change: moving inbound authority to Mailbox.
Description check ✅ Passed The description covers the change, system impact, risks, rollback procedure, and testing evidence, despite using Verification instead of the template's Testing heading.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/mailbox-do-810a

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

cursoragent and others added 2 commits August 2, 2026 16:40
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@kody-bot
kody-bot marked this pull request as ready for review August 2, 2026 16:51
@github-actions

github-actions Bot commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-1157.kody-a99.workers.dev

Worker: kody-pr-1157
D1: kody-pr-1157-db
KV: kody-pr-1157-oauth-kv

Mocks:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/worker/src/email/inbound.ts (1)

573-576: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the stale comment about the charge authority.

The comment states that the candidate object is returned when the invocation "won the atomic D1 charge". authority.charge no longer charges in D1. It claims the dedupe window in Mailbox, consumes quota in UserMeter, and inserts the charged pending delivery through Mailbox CAS. The reference-identity logic on Line 576 is still correct, but the stated mechanism is wrong.

📝 Proposed comment update
-					// The charge helper returns the candidate object only when this
-					// invocation won the atomic D1 charge. A concurrently committed
-					// delivery is returned as a separately parsed object.
+					// The authority returns the candidate object only when this
+					// invocation won the Mailbox dedupe claim and its CAS insert.
+					// A concurrently committed delivery is returned as a separately
+					// projected object.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/email/inbound.ts` around lines 573 - 576, Update the
comment immediately above the chargedReceive assignment to describe
authority.charge’s current behavior: it claims the dedupe window in Mailbox,
consumes quota in UserMeter, and inserts the charged pending delivery through
Mailbox CAS. Preserve the explanation that a concurrently committed delivery is
returned as a separately parsed object, and leave the reference-identity logic
unchanged.
🧹 Nitpick comments (7)
packages/worker/src/email/mailbox-inbound-ledger.workers.test.ts (1)

344-351: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the 'deleted' outcome and assert the retry delay.

Line 348 uses outcome: 'delete-failed'. markMailboxInboundDeliveryOrphanCleaned selects a different retry delay per outcome: mailboxInboundOrphanVerificationMs for 'deleted' and mailboxInboundReconciliationRetryMs for 'delete-failed' (see packages/worker/src/email/mailbox-inbound-cleanup-ledger.ts lines 160-163). The 'deleted' branch is untested, and it is the branch that drives the cleaned counter in reconcileUserStaleInboundDeliveries.

Add an assertion on the resulting cleanupRetryAt so both delay constants are pinned.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/email/mailbox-inbound-ledger.workers.test.ts` around
lines 344 - 351, The test around markInboundDeliveryOrphanCleaned currently
covers only the 'delete-failed' outcome; add a separate or parameterized case
for outcome 'deleted' and assert cleanupRetryAt uses
mailboxInboundOrphanVerificationMs, while retaining coverage that
'delete-failed' uses mailboxInboundReconciliationRetryMs.
packages/worker/src/email/test-schema.ts (1)

192-204: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Mirror the two new partial indexes from migration 0129 in the test schema.

Migration 0129 creates idx_email_delivery_events_user_state_created and idx_email_delivery_events_user_dedupe_expires. The test schema creates neither. Tests that exercise owner discovery and dedupe pruning therefore run against a different index set than production. Index absence does not change query results, but it lets index-name or partial-predicate regressions pass unnoticed in the worker tests.

♻️ Proposed additions
 		`CREATE TABLE IF NOT EXISTS email_inbound_usage_effects (
 	user_id TEXT NOT NULL,
 	delivery_id TEXT NOT NULL,
 	finalization_token TEXT NOT NULL,
 	created_at TEXT NOT NULL,
 	PRIMARY KEY (user_id, delivery_id, finalization_token),
 	FOREIGN KEY (delivery_id) REFERENCES email_delivery_events(id) ON DELETE CASCADE
 );`,
+		`CREATE INDEX IF NOT EXISTS idx_email_delivery_events_user_state_created
+ON email_delivery_events(user_id, state, created_at, id)
+WHERE provider = 'cloudflare-email-routing' AND state IS NOT NULL;`,
+		`CREATE INDEX IF NOT EXISTS idx_email_delivery_events_user_dedupe_expires
+ON email_delivery_events(user_id, dedupe_expires_at, id)
+WHERE provider = 'cloudflare-email-routing-dedupe'
+	AND dedupe_expires_at IS NOT NULL;`,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/email/test-schema.ts` around lines 192 - 204, The test
schema’s SQL definitions must mirror the two partial indexes added by migration
0129. In the schema statement collection near
idx_email_delivery_events_pending_effects, add
idx_email_delivery_events_user_state_created and
idx_email_delivery_events_user_dedupe_expires with the same columns and partial
predicates as production.
packages/worker/src/email/inbound-delivery-authority.ts (1)

237-262: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider a read path that does not write to D1 on every point read.

get and getWindow mirror to D1 on every successful Mailbox read, even when the snapshot is unchanged. bootstrapPreDeployDueRows in packages/worker/src/email/inbound-delivery-reconciliation-authority.ts calls authority.get(row.id) in a sequential loop over a stale batch. Each iteration then costs one Mailbox RPC plus one D1 upsert, serially.

The updated_at fence in mirrorUserInboundDeliverySnapshotToD1 already makes a repeat mirror a no-op write, so the write is pure overhead when the snapshot has not advanced. Two options: skip the mirror when the snapshot updatedAt matches the D1 row, or expose a read-only variant for bulk reconciliation loops.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/email/inbound-delivery-authority.ts` around lines 237 -
262, Adjust the read paths in get and getWindow to avoid mirroring unchanged
Mailbox snapshots to D1 on every successful read. Reuse the existing updated_at
comparison or expose a read-only authority variant for
bootstrapPreDeployDueRows, while preserving mirroring when the snapshot has
advanced or must be bootstrapped.
packages/worker/migrations/0129-email-inbound-mailbox-authority-mirror.sql (1)

115-122: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Add an index on email_inbound_usage_effects(delivery_id) for the cascade path.

The primary key is (user_id, delivery_id, finalization_token). SQLite cannot use that index for a lookup keyed on delivery_id alone. Every ON DELETE CASCADE from email_delivery_events therefore scans the whole child table. The 90-day delivery-event retention job deletes rows in bulk, so this scan repeats per parent row.

♻️ Proposed index
 	PRIMARY KEY (user_id, delivery_id, finalization_token),
 	FOREIGN KEY (delivery_id) REFERENCES email_delivery_events(id) ON DELETE CASCADE
 );
+
+CREATE INDEX IF NOT EXISTS idx_email_inbound_usage_effects_delivery
+ON email_inbound_usage_effects(delivery_id);

Note: changing this file changes its SHA-256, so update tools/migration-ledger.json as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/migrations/0129-email-inbound-mailbox-authority-mirror.sql`
around lines 115 - 122, Add a dedicated index on
email_inbound_usage_effects(delivery_id) to support the email_delivery_events ON
DELETE CASCADE lookup, while keeping the existing primary key unchanged. After
modifying the migration, update its corresponding SHA-256 entry in
tools/migration-ledger.json.
packages/worker/src/email/inbound-mailbox-authority-mirror-migration.node.test.ts (1)

110-126: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a non-inbound fixture row to cover the second backfill statement.

All four fixture rows use cloudflare-email-routing or cloudflare-email-routing-dedupe. Migration statement 2 (UPDATE ... SET updated_at = created_at WHERE updated_at IS NULL) applies to every other row, and the system:email path must keep state NULL while still receiving updated_at. That invariant is central to the D1-only system path and is not asserted today.

💚 Proposed fixture and assertion
 	insert.run(
 		'email-inbound-dedupe:fingerprint-dedupe',
 		'user-dedupe',
 		'receive_started',
 		'cloudflare-email-routing-dedupe',
 		JSON.stringify({
 			state: 'pending',
 			fingerprint: 'fingerprint-dedupe',
 			dedupeExpiresAt: '2026-08-01T01:00:00.000Z',
 		}),
 		0,
 		null,
 		createdAt,
 	)
+	insert.run(
+		'delivery-system',
+		'system:email',
+		'sent',
+		'kody',
+		JSON.stringify({ state: 'received', fingerprint: 'fingerprint-system' }),
+		0,
+		null,
+		createdAt,
+	)
expect(
	db
		.prepare(
			`SELECT state, fingerprint, updated_at
			FROM email_delivery_events
			WHERE id = 'delivery-system'`,
		)
		.get(),
).toEqual({ state: null, fingerprint: null, updated_at: createdAt })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/worker/src/email/inbound-mailbox-authority-mirror-migration.node.test.ts`
around lines 110 - 126, Add a non-inbound `system:email` fixture row such as
`delivery-system` before applying `authorityMirrorMigration`, ensuring its
`state` and `fingerprint` are NULL and `updated_at` starts NULL. Extend the
migration assertions to query this row and verify `state` and `fingerprint`
remain NULL while `updated_at` is populated from `createdAt`.
packages/worker/src/account/data-targets.ts (1)

159-159: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Exclude the inbound usage-effects ledger from account export.

includeInExport: false combined with surface and reason keeps deletion coverage while documenting that this internal idempotency bookkeeping table has no user content to export. The table contains only user_id, delivery_id, finalization_token, and created_at.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/account/data-targets.ts` at line 159, Update the
email_inbound_usage_effects entry in the account data-target configuration to
set includeInExport to false while retaining its surface and reason metadata.
Keep the user_id key and deletion coverage unchanged, and document that this
internal idempotency ledger contains no user-exportable content.

Source: Coding guidelines

packages/worker/src/email/inbound-mailbox-mirror.workers.test.ts (1)

93-173: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a Proxy forwarder instead of the hand-written method list.

createLedgerBackedMailboxStub forwards 15 ledger RPCs by name. When the authority gains a new ledger RPC, this list silently omits it and the affected test fails with an opaque "not a function" error instead of a clear signal. A Proxy forwards every method automatically while still allowing the graph-mirror overrides applied on Lines 194-204.

♻️ Proposed refactor
 function createLedgerBackedMailboxStub(
 	mailbox: ReturnType<typeof env.MAILBOX.get>,
+	overrides: Record<string, unknown> = {},
 ) {
-	return {
-		async getInboundDelivery(
-			...args: Parameters<typeof mailbox.getInboundDelivery>
-		) {
-			return await mailbox.getInboundDelivery(...args)
-		},
-		// ...remaining explicit forwarders...
-	}
+	return new Proxy(overrides, {
+		get(target, property, receiver) {
+			if (property in target) return Reflect.get(target, property, receiver)
+			const value = (mailbox as Record<string | symbol, unknown>)[property]
+			return typeof value === 'function' ? value.bind(mailbox) : value
+		},
+	})
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/email/inbound-mailbox-mirror.workers.test.ts` around
lines 93 - 173, Replace the hand-written forwarding methods in
createLedgerBackedMailboxStub with a Proxy that dynamically forwards any missing
mailbox property or method to the underlying mailbox. Preserve the existing
graph-mirror overrides applied after this helper, ensuring explicitly overridden
properties continue to take precedence.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/contributing/architecture/data-storage.md`:
- Around line 717-747: Reconcile the stale phase and durability sections with
the USER inbound authority model: update the sections around the phase
description and inbound commit boundary so Mailbox owner-bound CAS is
authoritative, D1 is only the synchronous mirror, and retries follow Mailbox
state. Preserve explicit exceptions for system:email and the one-time
D1-to-Mailbox bootstrap bridge; if these sections describe future behavior
instead, label them clearly as a future phase.

In `@packages/worker/src/email/inbound-delivery-authority.ts`:
- Around line 331-334: Update the return logic after claimWindow so newly
inserted deliveries always return toUserInboundDelivery(userId,
result.delivery), including when the claimed deliveryId matches
chargeInput.delivery.deliveryId; preserve the existing handling for non-inserted
results.

In `@packages/worker/src/email/inbound-delivery-projection.ts`:
- Around line 200-254: The inbound projection write currently treats both
owner/provider conflicts and stale snapshots as failures. Update the mirror
logic around the D1 upsert and its changes check to re-read the existing row
when no update occurs, return successfully when the stored row is at least as
current as the snapshot, and throw only if the existing owner or provider
differs from the incoming values; preserve the owner/provider isolation fence
and existing error behavior for genuine conflicts.

In `@packages/worker/src/email/inbound-delivery-reconciliation-authority.ts`:
- Around line 181-188: Guard the await of authority.deferReconciliation in the
catch handler for inbound delivery recovery so a rejection is swallowed,
matching reconcileStaleInboundDeliveries. Ensure reconciliation continues
processing remaining deliveries and preserves accumulated counters.
- Around line 262-265: Validate each row’s detail_json in the
pruneUserExpiredInboundDedupePointers loop before calling authority.claimWindow:
safely handle NULL or malformed JSON without aborting remaining rows, and reject
parsed deliveries whose userId differs from input.userId. Only pass validated,
user-owned InboundDelivery values to claimWindow, matching the legacy
pruneExpiredInboundDedupePointers behavior.

In `@packages/worker/src/email/inbound-effects.ts`:
- Around line 732-744: Update the bridge-row loop in the reconciliation flow to
catch failures from each authority.get(row.id), count each failed row using the
existing error-counting mechanism, and continue processing subsequent rows so
listDueEffects still runs. Keep successful reads and existing due-processing
behavior unchanged.

In `@packages/worker/src/email/inbound-mailbox.ts`:
- Around line 103-115: Move createUserInboundDeliveryAuthority inside an async
promise wrapper in scheduleInboundRejectedTerminalWork so both synchronous
construction errors and the subsequent get(input.deliveryId) failure are handled
by the existing catch. Preserve the isSystemEmailOwner early return and the
current error logging, ensuring the function never propagates errors to its
caller.

In `@packages/worker/src/email/mailbox-inbound-ledger.ts`:
- Around line 186-203: Update claimInboundDeliveryCleanup so its
compare-and-swap only claims rows that still satisfy the same stale eligibility
predicate enforced by listDueStaleInboundDeliveries, rather than every pending
row. Persist or pass the stale eligibility token as needed and validate it
atomically before transitioning the delivery to cleaning, preventing newly
pending deliveries from entering orphan cleanup.

In `@packages/worker/src/email/mailbox-live-mirror.ts`:
- Around line 137-138: Protect the Mailbox authority boundary across
packages/worker/src/email/mailbox-live-mirror.ts:137-138,
packages/worker/src/email/mailbox-live-mirror.ts:175-177, and
packages/worker/src/email/mailbox-types.ts:502-503. Require explicit
event-mirroring intent in the live-mirror API or reject USER inbound and
system:email events, retain fail-closed behavior based on that intent, and
separate bootstrap upserts from normal Mailbox RPCs or enforce bootstrap-only
and missing-row checks at runtime.

---

Outside diff comments:
In `@packages/worker/src/email/inbound.ts`:
- Around line 573-576: Update the comment immediately above the chargedReceive
assignment to describe authority.charge’s current behavior: it claims the dedupe
window in Mailbox, consumes quota in UserMeter, and inserts the charged pending
delivery through Mailbox CAS. Preserve the explanation that a concurrently
committed delivery is returned as a separately parsed object, and leave the
reference-identity logic unchanged.

---

Nitpick comments:
In `@packages/worker/migrations/0129-email-inbound-mailbox-authority-mirror.sql`:
- Around line 115-122: Add a dedicated index on
email_inbound_usage_effects(delivery_id) to support the email_delivery_events ON
DELETE CASCADE lookup, while keeping the existing primary key unchanged. After
modifying the migration, update its corresponding SHA-256 entry in
tools/migration-ledger.json.

In `@packages/worker/src/account/data-targets.ts`:
- Line 159: Update the email_inbound_usage_effects entry in the account
data-target configuration to set includeInExport to false while retaining its
surface and reason metadata. Keep the user_id key and deletion coverage
unchanged, and document that this internal idempotency ledger contains no
user-exportable content.

In `@packages/worker/src/email/inbound-delivery-authority.ts`:
- Around line 237-262: Adjust the read paths in get and getWindow to avoid
mirroring unchanged Mailbox snapshots to D1 on every successful read. Reuse the
existing updated_at comparison or expose a read-only authority variant for
bootstrapPreDeployDueRows, while preserving mirroring when the snapshot has
advanced or must be bootstrapped.

In
`@packages/worker/src/email/inbound-mailbox-authority-mirror-migration.node.test.ts`:
- Around line 110-126: Add a non-inbound `system:email` fixture row such as
`delivery-system` before applying `authorityMirrorMigration`, ensuring its
`state` and `fingerprint` are NULL and `updated_at` starts NULL. Extend the
migration assertions to query this row and verify `state` and `fingerprint`
remain NULL while `updated_at` is populated from `createdAt`.

In `@packages/worker/src/email/inbound-mailbox-mirror.workers.test.ts`:
- Around line 93-173: Replace the hand-written forwarding methods in
createLedgerBackedMailboxStub with a Proxy that dynamically forwards any missing
mailbox property or method to the underlying mailbox. Preserve the existing
graph-mirror overrides applied after this helper, ensuring explicitly overridden
properties continue to take precedence.

In `@packages/worker/src/email/mailbox-inbound-ledger.workers.test.ts`:
- Around line 344-351: The test around markInboundDeliveryOrphanCleaned
currently covers only the 'delete-failed' outcome; add a separate or
parameterized case for outcome 'deleted' and assert cleanupRetryAt uses
mailboxInboundOrphanVerificationMs, while retaining coverage that
'delete-failed' uses mailboxInboundReconciliationRetryMs.

In `@packages/worker/src/email/test-schema.ts`:
- Around line 192-204: The test schema’s SQL definitions must mirror the two
partial indexes added by migration 0129. In the schema statement collection near
idx_email_delivery_events_pending_effects, add
idx_email_delivery_events_user_state_created and
idx_email_delivery_events_user_dedupe_expires with the same columns and partial
predicates as production.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 91afb047-7927-4bd2-bba2-09e2f93b92e5

📥 Commits

Reviewing files that changed from the base of the PR and between 7d762da and 152b682.

📒 Files selected for processing (26)
  • docs/contributing/architecture/data-storage.md
  • packages/worker/migrations/0129-email-inbound-mailbox-authority-mirror.sql
  • packages/worker/src/account/data-targets.ts
  • packages/worker/src/email/inbound-delivery-authority.node.test.ts
  • packages/worker/src/email/inbound-delivery-authority.ts
  • packages/worker/src/email/inbound-delivery-projection.ts
  • packages/worker/src/email/inbound-delivery-reconciliation-authority.ts
  • packages/worker/src/email/inbound-effects.ts
  • packages/worker/src/email/inbound-mailbox-authority-mirror-migration.node.test.ts
  • packages/worker/src/email/inbound-mailbox-mirror.workers.test.ts
  • packages/worker/src/email/inbound-mailbox.node.test.ts
  • packages/worker/src/email/inbound-mailbox.ts
  • packages/worker/src/email/inbound.ts
  • packages/worker/src/email/inbound.workers.test.ts
  • packages/worker/src/email/mailbox-do.ts
  • packages/worker/src/email/mailbox-inbound-cleanup-ledger.ts
  • packages/worker/src/email/mailbox-inbound-effect-ledger.ts
  • packages/worker/src/email/mailbox-inbound-ledger-shared.ts
  • packages/worker/src/email/mailbox-inbound-ledger.ts
  • packages/worker/src/email/mailbox-inbound-ledger.workers.test.ts
  • packages/worker/src/email/mailbox-live-mirror.ts
  • packages/worker/src/email/mailbox-types.ts
  • packages/worker/src/email/reconcile-inbound-deliveries.ts
  • packages/worker/src/email/service.ts
  • packages/worker/src/email/test-schema.ts
  • tools/migration-ledger.json

Comment thread docs/contributing/architecture/data-storage.md Outdated
Comment thread packages/worker/src/email/inbound-delivery-authority.ts Outdated
Comment thread packages/worker/src/email/inbound-delivery-projection.ts
Comment thread packages/worker/src/email/inbound-effects.ts Outdated
Comment thread packages/worker/src/email/inbound-mailbox.ts
Comment thread packages/worker/src/email/mailbox-inbound-ledger.ts
Comment thread packages/worker/src/email/mailbox-live-mirror.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/worker/src/email/mailbox-inbound-cleanup-ledger.ts (1)

44-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate eligibility logic between the JS predicate and the SQL WHERE clause.

The claimable predicate (lines 47-61) and the SQL WHERE clause (lines 81-89) encode the same staleness and state-eligibility rules independently. If a future change updates one side and not the other, the JS-side early return at Line 62 causes an incorrect not-claimed result whenever the SQL predicate would actually have allowed the claim, because the UPDATE is never attempted in that case. The post-write verification at lines 106-110 cannot catch this, since it only runs after the UPDATE executes.

Consider removing the JS-side pre-check and relying solely on the SQL WHERE clause plus the existing post-write verification. A non-matching UPDATE is cheap and the final after-based check already determines the correct result.

♻️ Proposed direction
-	const claimable =
-		current.state === expectedState &&
-		current.updatedAt === expectedUpdatedAt &&
-		current.createdAt < staleBefore &&
-		(current.reconcileAfter == null || current.reconcileAfter <= now) &&
-		(current.state === 'pending' ||
-			(current.state === 'storing' &&
-				current.storageLeaseAt != null &&
-				current.storageLeaseAt < leaseExpiredBefore) ||
-			(current.state === 'cleaning' &&
-				current.cleanupLeaseAt != null &&
-				current.cleanupLeaseAt < leaseExpiredBefore) ||
-			(current.state === 'orphan-cleaned' &&
-				current.cleanupRetryAt != null &&
-				current.cleanupRetryAt <= now))
-	if (!claimable) return { status: 'not-claimed', delivery: current }
-
 	const cleanupLease = crypto.randomUUID()

Let the SQL UPDATE and the existing after-based verification (lines 105-113) be the single source of truth for eligibility.

Also applies to: 72-90

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/email/mailbox-inbound-cleanup-ledger.ts` around lines 44
- 62, Remove the duplicated claimable predicate and its early return from the
claim flow around the mailbox inbound cleanup ledger update. Let the SQL UPDATE
WHERE clause remain the sole eligibility check, then use the existing
post-update after-based verification to return the correct claimed or
not-claimed result.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/worker/src/email/mailbox-inbound-cleanup-ledger.ts`:
- Around line 44-62: Remove the duplicated claimable predicate and its early
return from the claim flow around the mailbox inbound cleanup ledger update. Let
the SQL UPDATE WHERE clause remain the sole eligibility check, then use the
existing post-update after-based verification to return the correct claimed or
not-claimed result.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c5e1fff2-af40-4b69-aac7-392143b93f78

📥 Commits

Reviewing files that changed from the base of the PR and between 152b682 and 942debc.

📒 Files selected for processing (31)
  • docs/contributing/architecture/data-storage.md
  • packages/worker/migrations/0129-email-inbound-mailbox-authority-mirror.sql
  • packages/worker/src/account/data-targets.ts
  • packages/worker/src/email/inbound-delivery-authority.node.test.ts
  • packages/worker/src/email/inbound-delivery-authority.ts
  • packages/worker/src/email/inbound-delivery-projection.ts
  • packages/worker/src/email/inbound-delivery-reconciliation-authority.ts
  • packages/worker/src/email/inbound-delivery.ts
  • packages/worker/src/email/inbound-effects.ts
  • packages/worker/src/email/inbound-mailbox-authority-mirror-migration.node.test.ts
  • packages/worker/src/email/inbound-mailbox-mirror.workers.test.ts
  • packages/worker/src/email/inbound-mailbox.node.test.ts
  • packages/worker/src/email/inbound-mailbox.ts
  • packages/worker/src/email/inbound.ts
  • packages/worker/src/email/mailbox-do.ts
  • packages/worker/src/email/mailbox-do.workers.test.ts
  • packages/worker/src/email/mailbox-inbound-bootstrap.ts
  • packages/worker/src/email/mailbox-inbound-cleanup-ledger.ts
  • packages/worker/src/email/mailbox-inbound-ledger.ts
  • packages/worker/src/email/mailbox-inbound-ledger.workers.test.ts
  • packages/worker/src/email/mailbox-live-mirror.ts
  • packages/worker/src/email/mailbox-mirror.ts
  • packages/worker/src/email/mailbox-snapshot-repo.ts
  • packages/worker/src/email/mailbox-snapshots.ts
  • packages/worker/src/email/mailbox-store.ts
  • packages/worker/src/email/mailbox-types.ts
  • packages/worker/src/email/reconcile-inbound-deliveries.ts
  • packages/worker/src/email/service.ts
  • packages/worker/src/email/system-inbound-delivery-authority.ts
  • packages/worker/src/email/test-schema.ts
  • tools/migration-ledger.json
🚧 Files skipped from review as they are similar to previous changes (15)
  • tools/migration-ledger.json
  • packages/worker/src/email/mailbox-types.ts
  • packages/worker/src/email/test-schema.ts
  • packages/worker/src/email/inbound-mailbox-mirror.workers.test.ts
  • packages/worker/src/account/data-targets.ts
  • packages/worker/src/email/reconcile-inbound-deliveries.ts
  • packages/worker/src/email/service.ts
  • packages/worker/migrations/0129-email-inbound-mailbox-authority-mirror.sql
  • packages/worker/src/email/inbound-delivery-reconciliation-authority.ts
  • packages/worker/src/email/inbound-mailbox.ts
  • packages/worker/src/email/inbound-mailbox.node.test.ts
  • packages/worker/src/email/inbound-mailbox-authority-mirror-migration.node.test.ts
  • packages/worker/src/email/mailbox-inbound-ledger.ts
  • packages/worker/src/email/inbound.ts
  • packages/worker/src/email/inbound-effects.ts

cursoragent and others added 2 commits August 2, 2026 17:40
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/email/inbound-delivery-authority.ts Outdated
Comment thread packages/worker/src/email/inbound-delivery-authority.ts
cursoragent and others added 2 commits August 2, 2026 17:54
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a5f3952. Configure here.

Comment thread packages/worker/src/email/inbound-delivery-projection.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/worker/src/email/mailbox-mirror.ts (1)

371-376: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Partition the page in one pass.

Both filter calls evaluate isUserInboundLegacyAuthoritySnapshot for every event. That predicate runs JSON.parse on detailJson for each inbound-provider event, so each such event is parsed twice per page. A single loop removes the duplicate work and keeps the two subsets in input order.

♻️ Proposed refactor
-	const bootstrapEvents = input.events.filter(
-		isUserInboundLegacyAuthoritySnapshot,
-	)
-	const normalEvents = input.events.filter(
-		(event) => !isUserInboundLegacyAuthoritySnapshot(event),
-	)
+	const bootstrapEvents: Array<MailboxDeliveryEventInput> = []
+	const normalEvents: Array<MailboxDeliveryEventInput> = []
+	for (const event of input.events) {
+		if (isUserInboundLegacyAuthoritySnapshot(event)) bootstrapEvents.push(event)
+		else normalEvents.push(event)
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/email/mailbox-mirror.ts` around lines 371 - 376, Update
the event partitioning around bootstrapEvents and normalEvents to iterate over
input.events once, evaluate isUserInboundLegacyAuthoritySnapshot only once per
event, and append each event to the corresponding subset while preserving input
order.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/worker/src/email/mailbox-inbound-bootstrap.ts`:
- Around line 175-236: Update assertUserInboundLegacyBootstrapSnapshot to
validate cleanupRetryAt and reconcileAfter against the delivery detail,
requiring both fields to be null rather than accepting persisted event values.
Add these checks alongside the existing optionalMatches assertions without
changing validation of the other snapshot fields.

---

Nitpick comments:
In `@packages/worker/src/email/mailbox-mirror.ts`:
- Around line 371-376: Update the event partitioning around bootstrapEvents and
normalEvents to iterate over input.events once, evaluate
isUserInboundLegacyAuthoritySnapshot only once per event, and append each event
to the corresponding subset while preserving input order.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 37459523-23d4-4ba2-b76d-1216165077f5

📥 Commits

Reviewing files that changed from the base of the PR and between 942debc and a5f3952.

📒 Files selected for processing (20)
  • docs/contributing/architecture/data-storage.md
  • docs/contributing/disaster-recovery.md
  • packages/worker/src/email/inbound-delivery-authority.node.test.ts
  • packages/worker/src/email/inbound-delivery-authority.ts
  • packages/worker/src/email/inbound-delivery-projection.ts
  • packages/worker/src/email/inbound-mailbox-mirror.workers.test.ts
  • packages/worker/src/email/mailbox-delivery-event-bootstrap.ts
  • packages/worker/src/email/mailbox-delivery-event-upsert.ts
  • packages/worker/src/email/mailbox-do.ts
  • packages/worker/src/email/mailbox-do.workers.test.ts
  • packages/worker/src/email/mailbox-inbound-authority-guard.workers.test.ts
  • packages/worker/src/email/mailbox-inbound-bootstrap.ts
  • packages/worker/src/email/mailbox-inbound-ledger.ts
  • packages/worker/src/email/mailbox-inbound-ledger.workers.test.ts
  • packages/worker/src/email/mailbox-mirror.node.test.ts
  • packages/worker/src/email/mailbox-mirror.ts
  • packages/worker/src/email/mailbox-parity-phases.ts
  • packages/worker/src/email/mailbox-reconcile.workers.test.ts
  • packages/worker/src/email/mailbox-types.ts
  • packages/worker/src/email/service.ts
💤 Files with no reviewable changes (1)
  • packages/worker/src/email/mailbox-do.workers.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/worker/src/email/service.ts
  • packages/worker/src/email/mailbox-inbound-ledger.workers.test.ts
  • packages/worker/src/email/mailbox-inbound-ledger.ts
  • packages/worker/src/email/inbound-delivery-authority.ts
  • packages/worker/src/email/inbound-delivery-projection.ts

Comment on lines +175 to +236
!optionalMatches(input.event.storageLease, delivery.storageLease) ||
!optionalMatches(input.event.storageLeaseAt, delivery.storageLeaseAt) ||
!optionalMatches(input.event.cleanupLease, delivery.cleanupLease) ||
!optionalMatches(input.event.cleanupLeaseAt, delivery.cleanupLeaseAt) ||
!optionalMatches(
input.event.expectedAttachmentCount,
delivery.expectedAttachmentCount,
) ||
!optionalMatches(
input.event.finalizationToken,
delivery.finalizationToken,
) ||
!optionalMatches(input.event.dedupeExpiresAt, delivery.dedupeExpiresAt) ||
!optionalMatches(
input.event.usageEffectRecordedAt,
delivery.usageEffectRecordedAt,
) ||
!optionalMatches(
input.event.usageEffectSuppressedAt,
delivery.usageEffectSuppressedAt,
) ||
!optionalMatches(input.event.usageStartedAt, delivery.usageStartedAt) ||
!optionalMatches(input.event.usageMonth, delivery.usageMonth) ||
!optionalMatches(input.event.usageBytes, delivery.usageBytes) ||
!optionalMatches(input.event.usageDurationMs, delivery.usageDurationMs) ||
!optionalMatches(
input.event.usageEffectRetryAt,
delivery.usageEffectRetryAt,
) ||
!optionalMatches(input.event.usageEffectLease, delivery.usageEffectLease) ||
!optionalMatches(
input.event.usageEffectLeaseAt,
delivery.usageEffectLeaseAt,
) ||
!optionalMatches(
input.event.subscriptionEffectState,
delivery.subscriptionEffectState,
) ||
!optionalMatches(
input.event.subscriptionEffectLease,
delivery.subscriptionEffectLease,
) ||
!optionalMatches(
input.event.subscriptionEffectLeaseAt,
delivery.subscriptionEffectLeaseAt,
) ||
!optionalMatches(
input.event.subscriptionEffectRetryAt,
delivery.subscriptionEffectRetryAt,
) ||
!optionalMatches(
input.event.subscriptionEffectAttemptCount,
delivery.subscriptionEffectAttemptCount,
) ||
!optionalMatches(
input.event.subscriptionEffectDeadLetterAt,
delivery.subscriptionEffectDeadLetterAt,
) ||
!optionalMatches(
input.event.subscriptionEffectLastError,
delivery.subscriptionEffectLastError,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Resolve the InboundDelivery type and check for the two fields.
fd -t f 'inbound-delivery.ts' packages/worker/src/email
ast-grep run --pattern 'export type InboundDelivery = $_' --lang typescript packages/worker/src/email
rg -nP -C2 '\b(reconcileAfter|cleanupRetryAt)\b' packages/worker/src/email/inbound-delivery.ts

Repository: kentcdodds/kody

Length of output: 4722


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== mailbox-inbound-bootstrap nearby =="
sed -n '120,245p' packages/worker/src/email/mailbox-inbound-bootstrap.ts

echo
echo "== inbound-delivery summary =="
sed -n '1,100p' packages/worker/src/email/inbound-delivery.ts

echo
echo "== write/read event shape references =="
rg -nP -C3 'cleanupRetryAt|reconcileAfter' packages/worker/src/email/mailbox-delivery-events.ts packages/worker/src/email/mailbox-inbound-bootstrap.ts packages/worker/src/email/inbound-delivery.ts

Repository: kentcdodds/kody

Length of output: 12689


Validate scheduling fields against the D1 detail.

cleanupRetryAt and reconcileAfter are authority-bearing fields and can be persisted from bootstrap events, but assertUserInboundLegacyBootstrapSnapshot does not compare them. Assert cleanupRetryAt == null and reconcileAfter == null; do not accept values that the D1 detail does not represent as a complete owner-bound snapshot.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/email/mailbox-inbound-bootstrap.ts` around lines 175 -
236, Update assertUserInboundLegacyBootstrapSnapshot to validate cleanupRetryAt
and reconcileAfter against the delivery detail, requiring both fields to be null
rather than accepting persisted event values. Add these checks alongside the
existing optionalMatches assertions without changing validation of the other
snapshot fields.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@kody-bot
kody-bot merged commit 3afd200 into main Aug 2, 2026
10 checks passed
@kody-bot
kody-bot deleted the cursor/mailbox-do-810a branch August 2, 2026 19:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants