mail move was not reversible. A message filed into any folder that is not
one of Mail's six special mailboxes could not be named again, so the move that
put it there could not be undone. Upgrade if you use mail move.
_mailbox_reference accepted inbox, drafts, sent, trash, junk and
outbox and nothing else, and four operations built their source scope
through it — reply, forward, move and delete. The selector is
every message of <scope> whose id = N, so a message anywhere else was
unreachable by id in all four. A live tester found it through move, because
move is the operation that puts messages there; the same limit had been
quietly constraining the other three since they were written.
The fix
The source scope now comes from the Envelope Index. messages.mailbox joins
mailboxes.url, which carries the account and the full path, so a message_id
resolves to its real mailbox from read-only SQLite with no Apple Event and
nothing launched — the route every other mail read already takes. mailbox
also accepts any real folder, with Account: Mailbox for one that two accounts
share. The six keywords are unchanged, because Mail.sdef declares them on the
application rather than on an account: they are a role unified across accounts,
not a name.
- Naming a
mailboxand amessage_idthat live apart is refused, not
resolved — that pairing is an assertion about which message this is, and
retargeting it silently would act on a different one. UnknownMailboxcarries areason(unknown,ambiguous,conflict,
unreadable), so a caller branches on a value and "the index would not open"
stays distinct from "no such mailbox".- A nested mailbox Mail will not name gets its own
NOSOURCEoutcome, raised
before thewhoseclause so no message is examined. It used to arrive as a
generic error thatmovethen blamed on the destination.
The fix shipped inert, which is the more useful finding
Both front ends passed mailbox=mailbox or "inbox" at nine call sites, pinning
the keyword branch and discarding the resolution. The handler was correct, every
call site read reasonably, the whole suite was green, and the feature did
nothing — because every test called the handler directly. It is now checked as a
pair, across the source of both front ends.
2051 tests pass.