-
released this
2026-08-20 13:14:15 -05:00 | 36 commits to main since this releaseM4: the mailbox's structure, not the messages in it.
create_folder,rename_folderanddelete_folder, behindMAIL_FOLDER_ADMIN— off by default, and off means the tools are absent rather than registered and refusing.Parallel to
MAIL_WRITABLE_FOLDERSrather than nested under it, which is the positionCALENDAR_ADMINalready takes: managing a folder is a different question from managing what is inside it, and neither implies the other.Where it diverges from the calendar, on purpose
delete_calendarreaches every collection on the account.rename_folderanddelete_folderact only on folders insideMAIL_FOLDERS, because the two allowlists are not the same kind of thing:CALENDARSselects among collections the user picked, whileMAIL_FOLDERSis the read boundary for this whole tenant and makes a folder invisible.Reaching past it would destroy mail this server was never permitted to read — and worse, the count guard below would answer "how many messages are in the folder you cannot see" for any name a caller cares to try, turning a guard into an oracle.
create_folderis necessarily exempt: the name does not exist yet, so there was nothing to have granted.delete_foldertakes guards, not aforceflagThe plan said
force=True. That is not a guard — it is a boolean a model sets while re-reading its own last refusal, andCLAUDE.mdhad already settled the question: destructive tools make the caller name what they are destroying.So it takes
expected_nameandexpected_message_count, the two-step shapedelete_calendarestablished. Calling without the count refuses and reports it, which puts the number in front of a human before the second call acknowledges it. A test pins both arguments into the schema and pinsforceout of it.The messages do not go to trash. They go with the folder — the second operation in this tenant that nothing can undo, after
delete_message(permanent=true). INBOX is refused because IMAP forbids it anyway; trash, sent, drafts and junk are refused because this tenant addresses them, and deleting trash destroys what the user throws away in order to be able to undo.rename_folderinvalidates handlesEvery outstanding
message_idnaming the old folder stops working, because a handle carries its folder name verbatim. Inherent, not a defect — but the description says so, or the model reuses handles it read moments earlier and reads the resulting "no such folder" as a failure rather than as its own doing.Renaming a folder out of
MAIL_FOLDERSis refused rather than warned about: this server loses sight of it immediately, including the ability to rename it back.Also
imap/adminlog phase — no folder name logged, only that it happened and, for a delete, how many messages went.folder_admin=on|offon the boot log'sgrantsline, and a preflight check for a gate that is on with nothing it may act on.- All three bust the
list_folderscache, or the next read serves a stale answer to a model that was just told the call succeeded.
Not verified
RENAMEhas never run against the live account.CREATEandDELETEwere exercised during probe 5;RENAMEwas not, and it is the one operation here carrying a live-verify condition. Unit tests cannot clear it — the fake renames a folder in a dict, and what needs checking is that a stale handle fails cleanly rather than resolving to a different message under a collidingUIDVALIDITY. Probe 10 is written up indocs/icloud-mail-plan.md.The same sentence was true of
move_messagefor four releases. Treat this as working-but-unproven.969 tests green.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads