Accounts finishing done_with_errors showed only a count and a single
last_error. This adds a modal listing every concrete error of the
account's most recent run.
- migration 0005: account_errors table (kind folder|message|account,
folder, message_ref, error, created_at; ON DELETE CASCADE; indexed)
- store: AddAccountError / ClearAccountErrors / ListAccountErrors
- copy: OnError callback captures per-message error text (previously
only counted), with a "UID N: subject" reference
- orchestrator: clear errors at run start; persist folder/message/
account errors; cap 500 rows/account/run with a suppressed-note row
- api: GET /api/tasks/{id}/accounts/{accountId}/errors
- web: AccountErrorsModal, clickable ERRORS count, api + styles
Verified: migration applies on Postgres 18; store add/list/clear and
cascade tests pass against real pg; backend build/vet/test green; web
tsc+vite build and oxlint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U9Eq4JtWjyNTv5qat3B3mM
CopyFolder now streams envelope metadata via Next() in a first pass (dedup +
queue new messages), then streams bodies for new ones in a second pass —
no more blocking Collect of the whole folder with zero feedback, and memory
stays flat (only new-message meta is held).
- imapx: two-pass streaming CopyFolder + CopyDeps.OnScan(scanned,total)
- orchestrator: throttled 'scan' events during the metadata pass
- web: per-account 'scanning folder: X/N' line under the progress bar;
scan events kept out of the log to avoid flooding
Verified on greenmail: idempotency and internal-date preservation still hold.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMHQTtnQtQqL8muAXHr9kd
- orchestrator: per-account cancellable context registry + CancelAccount;
on cancel, close IMAP connections to unblock in-flight FETCH; account ends
in 'cancelled' status with a cancelled event
- imapx: CopyDeps.OnFolder callback fires after EXAMINE with the folder's
message count (before the long fetch) for visibility
- httpapi: POST /tasks/{id}/accounts/{accountId}/cancel
- web: per-row cancel button while running, folder event shows N messages,
cancelled/done_with_errors status badges
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMHQTtnQtQqL8muAXHr9kd