Commit Graph
3 Commits
Author SHA1 Message Date
Lucas RayanandClaude Sonnet 5.5 67d0c8aa5a feat(protocols): conversations - thread_id and in_reply_to written at delivery, list and read APIs
A reply now joins the conversation of the message it answers (Message-ID,
In-Reply-To, References), falling back to the subject for replies with no
headers. /mail/threads lists a folder as conversations and /mail/thread/{id}
returns one. Replying from the webmail flags the answered message.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 16:38:36 -03:00
Lucas Rayan 183191d18d refactor(mail): one send path for Sent, a fixed lock order, and concurrency tests
- the webmail's Send no longer files its own Sent copy: it asks Submit to
  (FileSentCopy), the same path SMTP clients use, so the rule exists once
- every operation now locks the folders first (lowest id first) and the message
  after. Opposite moves, and a move racing a flag change, deadlocked (SQLSTATE
  40P01) because they took those locks in different orders; the new test
  reproduces it (10-19 s of retries, errors) and passes in ~1 s with the order
- TestOneMailboxEverywhere drives SMTP, IMAP and the webmail on one mailbox,
  mixing them, and checks after each step that every view agrees with the others
  and with the rows. It fails on the previous code and passes now.
2026-10-16 13:30:59 -03:00
Lucas Rayan caf52c0225 refactor(mail): one MailStore changes message state for every path
Flags, expunge, move, copy and folder deletion each existed twice: once for IMAP
(FlagRepository, ExpungeRepository, CopyRepository) and once for the webmail
(WebmailRepository). They kept the cached numbers true on their own and drifted:

- reading a message over IMAP never moved the folder's unread counter
- deleting in the webmail, or deleting a folder, never gave the quota back
- an IMAP expunge could push a counter below zero

MailStore now holds each state change once, in one transaction, with the same
rules everywhere: counters never go negative, a change that alters what a client
can observe raises the folder's modseq (one that alters nothing does not), the
quota follows the bytes, and a message is located by folder. The four repositories
are thin adapters that only translate how each path names a message.

The contract test added before this change (which failed on 8 of its cases)
now passes, as does the full suite.
2026-10-16 13:15:32 -03:00