Skip to content

Fix searchfolder-create's inert default --source_folders (issue #21) - #23

Merged
neilgfoster merged 2 commits into
mainfrom
worktree-agent-a6f0a69f3e05cebad
Jul 24, 2026
Merged

Fix searchfolder-create's inert default --source_folders (issue #21)#23
neilgfoster merged 2 commits into
mainfrom
worktree-agent-a6f0a69f3e05cebad

Conversation

@neilgfoster

Copy link
Copy Markdown
Owner

What and why

Fix searchfolder-create's inert default --source_folders (issue #21)

Conventions

  • Runtime stays stdlib-only, zero-dependency, zero-backend (urllib/json; ruff/pytest are
    dev tooling only).
  • No secrets/tokens in the repo — they live outside it in an XDG path (0600).
  • Any new/changed skill follows docs/AGENT-FRIENDLY.md (description + CLI I/O are the contract).
  • Read-only safety model intact: Mail.Read-only read path, scope ratchet for writes,
    verify-then-install (read-only catch-set), file-to-folder (never delete).

Verification

ruff check . && ruff format --check .
python3 -m pytest -q
  • ruff check . and ruff format --check . pass.
  • python3 -m pytest -q passes.

…er-create

Graph's mailSearchFolder creation API does not accept a well-known folder name
(e.g. "inbox") verbatim in sourceFolderIds the way destinationId fields elsewhere
in this codebase do — it silently produces a search folder with zero source
folders, whose filter can never match any mail regardless of how much matching
mail exists. Well-known names (including the documented `inbox` default) are now
resolved to real Graph folder ids via GET /me/mailFolders/{name} before creation,
and the verb verifies the created folder's sourceFolderIds actually applied,
refusing loudly instead of reporting a false success if it comes back short.

Fixes #21.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011NWPnfcJ9JXDvRThCCUcCH

@neilgfoster neilgfoster left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adversarial review (cold pass)

1. Possible false-positive refusal on duplicate source folder namesplugin/src/msgraph/verbs.py (the new applied_ids check after the creation POST): the refusal condition is len(applied_ids) < len(source_ids), a count-only comparison. If a caller passes --source_folders inbox inbox (or a well-known name plus a display name that happens to resolve to the same folder id), source_ids will have 2 entries but Graph will legitimately dedupe and return a sourceFolderIds array of length 1 on the created folder. The verb would then raise SteerError claiming only 1 of 2 requested source folders applied — even though the search folder was created correctly and its filter can match mail fine. This leaves an orphaned-but-functional search folder while telling the operator to go remove it. Comparing distinct/deduped ids (or deduping source_ids before building the request body) would avoid this false positive. Low likelihood in practice (requires a caller to pass overlapping --source_folders values) but worth a quick fix or an explicit acknowledgment that duplicate inputs aren't supported.

2. Minor: the new guard checks cardinality, not identity — if Graph ever returned the same count of ids but a different set (e.g. silently substituting one folder for another), the guard wouldn't catch it. This is consistent with what's actually implemented/described ("refuses loudly ... if it comes back short"), just narrower than the CHANGELOG's phrase "verifies the created folder's sourceFolderIds actually applied" might imply. No action required unless the CHANGELOG wording should be tightened.

No other correctness issues found. The forward-reference from _resolve_source_folder_id to _WELL_KNOWN_FOLDERS (defined later in the same module) is safe — Python resolves module-level names at call time. Test coverage for both the well-known-name and non-well-known display-name resolution paths, plus the short-count refusal case, looks solid.

cmd_searchfolder_create's post-creation short-list check compared
len(returned_ids) against len(source_ids) — the raw requested list.
A duplicate/aliased --source_folders input (two names resolving to
the same folder id) would trip a false-positive SteerError even
though the search folder was created correctly, since Graph
legitimately dedupes sourceFolderIds. Compare against the count of
distinct resolved ids instead.

Add a regression test covering duplicate source_folders input
resolving to the same id twice.
@neilgfoster
neilgfoster merged commit ce8a5e2 into main Jul 24, 2026
1 check passed
@neilgfoster
neilgfoster deleted the worktree-agent-a6f0a69f3e05cebad branch July 24, 2026 05:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants