fs-mail 2.3.0: download controls and selective-download proposal
===============================================================

STATUS

fs-mail 2.3.0 implements folder inclusion/exclusion and a per-run raw-message
payload budget. It still downloads a selected message as a complete original,
including its MIME attachments. Header-only views, selective body retrieval,
and deferred attachment retrieval are proposals in this document.

--download-message, --download-folder, --folder-download, and
--download-attachments are NOT implemented command options. Their proposed
modifiers and examples below are not current command syntax. Read the shipped
manual or fs-mail -h for the implemented command interface.

IMPLEMENTED IN 2.3.0

The built-in download policy is:

    download_folders = *
    download_exclude =
    max_download_mb_per_run = 0
    max_messages_per_run = 500
    max_message_mb = 50

These are supported behavior settings in config.ini's shared [mail] section,
in an accounts.ini [account NAME] override, and through --set KEY=VALUE.
They can vary per account. For example, this command actually works now:

    fs-mail --receive-only --set download_folders=INBOX \
        --set max_download_mb_per_run=25

It downloads complete Inbox messages within a 25 MiB raw-message payload
budget for each selected account. It neither sends queued drafts nor pushes
local IMAP changes. Add -a NAME to select one account.

A persistent example is:

    [mail]
    download_folders = INBOX, Archive
    download_exclude = Trash, Junk
    max_download_mb_per_run = 25

download_folders and download_exclude contain comma-separated shell-style
patterns for remote IMAP folder names. Exclusions win. * can match a hierarchy
delimiter; matching is case sensitive except the special INBOX name. Quote a
pattern containing a comma as one CSV field. The default * includes all
selectable folders, so an uncustomized initial download can still be large.

Filters control new message-body downloads. They do not remove existing local
files or disable synchronization of already tracked mail in excluded folders.
Normal and --sync-only runs can still push those messages' pending moves,
deletions, or flags. Use --receive-only when remote changes are not wanted.

max_download_mb_per_run is a complete raw-message payload budget per account
pass. Zero means unlimited. Protocol metadata and TLS overhead are additional.
The advertised size is reserved before fetching a body. A message that will
not fit the remaining budget is left pending; smaller messages may still fit.
Pending identities are retained across runs even when the scan advances. A
message larger than the whole per-run budget reports an error until the limit
is increased or set to zero. Failed attempts retain their reservations because
some bytes may already have crossed the connection.

max_messages_per_run separately bounds attempted messages per direction, and
max_message_mb separately bounds each whole message. Lowering the latter can
skip a large message entirely; it does not retain just its readable body.
Hiding Attachments through view_headers also changes presentation only.

The implemented offline policy preview is:

    fs-mail -n --receive-only --set download_folders=INBOX \
        --set max_download_mb_per_run=25

It reports settings without opening mail state, creating roots, or connecting.
It cannot list messages or parts that have not been discovered on the server.
Ordinary --status and other -n inspections retain their documented local
recovery behavior; they do not provide a proposed selective-transfer preview.

PURPOSE

Keep the filesystem useful as the user's mail interface without requiring a
complete offline copy of every message and attachment. Separate discovering
mail, fetching readable message bodies, and fetching attachment payloads.

PROPOSED COMMANDS

    fs-mail --download-message email/inbox/msg.txt
    fs-mail --download-folder email/Archive/
    fs-mail --download-attachments email/inbox/msg.txt

--download-message FILE...
    Fetch the readable body of each identified message. Leave attachments
    remote unless --with-attachments is supplied.

--download-folder DIR...
    Fetch readable message bodies in each mapped IMAP folder. The directory
    operand is a local account-folder path; the program resolves the remote
    mailbox through its saved mapping. Include subfolders only with --recursive.
    Leave attachment payloads remote unless --with-attachments is supplied.

--folder-download DIR...
    A compatibility alias for --download-folder. Documentation should use the
    verb-first spelling consistently with --download-attachments.

--download-attachments FILE...
    Fetch the identified messages' pending attachment payloads. Save ordinary
    files under each account's attachments/ directory and update the readable
    message's local attachment inventory. Reuse existing verified files.
    A later extension can select individual attachments by a stable part ID;
    do not guess selection from a potentially duplicate filename.

--with-attachments
    Modifier for message/folder retrieval. Explicitly request a full local copy
    of the selected contents. Downloading a folder must not silently enable it.

--recursive
    Modifier for folder retrieval. Include mapped descendants. A directory
    operation without this option is limited to that mailbox itself.

--dry-run
    Modifier for download actions. Show which messages/parts would be fetched
    and known size estimates without transferring their body payloads.
    Refreshing remote metadata, if requested, must be distinguished from a
    completely offline inspection. Estimates may differ from actual wire bytes.

--
    End option parsing so paths that begin with a dash can be used literally.

Example combinations:

    fs-mail --download-folder email/Archive/
    fs-mail --download-folder --with-attachments email/Archive/
    fs-mail --download-folder --recursive email/Projects/
    fs-mail --download-attachments email/inbox/first.txt email/inbox/second.txt
    fs-mail --download-folder --dry-run email/Archive/

PROPOSED NORMAL RUN POLICY

An ordinary run should update the folder/message listing, reconcile authorized
local changes, process eligible outbox submissions, and apply the configured
download policy. Initial discovery must not imply full offline mirroring.

A useful proposed default is readable bodies for Inbox, headers for other
visible folders, and deferred attachments. Configuration must permit different
policies per account and folder, including an explicit full-mirror policy.
Folder inclusion/exclusion, byte budgets, message-count limits, and large-file
limits already have separate settings in 2.3.0 as described above. A future
partial-download policy must preserve or explicitly migrate those meanings.
The additional body-versus-header and attachment-deferral keys, state model,
parser, and manual are still to be implemented. The proposed default above
does not describe the current default.

FILESYSTEM CONTRACT

1. A header-only message is a real tracked .txt file with a clear statement that
   its body is not downloaded. Missing body text must not look like an empty
   original message. Replies require the needed source text to be fetched or
   the user to explicitly compose without it.

2. Distinguish remote attachment inventory from downloaded local paths. Show
   names, content types, known sizes, and a pending/downloaded status. Do not
   create zero-byte placeholders that look like successful attachment files.
   MIME attachment identity is separate from the safe local filename.

3. Attachments: should name usable absolute local file paths. Pending attachment
   metadata needs a separate, specified field or section. Copying a received
   message into outbox must not silently omit pending attachments: require an
   explicit choice to fetch them or remove the pending entries from the draft.

4. Merely opening an ordinary .txt file cannot trigger a network download or
   reliably mark it read. Keep those actions explicit. Users can invoke the
   command from bash, Midnight Commander, or a file-manager custom action.

5. Renaming or moving a message inside an account does not change its stable
   local identity. Resolve it through account state, not its filename, subject,
   sender, or Message-ID alone. Resolve the current mailbox and UID after moves.

6. A deferred attachment is not a locally deleted attachment. A downloaded
   attachment removed by the user must not be automatically recreated by a
   metadata-only poll. An explicit download command may restore it after
   verifying the corresponding remote message or complete local raw copy.

7. Fully downloaded original .eml files remain useful for offline extraction.
   A partial download must not be labeled as a complete original message.

TRANSFER AND RECOVERY CONTRACT

Use IMAP MIME structure and individual BODY.PEEK sections to avoid transferring
unrequested attachments. Downloading a full raw message and merely suppressing
extraction cannot meet the bandwidth requirement.

Verify account identity, UIDVALIDITY, mailbox, UID, and recorded message identity
before retrieving deferred parts. If the original has disappeared or cannot be
verified after a mailbox reset, report the problem instead of fetching another
message that happens to have the same numeric UID.

Track part identity, expected representation, transfer encoding, size, local
path, completed hash, and publication state durably. Publish with no-overwrite
semantics. Matching completed files are reusable; changed files require a clear
decision. A crash during download or text-inventory update must be recoverable.

Respect account locks and preserve user edits, flags, and pending local changes
when updating attachment inventory or adding fetched body text. A download-only
command must not send outbox messages or apply unrelated remote deletions.

Partial views require a distinct state model. Code that currently assumes every
tracked message has a complete raw archive must be audited and adapted before
this feature is released. Deferred retrieval must remain safe after local moves,
remote moves, UIDVALIDITY changes, concurrent edits, and interrupted runs.

UNIX INTERFACE CONTRACT

- Accept ordinary absolute or relative paths and multiple operands.
- Infer each operand's account from explicit configured roots and saved state;
  report ambiguity. Never derive account roots from usernames.
- Keep normal results on stdout and diagnostics on stderr; retain per-account
  errors.log. --quiet suppresses routine progress, not errors.
- Use exit status 0 only when all requested work succeeds or is already complete.
  A partial batch failure returns nonzero while independent operands may finish.
- Honor -- as an option terminator; quote paths normally through the user's shell.
- Avoid prompts in unattended mode. Require an explicit overwrite/replacement
  choice instead of changing an edited local file automatically.
- Make repeated commands idempotent with respect to verified completed files.
- Document canonical long-option names and keep useful aliases consistent.

IMPLEMENTATION GATES

Add protocol tests proving that deferred attachment bytes are not requested,
not merely that extraction files are absent. Include multipart alternatives,
inline parts, nested attached messages, encoded filenames, very large parts,
part-number changes, lost replies, moved messages, and UIDVALIDITY resets.

Test header-only, body-complete, attachment-complete, and fully archived states
across filesystem edits and process restarts. Keep existing complete downloads
usable during migration. Update the actual CLI and manpage only when these
semantics are implemented and verified.
