Files
platform/docs/email-migration-checklist.md
T
pastilhasandClaude Opus 5 7d1212765b docs: test checklist for the email sidecar migration
Written to be run later rather than now, so it says what changed underneath and where a
failure will land: the routes moved verbatim, but the transport (browser → proxy → sidecar
HTTP) and the source of user identity (X-Officer-User instead of userMiddleware) did not,
and that is where breakage will cluster.

Includes the attribution table — 503 vs 502 vs 401 each point at a different half — and
flags the two things that are expected rather than wrong: sync still runs platform-side on
purpose, and the channel handlers' "sync emails" was already broken before this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 12:13:58 +00:00

5.1 KiB

Email sidecar migration — test checklist

For 7c173b4, which moved the mail store and all 14 routes out of the platform and into officer-email. Each line is a single yes/no. Report as "M3 ok, R2 needs fix".

Pre-req: pm2 restart officer-email && pm2 restart officer, then hard-refresh. Both are needed — the sidecar gained an HTTP listener and the platform became a proxy; restarting only one leaves them speaking different protocols.

Why this needs testing at all: the routes moved verbatim, so the logic should be identical. What changed underneath is the transport (browser → platform proxy → sidecar HTTP) and where the user identity comes from (X-Officer-User header instead of the platform's userMiddleware). Failures will cluster in those two places, not in mail logic.


M — Messages and folders

  • M1 /email loads and lists messages.
  • M2 Folder switching works, and per-folder counts look right (inbox / sent / all).
  • M3 Paging past the first page works.
  • M4 /email/stats renders — the totals panel is populated, not zeroed.
  • M5 Labels/folders list is populated.

R — Reading a message

  • R1 Open a message → body, sender, date, recipients all render.
  • R2 Open a thread → the whole conversation, in order.
  • R3 Mark as read → state sticks after a refresh.
  • R4 Mark a whole thread read.
  • R5 Delete a message → it disappears and stays gone after a refresh.
  • R6 Deep-link /email/:emailId in a fresh tab opens that message.

S — Search and contacts

  • S1 Search returns sensible results.
  • S2 Search with an apostrophe or quote in the term behaves (it's, "x"), i.e. no 500.
  • S3 Contacts list populates (used by the compose autocomplete).

A — Attachments

  • A1 A message with attachments shows them.
  • A2 Extracting/downloading an attachment works and the file is intact. Attachments write under DATA_PATH; the sidecar does this now, so a permissions problem would surface here first.

L — Live updates (the SSE path)

  • L1 Leave /email open, send yourself mail → it appears without a manual refresh.
  • L2 No repeated reconnect errors in the browser console while the page sits idle. This is the biggest structural change: email:new used to travel sidecar → platform → SSE. The stream is inside the sidecar now and the IDLE watcher pushes to it in-process.

C — Accounts

  • C1 The accounts screen lists the account with its status.
  • C2 Sync status / last-synced is shown and looks current.
  • C3 Trigger a manual sync → it starts.
  • C4 Adding an account still validates the IMAP connection (test with a deliberately wrong password and expect a clear failure, not a hang).

X — Sync regression (still platform-side, deliberately)

The two sync handlers did not move; they still run in the platform queue and now import the store from its new location. So this block is checking nothing broke, not that anything improved.

  • X1 New mail still arrives — the periodic sync is running.
  • X2 Email sync jobs still appear in the Jobs list (they will stop doing so in stage 2, by design — option A).

P — Proxy behaviour

Run from the host. Mint a token the same way the app does, or reuse a browser one.

  • P1 A long request survives: trigger a from-scratch resync and confirm it isn't cut off around 60s. The proxy sets timeoutSeconds: 1800 for the whole /api/email prefix.
  • P2 /api/email/messages returns 503 (not a hang or a 500) while officer-email is stopped: bash pm2 stop officer-email curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $TOK" \ -H 'Host: officer.pastilhas.dev' http://127.0.0.1:9010/api/email/messages # expect 503 pm2 start officer-email
  • P3 Nothing in pm2 logs officer mentions email internals any more — the platform should be logging proxy failures at most, never mail.

Attributing a failure

The split makes this quick:

symptom look at
503 email sidecar not available the sidecar isn't running, or hasn't announced its port — pm2 logs officer-email
502 email sidecar unreachable it announced a port but died; check for a crash on boot
401 missing X-Officer-User the proxy didn't inject the header — a platform-side problem
anything about messages, folders, IMAP the sidecar — pm2 logs officer-email

pm2 logs officer-email should now carry everything mail-related. If mail errors are still appearing in pm2 logs officer, something didn't move.

Known-broken already, not caused by this

gmail-sync is hardcoded in all three channel handlers (telegram, discord, whatsapp), but the only account is provider=gmail, auth_type=password, which routes to IMAP. So "sync emails" from a chat channel was already failing before this change. It gets fixed in stage 2, when the channels stop opening the mail store directly and ask the sidecar over HTTP.