You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
download_if_missing — opt-in best-effort fetch for a server-side-only attachment (#272). #238 taught save_attachment to detect a not-downloaded attachment (savable_reason:"not_downloaded") but the remedy stayed manual ("open the message in Mail"). This adds an opt-in boolean (default false, fully backward-compatible): when both tiers fail on the not_downloaded / generic -10000 path AND the caller set the flag, the tool nudges Mail to materialize the message — reading source of theMessage (the raw RFC 822; non-GUI, no viewer window), which on an IMAP account pulls the server-side content into the local store as a side effect — then poll-retries the save on a bounded budget (~30s, 2s interval). Best-effort and unverified by design (Phase-0 spike, direction of #238(a)): Mail's scripting dictionary exposes no per-attachment download verb (downloaded is read-only, the only command is save), so this relies on an undocumented, version-/account-dependent materialization side effect that may not fetch on every account (notably where the save simply errors). It fails honestly — a spent budget throws the not_downloaded guidance, never a false "saved" — and the gate is a strict conjunction (notDownloaded && code==-10000 && downloadIfMissing) so every non-opt-in caller's behavior is byte-identical. Detection stayed on the SQLite path; only the state-changing fetch trigger + retry use AppleScript (per r-must-direct-db). New AttachmentDownloadScriptBuilder (pure trigger-script builder + gate + DownloadRetryPolicy) and MailController.saveAttachmentRetryingForDownload; 13 unit tests (script text, gate, policy, deterministic retry loop via the injected script-runner seam). The live question — does source-read actually pull a not-downloaded attachment down? — is a pre-merge gate (no not-downloaded specimen was available to live-test; source is confirmed a valid message property via the sdef). Post-verify hardening (6-AI APPROVE, PR #273): the retry loop is bounded by a real wall-clock deadline (not just a sleep-count that ignored each save's ~1-2s IPC), aborts immediately with the honest cause on a definitive "not found" instead of polling the full budget, logs a fetch-trigger failure to stderr, and uses a precise hasPrefix success check; the flag is documented as a silent no-op on Exchange/EWS (no .emlx) and when the local index is unavailable.