# AGENTS.md — Build guide for Airmail

## Project scope
Build a single-account desktop-accessible inbox that caches IMAP headers/text, searches offline, queues archive actions and sends reviewed drafts through the account's SMTP service.

Catalogue verdict: kinda. The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Airmail, create a configurable Apple email client with actions, snooze, and send later. The hard boundary is native apps, provider quirks, automation, push, and years of compatibility work, plus mail infrastructure, integrations, and client polish.
Use the implementation prompt below to define the deliverable. Complete each phase's acceptance checks before extending the scope.

## Working agreement
- Inspect the repository and its existing instructions before choosing paths, dependencies or commands. Keep one coherent stack and explain changes to the proposed architecture.
- Plan a vertical slice that accepts a real input and produces the useful output described below. Persist only the state the prompt calls for; respect memory-only and upstream-managed workflows. Use fixtures only when they are clearly labelled.
- After scaffolding, document the actual install, development, check and build commands in README and keep them synchronized with the package or project manifest. Do not report commands as successful unless they ran.
- Work in small steps. At handoff, list implemented flows, checks actually performed, remaining blockers, and any credentials or provider setup the owner must supply.
- Do not publish, spend money, contact customers, delete source data or run irreversible migrations without the project owner's authorization.

## Prerequisites
- Runtime and tools: Python, PySide6, imaplib, smtplib and SQLite FTS5, with credentials in the operating-system keyring.
- Before starting: Python with PySide6, one authorized IMAP/SMTP account over TLS and its supported app-password or OAuth configuration.

## Stack and architecture
- Python, PySide6, imaplib, smtplib and SQLite FTS5, with credentials in the operating-system keyring
- Data design: Store Mailbox, UIDValidity, MessageUID, CachedBody, PendingAction and OutboxAttempt; a UID is meaningful only within its mailbox generation. Keep local draft identity independent of server IDs.
- Setup: Python with PySide6, one authorized IMAP/SMTP account over TLS and its supported app-password or OAuth configuration

## Security and data integrity
- Treat cached mail as a replica. Queue server mutations with their mailbox generation/UID; reconcile ambiguous sends before retry, keep HTML inert and block remote images by default.
- Use the existing Python/PySide6 IMAP/SMTP approach for the native client; keep credentials in the OS keyring. Snooze and send-later work only while its worker runs; HTML mail stays inert and remote images blocked.
- Keep secrets outside client bundles and exported projects; document what leaves the device and make retention/deletion controls visible.

## Agent implementation rules
- Project rule — domain: Store Mailbox, UIDValidity, MessageUID, CachedBody, PendingAction and OutboxAttempt; a UID is meaningful only within its mailbox generation. Keep local draft identity independent of server IDs.
- Project rule — scope and recovery: Use the existing Python/PySide6 IMAP/SMTP approach for the native client; keep credentials in the OS keyring. Snooze and send-later work only while its worker runs; HTML mail stays inert and remote images blocked.
- Project rule — acceptance: Change the mailbox UID validity during a resync and interrupt an SMTP send after upload; rebuild the affected cache and mark the send outcome unknown rather than sending a duplicate.
- Project rule — delivery: document real setup commands and permissions; do not claim a build, accuracy level, performance result or security certification that has not been demonstrated.

## Optional agent skills and references
- Recommended skill: [modern-python](https://github.com/trailofbits/skills/blob/main/plugins/modern-python/skills/modern-python/SKILL.md) — structure the Python worker or explicitly optional read-only utility with pinned dependencies, typed boundaries and clear failure handling. Follow the maintainer's installation instructions and match its requirements to the chosen runtime.
- Recommended skill: [sharp-edges](https://github.com/trailofbits/skills/blob/main/plugins/sharp-edges/skills/sharp-edges/SKILL.md) — review configuration and API defaults against the app-specific invariants and recovery boundaries above; this is not a security certification. Follow the maintainer's installation instructions and match its requirements to the chosen runtime.

Read the linked SKILL.md and its dependencies before adding a skill. Select only the skills matching this project's runtime and task; their documentation does not supply API access, credentials or approval to perform external actions. Pin the reviewed revision where the tool supports it. Follow the chosen agent's documented project-level installation mechanism.

## Distribution ideas
These are optional planning notes. Obtain the owner's approval before publishing or contacting anyone.
- Demonstrate this working slice using synthetic or explicitly authorized non-sensitive examples: Build a single-account desktop-accessible inbox that caches IMAP headers/text, searches offline, queues archive actions and sends reviewed drafts through the account's SMTP service.
- Share a synthetic example export and the acceptance walkthrough; keep real customer, health, financial and source data private: Change the mailbox UID validity during a resync and interrupt an SMTP send after upload; rebuild the affected cache and mark the send outcome unknown rather than sending a duplicate.
- State the limits before asking someone to replace their existing tool: Use the existing Python/PySide6 IMAP/SMTP approach for the native client; keep credentials in the OS keyring. Snooze and send-later work only while its worker runs; HTML mail stays inert and remote images blocked.

## Engineering roadmap
1. Phase 1 — Pin the working slice and create its example input: Build a single-account desktop-accessible inbox that caches IMAP headers/text, searches offline, queues archive actions and sends reviewed drafts through the account's SMTP service. Confirm setup: Python with PySide6, one authorized IMAP/SMTP account over TLS and its supported app-password or OAuth configuration.
2. Phase 2 — Implement persistence and write-time invariants before decorating the UI: Store Mailbox, UIDValidity, MessageUID, CachedBody, PendingAction and OutboxAttempt; a UID is meaningful only within its mailbox generation. Keep local draft identity independent of server IDs.
3. Phase 3 — Connect the working view to real saved state. Treat cached mail as a replica. Queue server mutations with their mailbox generation/UID; reconcile ambiguous sends before retry, keep HTML inert and block remote images by default.
4. Phase 4 — Expose the app-specific limits and recovery path in context: Use the existing Python/PySide6 IMAP/SMTP approach for the native client; keep credentials in the OS keyring. Snooze and send-later work only while its worker runs; HTML mail stays inert and remote images blocked.
5. Phase 5 — Walk through this concrete acceptance case and preserve its exported evidence: Change the mailbox UID validity during a resync and interrupt an SMTP send after upload; rebuild the affected cache and mark the send outcome unknown rather than sending a duplicate. Finish the README and backup/restore instructions; report unfinished capabilities explicitly.

## Paid-product capabilities outside this build
- native apps, provider quirks, automation, push, and years of compatibility work
- mail hosting and deliverability
- push on every platform
- advanced team collaboration
- provider-specific AI and search

## Implementation prompt
Build me a local desktop mail triage client for one IMAP mailbox as a limited substitute for Airmail. Requirements:

- Use Python, PySide6, imaplib, smtplib, and SQLite FTS5. Connect to one user-configured IMAP and SMTP account over TLS; keep credentials in the OS keyring rather than the database.
- Sync inbox headers and plain-text bodies into a local SQLite cache. Show sender, subject, date, unread status, and an offline search box.
- Keyboard actions: next, previous, mark read, archive to a configured folder, reply, and compose. Show a pending-action queue and retry failures without hiding server state.
- Add snooze by moving a message to a configured folder and restoring it when the app is running at the selected time. Add send later with a local outbox that sends only while the app is running; show unsent count on quit.
- Render plain text by default. For HTML-only messages, strip active content and block remote images; let me open the original in my existing mail client if needed.
- Send through SMTP with a confirmation screen showing recipients and attachments. Keep drafts local until the server accepts the message.
- No accounts beyond this mailbox, telemetry, universal OAuth support, multi-account inbox, perfect threading, or push guarantees. README: provider setup, app password or OAuth limits, cache path, and queue recovery.

EDITORIAL IMPLEMENTATION CONTRACT
Working slice: Build a single-account desktop-accessible inbox that caches IMAP headers/text, searches offline, queues archive actions and sends reviewed drafts through the account's SMTP service.
Data and invariants: Store Mailbox, UIDValidity, MessageUID, CachedBody, PendingAction and OutboxAttempt; a UID is meaningful only within its mailbox generation. Keep local draft identity independent of server IDs.
Boundary and recovery: Use the existing Python/PySide6 IMAP/SMTP approach for the native client; keep credentials in the OS keyring. Snooze and send-later work only while its worker runs; HTML mail stays inert and remote images blocked.
Acceptance walkthrough: Change the mailbox UID validity during a resync and interrupt an SMTP send after upload; rebuild the affected cache and mark the send outcome unknown rather than sending a duplicate.
Record actual dependency versions, permissions and provider access in setup instructions. Preserve originals, expose partial failures and document backup/restore. These are acceptance requirements, not a claim of a completed or production-certified build. Add the domain, recovery and acceptance rules to AGENTS.md so future edits preserve them.

## Completion evidence
Demonstrate the prompt's acceptance scenarios against the scoped workflow. Include setup from a clean checkout and failure recovery. Check persistence across restart and export/restore only for the state the prompt says to store; for memory-only tools, confirm that temporary content is discarded as specified. Record actual results and remaining limitations. A detailed plan alone does not establish a working replacement.
