0c90216c7a904a29e6ef8cb95fef353aa6d4ca35
The messages were in emails.db and the position — last_sync_at, and per-folder uidvalidity/lastuid — was a jsonb column on email_accounts in Postgres. Two stores for one fact, with an edge that only shows up when you try to move a mailbox to another machine. The expensive part of an email account is the first sync: hours of IMAP for a large mailbox, which is exactly why "copy emails.db to the new server" is the obvious way to bring one across. With the position in Postgres that silently does not work — the new server's column is empty, !last_sync_at says first sync, and the whole mailbox downloads again on top of the one just restored. The other direction is quieter and worse. Restore an OLDER emails.db while Postgres holds a NEWER position and the sidecar skips every message between the two, permanently, because nothing looks below lastuid again. Re-syncing is slow; skipping mail is data loss nobody notices. Not a new idea — the Gmail path already read SQLite and fell back to Postgres, backfilling so the fallback was taken once. Only the IMAP path had not followed. This extracts that pattern so both use one copy, and unifies the isFirstSync fork in accounts.ts, which is how the two drifted apart to begin with. The file wins over Postgres, always, and only migrates when it holds nothing at all. Topping up a partial position from Postgres would reintroduce precisely the divergence this removes. email_accounts.sync_meta is kept and marked legacy rather than dropped: it is the one-time backfill source for every account created before this, and dropping it would strand any that has not synced since. Nothing writes to it now. 11 tests on the migration, aimed at both expensive failures — migrating when we should not, and failing to migrate an account that predates the change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Description
No description provided
42 MiB
Languages
TypeScript
90.9%
Shell
4.7%
JavaScript
4.1%
CSS
0.2%
HTML
0.1%