Repository navigation
Make, rename and throw away a folder - #21
Merged
Merged
Conversation
mail_list_folders read folders and mail_move filed into them, but a folder itself could only be made in another mail client first -- so "put these in a folder called Belastingdienst" was a reasonable ask this server could not answer. mail_create_folder / mail_rename_folder / mail_delete_folder close that, on imap-tools' own folder commands. Two decisions shape them. A path is not the caller's to type. The hierarchy delimiter is "/" on one server and "." on the next, and a mailbox that keeps everything under INBOX refuses a bare top-level name -- so create takes a parent *folder* and the backend joins it with the delimiter it read off LIST, the same not-knowable rule the SPECIAL-USE roles are written with. A rename given a bare name keeps the folder where it is, for the same reason. Creating a folder that is already there is not a failure; it comes back created=False. And delete is the one genuinely irreversible thing in this package: IMAP's DELETE takes the folder's messages with it and no Trash catches them, which is exactly what mail_delete was written not to do. So it refuses a folder that still holds mail or sub-folders and says how many -- empty it the recoverable way first and the folder then goes. The confirm gate answers "did the user ask for this"; the refusal answers "can they get it back", so it has both. INBOX and the five special-use folders are refused outright rather than gated, by their flags or by special_folder's fallback name, since a server advertising no SPECIAL-USE still has the Trash this package files deletes into. tests/test_folder_admin.py pins the joining, the refusals and the emptiness check; the GreenMail e2e makes, fills, renames and deletes one for real. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W9TGZFHaq8x4vPvQ6kSQX6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #17.
mail_list_foldersread folders andmail_movefiled into them, but a folder itself could only be made in another mail client first. Three tools close that, onimap-tools' own folder commands:mail_create_folder(name, parent=None)mail_rename_folder(name, new_name)mail_delete_folder(name, confirm)confirm=true, and the folder must be emptyTwo decisions
A path is not the caller's to type. The delimiter is
/on one server and.on the next, and a mailbox that keeps everything under INBOX refuses a bare top-level name. So create takes aparentfolder and the backend joins it with the delimiter it read off LIST -- the same not-knowable rule the SPECIAL-USE roles are written with. A rename given a bare name keeps the folder where it is, for the same reason. Creating a folder that is already there is not a failure:created=False.Delete is the one genuinely irreversible thing in this package. IMAP's DELETE takes the folder's messages with it and no Trash catches them, which is exactly what
mail_deletewas written not to do. So it refuses a folder that still holds mail or sub-folders, with the count -- empty it the recoverable way first (mail_deletefiles the messages in Trash) and the folder then goes. The confirm gate answers "did the user ask for this"; the refusal answers "can they get it back", so it has both. INBOX and the five special-use folders are refused outright, by their flags or byspecial_folder's fallback name, since a server advertising no SPECIAL-USE still has the Trash this package files deletes into.Changes
providers/protocol.py-- three methods onMailProvider, read in the tool layer throughgetattrso a backend without them (the admin package's Graph provider) is refused by name rather than crashed into.providers/soverin/imap.py--create_folder/rename_folder/delete_folder, plus the delimiter lookup, the load-bearing-folder refusal and the emptiness check (STATUS, falling back to counting uids).tools/mail/folders.py,schemas.py,knowledge.py,README.md,CLAUDE.md.tests/test_folder_admin.py(34 tests) and four GreenMail e2e cases that make, fill, rename and delete a folder against a real server.Testing
make lintandmake test(316 passed) are green.make test-intwas not run: there is no Docker in this session, so the four new GreenMail cases are unverified against a real server -- CI covers them.🤖 Generated with Claude Code
https://claude.ai/code/session_01W9TGZFHaq8x4vPvQ6kSQX6
Generated by Claude Code