feat: fixed Message-ID per email and an effect handoff API for durable jobs #12

Merged
KofTwentyTwo merged 2 commits from feature/effect-message-id into develop 2026-10-14 14:30:02 +00:00
2 Commits
Author SHA1 Message Date
james.maesandClaude Opus 5.5 dc23fbdae7 test: PHI canary uses non-hex letters, so hex digests can't match by chance
PhiCanaryEgressTest checks that no five-character window of the canary
appears in any output. The canary's random part was a hex UUID, so a window
could match a SHA-256 body hash or a Message-ID by chance (CI hit "ebf93" in
a body hash). The random part is now mapped from hex to the letters g-v, so a
match can only mean the canary really leaked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 09:27:00 -05:00
james.maesandClaude Opus 5.5 40f6935c19 feat: fixed Message-ID per email and an effect handoff API for durable jobs
For the durable jobs outbox (QRun-IO/qqq#1191, spec section 2.13):
- Each queued message stores one RFC 5322 Message-ID (email_message.rfc_message_id,
  unique), derived from the idempotency key (else a stable per-message seed),
  and reuses it on every attempt. SMTP, Postmark, SendGrid and Mailgun send it.
  SES receives it in the raw MIME but replaces it; this is documented, and
  X-QQQ-Email-Id stays on every transport.
- EmailEffects.send(request, effectKey) hands an email to qbit-email's outbox
  exactly once per key: QUEUED_OR_DUPLICATE, or REFUSED (suppressed or
  invalid; never retried).
- findByIdempotencyKey / findByRfcMessageId look an email up for a reconcile.
- docs/DURABLE-JOBS.md has the outcome mapping and the binding class to add
  when QQQ RC.7 ships (EmailJobEffectTarget implements core's EffectTarget;
  no SNAPSHOT dependency now).
- Postgres upgrade SQL for existing installs; checkstyle now also covers test
  sources (the repo standard), with the existing violations fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:58:59 -05:00