Skip to content

feat(providers): reshape InboundEmailProvider seam to carry raw RFC822 (HT-35) - #35

Merged
zaridan merged 1 commit into
mainfrom
feat/ht-35-inbound-provider-raw-seam
Jul 14, 2026
Merged

feat(providers): reshape InboundEmailProvider seam to carry raw RFC822 (HT-35)#35
zaridan merged 1 commit into
mainfrom
feat/ht-35-inbound-provider-raw-seam

Conversation

@zaridan

@zaridan zaridan commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Implements HT-35 [B] — the first code ticket under the HT-33 inbound epic, built against the merged spec.

What changed

Reshapes the InboundEmailProvider seam so it carries raw RFC822 bytes + metadata, not a pre-parsed email — realizing specs/mail/inbound-ingestion.md §2's parse-once invariant.

  • src/providers/inbound-email.ts — deletes NormalizedInboundEmail/NormalizedInboundAttachment (they put the parse inside the provider); adds RawMessageContent (a kind-tagged inline | blobRef union, matching the QueueHandlerResult convention) and RawInboundMessage (content + mailboxId + providerMessageId + receivedAt); replaces parseWebhook → NormalizedInboundEmail with receiveDelivery → RawInboundMessage[] (0..N messages per delivery). verifySignature unchanged.
  • src/dev/dev-inbound-email.ts (new) + dev-inbound-email.test.ts — in-memory fake mirroring dev-sender.ts, so the ingest pipeline (HT-37) can be driven without a real provider.
  • src/providers/index.ts — barrel re-exports updated.
  • src/mail/parse.ts — doc comments that named the now-deleted types corrected (cleanup of this change's own mess; comments only, no behavior change).

Scope is the seam only — no Gmail adapter, no ingest pipeline (later tickets). Gates green locally: typecheck, biome lint, 402 tests.

One design note for review (consciously deferred to HT-41)

receiveDelivery(request: Request) takes the webhook Request. That fits a forwarding transport cleanly (the request is the message). For Gmail, the spec's flow is two-phase — the webhook enqueues a notification and acks fast (gmail-push.md §2), and a queue consumer does the history fetch (§3). A single receiveDelivery(request) can express that (the adapter reads the Pub/Sub envelope, then fetches), but where it's invoked — and whether the consumer wants the original Request or an enqueued descriptor — is best pinned when HT-41 builds the real adapter with concrete usage. I deliberately didn't over-design the seam for a flow that isn't built yet (charter: nothing speculative); it's a two-way door. Flagging so it's a conscious deferral, not an oversight.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Inbound email processing now supports raw message content supplied inline or through stored references.
    • Delivery metadata, including mailbox, provider message ID, and receipt time, is preserved.
    • Signature verification occurs before inbound deliveries are accepted.
    • Added a development email provider for queued delivery testing with configurable verification results.
  • Documentation

    • Clarified that email parsing keeps attachment content available for downstream storage and processing.

…2 (HT-35)

Replace parseWebhook -> NormalizedInboundEmail (which parsed inside the provider, breaking the spec's parse-once invariant) with receiveDelivery -> RawInboundMessage[]: the provider authenticates a delivery and yields raw RFC822 bytes (inline or a blob ref) + { mailboxId, providerMessageId, receivedAt }; the pipeline owns the single parseInboundEmail. verifySignature unchanged.

Delete NormalizedInboundEmail/NormalizedInboundAttachment (wrong boundary, no implementations yet); add RawMessageContent (kind-tagged inline|blobRef, matching QueueHandlerResult); add an in-memory dev fake + test mirroring dev-sender.ts; fix parse.ts doc comments that named the deleted types.

Implements specs/mail/inbound-ingestion.md §2. Gates green: typecheck, biome lint, 402 tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 551d841d-b297-44d0-b22a-db91b652fa7e

📥 Commits

Reviewing files that changed from the base of the PR and between 532a924 and 35ec621.

📒 Files selected for processing (5)
  • src/dev/dev-inbound-email.test.ts
  • src/dev/dev-inbound-email.ts
  • src/mail/parse.ts
  • src/providers/inbound-email.ts
  • src/providers/index.ts

📝 Walkthrough

Walkthrough

The inbound email provider contract now returns raw messages instead of normalized emails. A FIFO in-memory development provider and Vitest coverage were added, while parser documentation clarifies inline attachment handling and downstream persistence.

Changes

Inbound email ingestion

Layer / File(s) Summary
Raw provider contract
src/providers/inbound-email.ts, src/providers/index.ts, src/mail/parse.ts
The provider API now returns RawInboundMessage[] with inline or blob-reference content, and parsing documentation reflects downstream attachment persistence.
Development provider and validation
src/dev/dev-inbound-email.ts, src/dev/dev-inbound-email.test.ts
Adds a configurable FIFO in-memory provider with enqueue, raw delivery retrieval, signature results, and tests for ordering, draining, preservation, and empty queues.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: reshaping the inbound email provider to carry raw RFC822 content.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ht-35-inbound-provider-raw-seam

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

@zaridan
zaridan merged commit 911912e into main Jul 14, 2026
5 checks passed
@zaridan
zaridan deleted the feat/ht-35-inbound-provider-raw-seam branch July 14, 2026 01:19
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.

1 participant