RESEARCH: mail plugin (from mailternal) — decisions, architecture options, grill questions (NO implementation) #257

Closed
opened 2026-09-27 18:15:50 +00:00 by kayg · 15 comments
Owner

Owner (2026-09-27, night): "we need to add a mail plugin soon: https://git.kayg.org/kayg/mailternal. I already tried to make a native macOS app which already kinda works. I suppose you will [have] all the important decisions there but I am sure that you will have lots more questions so I want you to assign a /codex-subagents to research that extensively and file a issue. have questions ready for me in the morning. don't implement anything unless you've grilled me relentlessly tomorrow."

Status: RESEARCH ONLY. Do not implement anything. The output is a research document and a grill question list for the owner.

Inputs

  • mailternal repo: https://git.kayg.org/kayg/mailternal (clone at ~/Developer/mailternal): a native macOS Swift app, 38 commits, modules MailternalIMAP / MIME / Sanitizer / Store / Sync / Interfaces, DECISIONS.md, docs/spec, research/ (ios-imap-relay-apns-2026, message-reader-layout-2026, swift-static-binaries-2026).
  • calternal: DESIGN.md (plugins §8, files-as-truth, single writer, calternal-fs, search/indexing, deep links §33, chrome §34, notifications/reminders §42, AI §10, sync), CONTEXT.md, the Rust plugin trait (crates/calternal-plugin), existing plugins as patterns (calendar with CalDAV, notes, files).

Research deliverables (write docs/research/mail-plugin.md, commit on a research branch, summary here)

  1. What mailternal already decided (DECISIONS.md, spec, research notes): list each decision, and whether it carries over to a server-side calternal plugin unchanged, needs adapting (Swift/macOS → Rust server + web), or conflicts with calternal's principles. Reusable pieces: protocol handling, sanitizer rules, sync state machine, store schema, deep-link grammar.
  2. Architecture options for a calternal mail plugin, with trade-offs (performance, security, reliability, effort):
    • Server-side IMAP client syncing into calternal (storage: Maildir-style files in the home as the source of truth? .eml per message? a DB cache only?), JMAP support, SMTP submission; OAuth2/XOAUTH2 for Gmail/Outlook/iCloud app passwords; IDLE/push; multiple accounts.
    • calternal as its own mail server (receive via SMTP/MX, e.g. with Stalwart or Maddy integration) vs. client only.
    • How mail integrates with the rest: search (the index, "stupid fast" search requirement), linked notes, tasks from mail, calendar invites (iCalendar/iMIP), contacts, attachments into Documents/Photos, share, AI (summaries, Ask), notifications, deep links for threads/messages.
    • Rendering/sanitising HTML mail safely in the web app (sandboxed iframe, CSP, remote image proxy, tracking-pixel blocking), dark mode for HTML mail.
    • Native apps later (the macOS app: keep, retire, or become a calternal client?).
  3. Rust ecosystem survey (web search, Sept 2026): IMAP (async-imap, imap-codec/imap-next, …), MIME parsing (mail-parser, mailparse), SMTP (lettre), JMAP clients, sanitisation (ammonia), DKIM/SPF/DMARC verification, full-text indexing of mail with the existing search stack; licences (AGPL-compatible only), maintenance status.
  4. Competitor/UX survey: Fastmail, Superhuman, Hey, Apple Mail, Mimestream, Spark, Proton, Notion Mail: what to copy for a calm, fast, keyboard-first mail UI in calternal's chrome rules (sidebar sub-views, one bottom capsule, overlays on desktop, sheets on phone).
  5. Risks: security (HTML mail, attachments, credential storage), deliverability if sending, storage size, sync correctness, provider rate limits and ToS.
  6. The grill: a numbered design tree of questions for the owner, grouped into rounds (frontier first), each with options and a recommended answer, ready to paste into the morning session. Aim for completeness: accounts, providers, storage model, sync scope, sending, UI layout, search, integrations, notifications, security, native app future, MVP scope and order.

Use web search extensively. No product code. Post the grill questions as a comment on this issue.

Owner (2026-09-27, night): "we need to add a mail plugin soon: https://git.kayg.org/kayg/mailternal. I already tried to make a native macOS app which already kinda works. I suppose you will [have] all the important decisions there but I am sure that you will have lots more questions so I want you to assign a /codex-subagents to research that extensively and file a issue. have questions ready for me in the morning. **don't implement anything unless you've grilled me relentlessly tomorrow.**" **Status: RESEARCH ONLY. Do not implement anything.** The output is a research document and a grill question list for the owner. ## Inputs - mailternal repo: `https://git.kayg.org/kayg/mailternal` (clone at `~/Developer/mailternal`): a native macOS Swift app, 38 commits, modules MailternalIMAP / MIME / Sanitizer / Store / Sync / Interfaces, `DECISIONS.md`, `docs/spec`, `research/` (ios-imap-relay-apns-2026, message-reader-layout-2026, swift-static-binaries-2026). - calternal: DESIGN.md (plugins §8, files-as-truth, single writer, calternal-fs, search/indexing, deep links §33, chrome §34, notifications/reminders §42, AI §10, sync), CONTEXT.md, the Rust plugin trait (`crates/calternal-plugin`), existing plugins as patterns (calendar with CalDAV, notes, files). ## Research deliverables (write `docs/research/mail-plugin.md`, commit on a research branch, summary here) 1. **What mailternal already decided** (DECISIONS.md, spec, research notes): list each decision, and whether it carries over to a server-side calternal plugin unchanged, needs adapting (Swift/macOS → Rust server + web), or conflicts with calternal's principles. Reusable pieces: protocol handling, sanitizer rules, sync state machine, store schema, deep-link grammar. 2. **Architecture options for a calternal mail plugin**, with trade-offs (performance, security, reliability, effort): - Server-side IMAP client syncing into calternal (storage: Maildir-style files in the home as the source of truth? .eml per message? a DB cache only?), JMAP support, SMTP submission; OAuth2/XOAUTH2 for Gmail/Outlook/iCloud app passwords; IDLE/push; multiple accounts. - calternal as its own mail server (receive via SMTP/MX, e.g. with Stalwart or Maddy integration) vs. client only. - How mail integrates with the rest: search (the index, "stupid fast" search requirement), linked notes, tasks from mail, calendar invites (iCalendar/iMIP), contacts, attachments into Documents/Photos, share, AI (summaries, Ask), notifications, deep links for threads/messages. - Rendering/sanitising HTML mail safely in the web app (sandboxed iframe, CSP, remote image proxy, tracking-pixel blocking), dark mode for HTML mail. - Native apps later (the macOS app: keep, retire, or become a calternal client?). 3. **Rust ecosystem survey (web search, Sept 2026)**: IMAP (async-imap, imap-codec/imap-next, …), MIME parsing (mail-parser, mailparse), SMTP (lettre), JMAP clients, sanitisation (ammonia), DKIM/SPF/DMARC verification, full-text indexing of mail with the existing search stack; licences (AGPL-compatible only), maintenance status. 4. **Competitor/UX survey**: Fastmail, Superhuman, Hey, Apple Mail, Mimestream, Spark, Proton, Notion Mail: what to copy for a calm, fast, keyboard-first mail UI in calternal's chrome rules (sidebar sub-views, one bottom capsule, overlays on desktop, sheets on phone). 5. **Risks**: security (HTML mail, attachments, credential storage), deliverability if sending, storage size, sync correctness, provider rate limits and ToS. 6. **The grill**: a numbered design tree of questions for the owner, grouped into rounds (frontier first), each with options and a recommended answer, ready to paste into the morning session. Aim for completeness: accounts, providers, storage model, sync scope, sending, UI layout, search, integrations, notifications, security, native app future, MVP scope and order. Use web search extensively. No product code. Post the grill questions as a comment on this issue.
Author
Owner

Started research on branch research/mail from dev at base SHA ac048aa4b8. I am reviewing Mailternal's decisions, specs, research notes and Swift sources, calternal's plugin/search/security/deep-link rules, and current Rust mail libraries and mail UX. Research only; no product code.

Started research on branch research/mail from dev at base SHA ac048aa4b8828ada23e2e6145739a70e146af1df. I am reviewing Mailternal's decisions, specs, research notes and Swift sources, calternal's plugin/search/security/deep-link rules, and current Rust mail libraries and mail UX. Research only; no product code.
Author
Owner

9. Grill questions for the owner

Recommendations are proposals to discuss. They are not settled calternal
decisions. The first round covers the frontier choices.

Round 1 — What product is Mail?

1. Is Mail a client for existing mailboxes, or does calternal host mail?

Options: (A) Connect external mail providers only. (B) Connect providers and
offer optional hosted mail later. (C) Host mail with MX and SMTP from the
first release.

Recommendation: A. Mail hosting adds DNS, port 25, spam, abuse, and
deliverability work. Keep it outside the first Mail Plugin.

2. What is the source of truth for a connected mailbox?

Options: (A) The provider is canonical and the Index is a rebuildable
projection. (B) Every synced message becomes an .eml file in Home. (C) Keep
the projection and let the User export selected messages as .eml.

Recommendation: C. Keep the provider as the live source and allow
explicit durable exports through Files. Confirm that a rebuildable Index
projection is acceptable under “file over app.”

3. How much of Mailternal becomes the first calternal release?

Options: (A) Read, search, and read-state only. (B) Add archive, move, and
trash. (C) Add compose and send too.

Recommendation: A. Prove account auth, sync, search, link identity,
HTML safety, and deletion recovery before adding more provider writes.

4. Is the Mail Plugin optional?

Options: (A) Compile it as a Core Plugin, disabled until an admin enables it.
(B) Enable it by default for each Instance. (C) Build it as a future
community Plugin.

Recommendation: A. This matches the current compiled Plugin model and
keeps mailbox routes closed until enabled. The owner must add Mail to the
current Plugin catalog.

Round 2 — Providers, accounts, and sync

5. Which providers launch first?

Options: (A) Generic IMAP only. (B) Fastmail JMAP plus generic IMAP and
iCloud app passwords. (C) Add Gmail and Microsoft OAuth in the first
release.

Recommendation: B. It tests two protocols and a documented app-password
provider. Keep Gmail behind the Google review decision. Add Microsoft OAuth
when tenant setup and provider policy are tested.

6. Who owns OAuth client registration and callback URLs?

Options: (A) A calternal-managed broker for each provider. (B) Each
self-hosted Instance registers its own OAuth app. (C) Start with app
passwords, add provider OAuth later.

Recommendation: C for the first connector. For hosted service OAuth,
use a fixed callback with short-lived, Instance-bound state. Keep long-lived
tokens at the User's Instance. For self-hosting, document whether the owner
must bring provider OAuth credentials.

7. Is Gmail worth the restricted-scope cost?

Options: (A) Defer Gmail until the product has a security assessment budget.
(B) Support Gmail API with a narrower scope if it covers the chosen actions.
(C) Support Gmail IMAP through an app password.

Recommendation: A until the owner approves budget and privacy terms.
Check if Gmail API scopes can cover the exact read-only MVP before rejecting
that option.

8. How many provider mailboxes can one User connect?

Options: (A) One mailbox at first; schema can support more. (B) Many
mailboxes from day one, each with its own views. (C) Many mailboxes and a
unified Inbox at launch.

Recommendation: A. Keep the provider model extensible but avoid a
unified inbox before identity, search, notification, and account-switching
rules are tested.

9. Which history should sync?

Options: (A) Headers and previews only. (B) Full text history, bounded and
resumable. (C) A fixed recent window such as 30 days.

Recommendation: B if provider-as-source and the Instance quota allow
it. Show backfill progress and the oldest synced date. Do not silently skip
messages while advancing a durable cursor.

10. How should mail changes arrive?

Options: (A) Poll IMAP folders on a schedule. (B) IMAP IDLE plus delta
reconciliation, and JMAP EventSource where available. (C) Require provider
push subscription callbacks.

Recommendation: B. Keep the protocol state as the source of truth. Use
push only to wake a bounded delta job. Avoid provider callback URLs until
their SSRF and verification behavior is designed.

11. Should attachments sync before a User opens them?

Options: (A) Fetch all attachments. (B) Fetch only when opened, then use a
bounded cache. (C) Fetch images automatically but defer other files.

Recommendation: B. Add an explicit Save action to put a file in Home.
Do not let HTML auto-fetch image URLs.

12. What happens when a User disconnects a provider?

Options: (A) Revoke credentials and delete the live projection. (B) Keep
the full local copy. (C) Ask whether to delete or export first.

Recommendation: C, with “revoke access and delete the projection” as
the clear default. .eml files already saved in Home remain normal files.

Round 3 — Provider writes and integrations

13. When does calternal add archive, move, and delete?

Options: (A) First release. (B) After read, search, and \Seen are stable.
(C) Do not support provider writes; link to provider.

Recommendation: B. Make every action durable, idempotent, and
generation-scoped. Add undo only after its provider semantics are clear.

14. Which send route should compose use?

Options: (A) Provider JMAP EmailSubmission, else authenticated SMTP. (B)
Direct-to-MX from calternal. (C) No send until a separate send milestone.

Recommendation: C for MVP, then A. Do not add a public sending MTA to
the calternal host.

15. Should a User create a Task from a message?

Options: (A) Add a “Make task” route using Notes and link back to mail. (B)
Copy message text into a new task. (C) Do not add this.

Recommendation: A, after Mail identity and links are stable. Keep the
Task as a Note with task frontmatter. Do not copy the body by default.

16. How should calendar invites work?

Options: (A) Detect and preview invites only. (B) Accept or decline on sync.
(C) Add a user action that updates Calendar and sends an iMIP reply.

Recommendation: A first, then C. Never accept or decline because a
message was downloaded. Decide how to recover if Calendar and mail writes
have different results.

17. Should senders become Contacts automatically?

Options: (A) Show address details only. (B) Create Contacts on every new
sender. (C) Add an explicit “Create contact” action later.

Recommendation: A for launch, then C when Contacts exists.

18. Can a User share mail with a Group or Public link?

Options: (A) No; save a message as .eml first. (B) Share a selected message
through a private stable route. (C) Share a whole provider folder.

Recommendation: A. Normal Shares are designed for files and folders in
the Data directory. Do not make provider mailbox access a Share.

Round 4 — Search, safe reading, and notifications

19. What does global Search index?

Options: (A) Sender and subject only. (B) Sender, subject, recipients, and
body text. (C) Add attachment text and embeddings too.

Recommendation: B. Use Tantivy and exact search first. Add attachment
text or vectors only after costs, retention, and deletion behavior are
measured.

20. Should Ask and Agents use mail content?

Options: (A) No mail access. (B) Search and summarize only after an explicit
User action. (C) Send every new message to a model automatically.

Recommendation: B. Use authenticated Mail routes and citations. Never
expose provider credentials to the Agent container. Never summarize or send
mail to an external model by default.

21. When may HTML mail load remote content?

Options: (A) Never. (B) Only after a User selects “Load images.” (C)
Automatically through a proxy.

Recommendation: B after a secure image fetch boundary exists. Until
then, keep all remote content blocked. A direct request from the browser or
server reveals data.

22. What should a push notification show?

Options: (A) Generic “New mail.” (B) Sender and subject. (C) User-configured
detail by mailbox and Installation.

Recommendation: A by default. Let the User opt into details per
Installation. Never notify for backfill.

Round 5 — Reader, navigation, and native clients

23. Is Mail a new top-level mode?

Options: (A) Yes; it gets a mode icon and a sidebar of mail views. (B) No;
mail is a Search-only feature. (C) Add a mail panel inside Calendar.

Recommendation: A if Mail is a first-class product area. The mode tray
is full, so the owner must choose which mode moves. Do not add a second
pill bar.

24. What is the default reader layout?

Options: (A) Message list with a selected reader in the main area. (B) A
permanent three-column window on every screen. (C) A right-hand info
sidebar.

Recommendation: A. Use a responsive list and reader on desktop; open a
message as the main page or sheet on a phone. Do not create a right info
sidebar. A three-pane desktop option can be tested later.

25. Should calternal group mail into threads?

Options: (A) No threads. (B) Provider thread ID or strict RFC graph. (C)
Subject-based grouping.

Recommendation: B, with a chronological view always available. Never
merge by subject alone.

26. Should categories or priority inboxes sort mail by default?

Options: (A) Chronological only. (B) Optional categories and a chronological
All Mail view. (C) Forced AI-ranked Inbox.

Recommendation: B. Keep the category count small and let Users fix
misclassified senders.

27. What keyboard model should Mail use?

Options: (A) Fixed Gmail-style shortcuts. (B) User-selectable shortcut
presets. (C) The calternal shortcut registry, with any user remapping added
later.

Recommendation: C for the first release. Keep shortcuts in one
registry, test against browser keys, and provide a ? help sheet.

28. What is the deep-link identity?

Options: (A) Provider URL or IMAP UID. (B) RFC Message-ID header. (C) Stable
calternal item ID with provider coordinates kept behind it.

Recommendation: C. Message-ID can be absent or duplicated. UIDVALIDITY
can change. Every message and thread needs Copy link.

29. What is the future for the Swift Mailternal app?

Options: (A) Keep it as a separate product with direct provider sync. (B)
Retire it. (C) Turn it into a native calternal client.

Recommendation: C if a native Mail surface is still wanted. Keep one
mailbox sync owner on the server. If native support is not funded, choose B
instead of maintaining two stores.

Round 6 — MVP order and release gates

30. What order should the work follow?

Options: (A) Account linking, read, search, then actions and send. (B)
Compose and send before search. (C) Build a mail server and client together.

Recommendation: A. The first vertical slice should connect one provider,
sync recent Inbox items safely, open text and sanitized HTML, search them,
and restore a stable message link.

31. What must be true before this Plugin is enabled for an Instance?

Options: (A) A scripted protocol test only. (B) Protocol tests, sync
recovery tests, per-User authorization tests, malicious MIME/HTML tests,
provider checks, and one real-server adversarial round. (C) A product demo.

Recommendation: B. Keep the first release to a time-boxed test round.
Fix crashes, data loss, cross-User access, sync collisions, and unsafe
network fetches. Do not turn a cosmetic issue into a launch gate.

32. What is the release measure for “fast enough”?

Options: (A) Measure only app launch. (B) Measure newest Inbox mail visible,
search latency, mailbox page latency, server memory, and background sync
load. (C) Do not measure until after launch.

Recommendation: B. Use a small and a large mailbox, and set limits for
provider connections and backfill before opening this to all Users.

## 9. Grill questions for the owner Recommendations are proposals to discuss. They are not settled calternal decisions. The first round covers the frontier choices. ### Round 1 — What product is Mail? **1. Is Mail a client for existing mailboxes, or does calternal host mail?** Options: (A) Connect external mail providers only. (B) Connect providers and offer optional hosted mail later. (C) Host mail with MX and SMTP from the first release. **Recommendation:** A. Mail hosting adds DNS, port 25, spam, abuse, and deliverability work. Keep it outside the first Mail Plugin. **2. What is the source of truth for a connected mailbox?** Options: (A) The provider is canonical and the Index is a rebuildable projection. (B) Every synced message becomes an .eml file in Home. (C) Keep the projection and let the User export selected messages as .eml. **Recommendation:** C. Keep the provider as the live source and allow explicit durable exports through Files. Confirm that a rebuildable Index projection is acceptable under “file over app.” **3. How much of Mailternal becomes the first calternal release?** Options: (A) Read, search, and read-state only. (B) Add archive, move, and trash. (C) Add compose and send too. **Recommendation:** A. Prove account auth, sync, search, link identity, HTML safety, and deletion recovery before adding more provider writes. **4. Is the Mail Plugin optional?** Options: (A) Compile it as a Core Plugin, disabled until an admin enables it. (B) Enable it by default for each Instance. (C) Build it as a future community Plugin. **Recommendation:** A. This matches the current compiled Plugin model and keeps mailbox routes closed until enabled. The owner must add Mail to the current Plugin catalog. ### Round 2 — Providers, accounts, and sync **5. Which providers launch first?** Options: (A) Generic IMAP only. (B) Fastmail JMAP plus generic IMAP and iCloud app passwords. (C) Add Gmail and Microsoft OAuth in the first release. **Recommendation:** B. It tests two protocols and a documented app-password provider. Keep Gmail behind the Google review decision. Add Microsoft OAuth when tenant setup and provider policy are tested. **6. Who owns OAuth client registration and callback URLs?** Options: (A) A calternal-managed broker for each provider. (B) Each self-hosted Instance registers its own OAuth app. (C) Start with app passwords, add provider OAuth later. **Recommendation:** C for the first connector. For hosted service OAuth, use a fixed callback with short-lived, Instance-bound state. Keep long-lived tokens at the User's Instance. For self-hosting, document whether the owner must bring provider OAuth credentials. **7. Is Gmail worth the restricted-scope cost?** Options: (A) Defer Gmail until the product has a security assessment budget. (B) Support Gmail API with a narrower scope if it covers the chosen actions. (C) Support Gmail IMAP through an app password. **Recommendation:** A until the owner approves budget and privacy terms. Check if Gmail API scopes can cover the exact read-only MVP before rejecting that option. **8. How many provider mailboxes can one User connect?** Options: (A) One mailbox at first; schema can support more. (B) Many mailboxes from day one, each with its own views. (C) Many mailboxes and a unified Inbox at launch. **Recommendation:** A. Keep the provider model extensible but avoid a unified inbox before identity, search, notification, and account-switching rules are tested. **9. Which history should sync?** Options: (A) Headers and previews only. (B) Full text history, bounded and resumable. (C) A fixed recent window such as 30 days. **Recommendation:** B if provider-as-source and the Instance quota allow it. Show backfill progress and the oldest synced date. Do not silently skip messages while advancing a durable cursor. **10. How should mail changes arrive?** Options: (A) Poll IMAP folders on a schedule. (B) IMAP IDLE plus delta reconciliation, and JMAP EventSource where available. (C) Require provider push subscription callbacks. **Recommendation:** B. Keep the protocol state as the source of truth. Use push only to wake a bounded delta job. Avoid provider callback URLs until their SSRF and verification behavior is designed. **11. Should attachments sync before a User opens them?** Options: (A) Fetch all attachments. (B) Fetch only when opened, then use a bounded cache. (C) Fetch images automatically but defer other files. **Recommendation:** B. Add an explicit Save action to put a file in Home. Do not let HTML auto-fetch image URLs. **12. What happens when a User disconnects a provider?** Options: (A) Revoke credentials and delete the live projection. (B) Keep the full local copy. (C) Ask whether to delete or export first. **Recommendation:** C, with “revoke access and delete the projection” as the clear default. .eml files already saved in Home remain normal files. ### Round 3 — Provider writes and integrations **13. When does calternal add archive, move, and delete?** Options: (A) First release. (B) After read, search, and \Seen are stable. (C) Do not support provider writes; link to provider. **Recommendation:** B. Make every action durable, idempotent, and generation-scoped. Add undo only after its provider semantics are clear. **14. Which send route should compose use?** Options: (A) Provider JMAP EmailSubmission, else authenticated SMTP. (B) Direct-to-MX from calternal. (C) No send until a separate send milestone. **Recommendation:** C for MVP, then A. Do not add a public sending MTA to the calternal host. **15. Should a User create a Task from a message?** Options: (A) Add a “Make task” route using Notes and link back to mail. (B) Copy message text into a new task. (C) Do not add this. **Recommendation:** A, after Mail identity and links are stable. Keep the Task as a Note with task frontmatter. Do not copy the body by default. **16. How should calendar invites work?** Options: (A) Detect and preview invites only. (B) Accept or decline on sync. (C) Add a user action that updates Calendar and sends an iMIP reply. **Recommendation:** A first, then C. Never accept or decline because a message was downloaded. Decide how to recover if Calendar and mail writes have different results. **17. Should senders become Contacts automatically?** Options: (A) Show address details only. (B) Create Contacts on every new sender. (C) Add an explicit “Create contact” action later. **Recommendation:** A for launch, then C when Contacts exists. **18. Can a User share mail with a Group or Public link?** Options: (A) No; save a message as .eml first. (B) Share a selected message through a private stable route. (C) Share a whole provider folder. **Recommendation:** A. Normal Shares are designed for files and folders in the Data directory. Do not make provider mailbox access a Share. ### Round 4 — Search, safe reading, and notifications **19. What does global Search index?** Options: (A) Sender and subject only. (B) Sender, subject, recipients, and body text. (C) Add attachment text and embeddings too. **Recommendation:** B. Use Tantivy and exact search first. Add attachment text or vectors only after costs, retention, and deletion behavior are measured. **20. Should Ask and Agents use mail content?** Options: (A) No mail access. (B) Search and summarize only after an explicit User action. (C) Send every new message to a model automatically. **Recommendation:** B. Use authenticated Mail routes and citations. Never expose provider credentials to the Agent container. Never summarize or send mail to an external model by default. **21. When may HTML mail load remote content?** Options: (A) Never. (B) Only after a User selects “Load images.” (C) Automatically through a proxy. **Recommendation:** B after a secure image fetch boundary exists. Until then, keep all remote content blocked. A direct request from the browser or server reveals data. **22. What should a push notification show?** Options: (A) Generic “New mail.” (B) Sender and subject. (C) User-configured detail by mailbox and Installation. **Recommendation:** A by default. Let the User opt into details per Installation. Never notify for backfill. ### Round 5 — Reader, navigation, and native clients **23. Is Mail a new top-level mode?** Options: (A) Yes; it gets a mode icon and a sidebar of mail views. (B) No; mail is a Search-only feature. (C) Add a mail panel inside Calendar. **Recommendation:** A if Mail is a first-class product area. The mode tray is full, so the owner must choose which mode moves. Do not add a second pill bar. **24. What is the default reader layout?** Options: (A) Message list with a selected reader in the main area. (B) A permanent three-column window on every screen. (C) A right-hand info sidebar. **Recommendation:** A. Use a responsive list and reader on desktop; open a message as the main page or sheet on a phone. Do not create a right info sidebar. A three-pane desktop option can be tested later. **25. Should calternal group mail into threads?** Options: (A) No threads. (B) Provider thread ID or strict RFC graph. (C) Subject-based grouping. **Recommendation:** B, with a chronological view always available. Never merge by subject alone. **26. Should categories or priority inboxes sort mail by default?** Options: (A) Chronological only. (B) Optional categories and a chronological All Mail view. (C) Forced AI-ranked Inbox. **Recommendation:** B. Keep the category count small and let Users fix misclassified senders. **27. What keyboard model should Mail use?** Options: (A) Fixed Gmail-style shortcuts. (B) User-selectable shortcut presets. (C) The calternal shortcut registry, with any user remapping added later. **Recommendation:** C for the first release. Keep shortcuts in one registry, test against browser keys, and provide a ? help sheet. **28. What is the deep-link identity?** Options: (A) Provider URL or IMAP UID. (B) RFC Message-ID header. (C) Stable calternal item ID with provider coordinates kept behind it. **Recommendation:** C. Message-ID can be absent or duplicated. UIDVALIDITY can change. Every message and thread needs Copy link. **29. What is the future for the Swift Mailternal app?** Options: (A) Keep it as a separate product with direct provider sync. (B) Retire it. (C) Turn it into a native calternal client. **Recommendation:** C if a native Mail surface is still wanted. Keep one mailbox sync owner on the server. If native support is not funded, choose B instead of maintaining two stores. ### Round 6 — MVP order and release gates **30. What order should the work follow?** Options: (A) Account linking, read, search, then actions and send. (B) Compose and send before search. (C) Build a mail server and client together. **Recommendation:** A. The first vertical slice should connect one provider, sync recent Inbox items safely, open text and sanitized HTML, search them, and restore a stable message link. **31. What must be true before this Plugin is enabled for an Instance?** Options: (A) A scripted protocol test only. (B) Protocol tests, sync recovery tests, per-User authorization tests, malicious MIME/HTML tests, provider checks, and one real-server adversarial round. (C) A product demo. **Recommendation:** B. Keep the first release to a time-boxed test round. Fix crashes, data loss, cross-User access, sync collisions, and unsafe network fetches. Do not turn a cosmetic issue into a launch gate. **32. What is the release measure for “fast enough”?** Options: (A) Measure only app launch. (B) Measure newest Inbox mail visible, search latency, mailbox page latency, server memory, and background sync load. (C) Do not measure until after launch. **Recommendation:** B. Use a small and a large mailbox, and set limits for provider connections and backfill before opening this to all Users.
Author
Owner

Adversarial round

The one local real-server round completed on 2026-09-27 after the required merge from dev. The server stayed alive. The runner exited 1 because it reported findings.

Non-SLOW findings are filed:

  • #269: API request timeouts under mixed concurrency. Four of 16 bookmark storm requests timed out; 16 of 24 conditional Journal PATCH requests timed out; DAV initial sync and the second recurring Calendar Log write also timed out. The server showed slow SQLx pool acquisition warnings while other worktrees ran builds and adversarial suites.
  • #270: the public collaboration socket remained open after the probe sent the 301st frame against the 300-frame limit. Confirm this on a less-loaded server.
  • #271: the authorization matrix sent GET AdminConfigView as the PUT InstanceConfig body, so the standard User request returned 422 before the handler's admin check. This does not show an authorization bypass; the probe needs a valid request body.
  • #272: the standard full adversarial runner did not coordinate dedup restart barriers 4–6, and late User bearer fixtures had expired. Dedup findings from that phase are inconclusive.

The Journal race integrity check passed: 40 creates and 10 conditional uploads were present once in 46 entries. Seven concurrent moves were marked SLOW. The 10,000-block editor sync timeout and the other SLOW-only latency findings are load observations.

Correction: the recurring Calendar Log identity assertion recorded only one ID because the second occurrence write timed out. It did not prove that two occurrences shared an ID.

## Adversarial round The one local real-server round completed on 2026-09-27 after the required merge from dev. The server stayed alive. The runner exited 1 because it reported findings. Non-SLOW findings are filed: - #269: API request timeouts under mixed concurrency. Four of 16 bookmark storm requests timed out; 16 of 24 conditional Journal PATCH requests timed out; DAV initial sync and the second recurring Calendar Log write also timed out. The server showed slow SQLx pool acquisition warnings while other worktrees ran builds and adversarial suites. - #270: the public collaboration socket remained open after the probe sent the 301st frame against the 300-frame limit. Confirm this on a less-loaded server. - #271: the authorization matrix sent GET AdminConfigView as the PUT InstanceConfig body, so the standard User request returned 422 before the handler's admin check. This does not show an authorization bypass; the probe needs a valid request body. - #272: the standard full adversarial runner did not coordinate dedup restart barriers 4–6, and late User bearer fixtures had expired. Dedup findings from that phase are inconclusive. The Journal race integrity check passed: 40 creates and 10 conditional uploads were present once in 46 entries. Seven concurrent moves were marked SLOW. The 10,000-block editor sync timeout and the other SLOW-only latency findings are load observations. Correction: the recurring Calendar Log identity assertion recorded only one ID because the second occurrence write timed out. It did not prove that two occurrences shared an ID.
Author
Owner

Finished Forgejo #257 research-only work on branch research/mail.

Research: docs/research/mail-plugin.md records the Mailternal source and decision review, calternal design and plugin fit, verified Rust mail ecosystem and UX research, proposed architecture, risks, source links, and 32 grill questions in six rounds. The grill questions were also posted in the earlier issue comment.

Head: 9a53fc48f2fca4c05380b61f30b83f350064a95a (merge of dev performed once). The branch push completed; the remote was already up to date.

Decisions where DESIGN.md is silent: Recommend provider-backed canonical mail with a rebuildable local Index projection and explicit .eml export; use a provider client (IMAP or JMAP) in the first release, with no hosted MX; defer sending from the MVP; render untrusted HTML in a restricted sandbox and block remote images by default. These are recommendations for the owner to confirm, not implementation decisions.

Adversarial round: Ran once against the real local server after merging dev. The server stayed alive. Findings and evidence are in #269–#272: high-load request timeouts; a collaboration socket that stayed open after the frame limit; an authorization matrix case with an invalid PUT body (422 before auth, so no bypass evidence); and a late dedup phase with expired credentials and missing restart coordination. The recurring Calendar log assertion did not prove an ID collision because its second write timed out. Journal race integrity passed (40 creates and 10 conditional uploads, each once among 46 entries). Hostile HTML/SVG/script probes found no issue. The timeout and socket findings remain for follow-up; this research-only job did not change product code.

Gates (verbatim):

cargo fmt --check — exit 0, no output:

cargo clippy --all-targets -- -D warnings — exit 0:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 12m 37s

cargo test --quiet — exit 101:

running 1 test
.
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 50 tests
........................... 43/50
store::tests::key_rotation_revokes_sessions_and_rejects_competing_recovery --- FAILED
....

failures:

---- store::tests::key_rotation_revokes_sessions_and_rejects_competing_recovery stdout ----

thread 'store::tests::key_rotation_revokes_sessions_and_rejects_competing_recovery' (1522597) panicked at crates/calternal-auth/src/store.rs:2676:9:
assertion `left == right` failed
  left: 0
 right: 1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

failures:
    store::tests::key_rotation_revokes_sessions_and_rejects_competing_recovery

test result: FAILED. 49 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 43.53s

error: test failed, to rerun pass `-p calternal-auth --lib`

Filed for investigation as #274. Cargo stopped at this library failure, so later test binaries did not run.

bun run --cwd apps/web check — exit 0:

$ svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/mail-research/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run --cwd apps/web test — exit 0:

$ vitest run

 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/mail-research/apps/web

Could not parse CSS stylesheet
Not implemented: Window's scrollTo() method

Test Files  82 passed (82)
     Tests  594 passed (594)
  Start at  23:35:53
  Duration  71.37s (transform 63%, import 14%, environment 14%, tests 7%, setup 2%)

 Transform  |component| transforming modules took 97.47s · 54% of tracked time, re-done on every run
            persist transforms across runs with fsModuleCache: true
            learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns

Known gaps: No mail product code was written, as required. The cargo test gate has one unresolved failure (#274); the remaining Rust test binaries were not reached. Adversarial follow-ups #269 and #270 remain open. The web test runner emitted the CSS parse and scrollTo() diagnostics shown above despite passing all tests.

Finished Forgejo #257 research-only work on branch `research/mail`. **Research:** [docs/research/mail-plugin.md](docs/research/mail-plugin.md) records the Mailternal source and decision review, calternal design and plugin fit, verified Rust mail ecosystem and UX research, proposed architecture, risks, source links, and 32 grill questions in six rounds. The grill questions were also posted in the earlier issue comment. **Head:** `9a53fc48f2fca4c05380b61f30b83f350064a95a` (merge of `dev` performed once). The branch push completed; the remote was already up to date. **Decisions where DESIGN.md is silent:** Recommend provider-backed canonical mail with a rebuildable local Index projection and explicit `.eml` export; use a provider client (IMAP or JMAP) in the first release, with no hosted MX; defer sending from the MVP; render untrusted HTML in a restricted sandbox and block remote images by default. These are recommendations for the owner to confirm, not implementation decisions. **Adversarial round:** Ran once against the real local server after merging `dev`. The server stayed alive. Findings and evidence are in #269–#272: high-load request timeouts; a collaboration socket that stayed open after the frame limit; an authorization matrix case with an invalid PUT body (422 before auth, so no bypass evidence); and a late dedup phase with expired credentials and missing restart coordination. The recurring Calendar log assertion did not prove an ID collision because its second write timed out. Journal race integrity passed (40 creates and 10 conditional uploads, each once among 46 entries). Hostile HTML/SVG/script probes found no issue. The timeout and socket findings remain for follow-up; this research-only job did not change product code. **Gates (verbatim):** `cargo fmt --check` — exit 0, no output: ``` ``` `cargo clippy --all-targets -- -D warnings` — exit 0: ``` Finished `dev` profile [unoptimized + debuginfo] target(s) in 12m 37s ``` `cargo test --quiet` — exit 101: ``` running 1 test . test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 50 tests ........................... 43/50 store::tests::key_rotation_revokes_sessions_and_rejects_competing_recovery --- FAILED .... failures: ---- store::tests::key_rotation_revokes_sessions_and_rejects_competing_recovery stdout ---- thread 'store::tests::key_rotation_revokes_sessions_and_rejects_competing_recovery' (1522597) panicked at crates/calternal-auth/src/store.rs:2676:9: assertion `left == right` failed left: 0 right: 1 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failures: store::tests::key_rotation_revokes_sessions_and_rejects_competing_recovery test result: FAILED. 49 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 43.53s error: test failed, to rerun pass `-p calternal-auth --lib` ``` Filed for investigation as #274. Cargo stopped at this library failure, so later test binaries did not run. `bun run --cwd apps/web check` — exit 0: ``` $ svelte-kit sync && svelte-check --tsconfig ./tsconfig.json Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/mail-research/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run --cwd apps/web test` — exit 0: ``` $ vitest run RUN v5.0.1 /home/kayg/Developer/calternal-wt/mail-research/apps/web Could not parse CSS stylesheet Not implemented: Window's scrollTo() method Test Files 82 passed (82) Tests 594 passed (594) Start at 23:35:53 Duration 71.37s (transform 63%, import 14%, environment 14%, tests 7%, setup 2%) Transform |component| transforming modules took 97.47s · 54% of tracked time, re-done on every run persist transforms across runs with fsModuleCache: true learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns ``` **Known gaps:** No mail product code was written, as required. The cargo test gate has one unresolved failure (#274); the remaining Rust test binaries were not reached. Adversarial follow-ups #269 and #270 remain open. The web test runner emitted the CSS parse and `scrollTo()` diagnostics shown above despite passing all tests.
Author
Owner

Owner grill, round 1 (2026-09-28):

  • Q1: client only (A). Connect external providers. No hosting, MX or SMTP relay.
  • Q3: 'whatever mailternal has'. The first release matches mailternal at 955b815: one IMAP account per User; full-history, text-only sync, newest first; collapsible nested folders; a flat (non-threaded) message list; a reader with a reading mode (Original/Dark); remote images only after consent; \Seen writes (mark read/unread); Archive via IMAP MOVE; swipe actions (leading Mark as read, trailing Archive); offline full-text search; IDLE + delta reconciliation; new-mail notifications; stable deep links. Providers: generic IMAP with app passwords, plus iCloud and Fastmail presets. Consumer Gmail is deferred (restricted-scope OAuth). Attachments are fetched on demand. Not in the first release: compose/send, threading, flag, trash/delete, move to an arbitrary folder, multiple accounts, unified inbox (mailternal roadmap 0.0.2/0.0.4).
  • Q4: optional Core plugin, ON by default. Owner principle: make as many plugins optional as possible, but enable them by default. An admin can disable Mail per Instance.
  • Q2: open. The owner asked what .eml is and whether mail can be stored as plaintext or Markdown; the explanation and the options follow in round 2.
Owner grill, round 1 (2026-09-28): - Q1: **client only (A).** Connect external providers. No hosting, MX or SMTP relay. - Q3: **'whatever mailternal has'.** The first release matches mailternal at 955b815: one IMAP account per User; full-history, text-only sync, newest first; collapsible nested folders; a flat (non-threaded) message list; a reader with a reading mode (Original/Dark); remote images only after consent; \\Seen writes (mark read/unread); Archive via IMAP MOVE; swipe actions (leading Mark as read, trailing Archive); offline full-text search; IDLE + delta reconciliation; new-mail notifications; stable deep links. Providers: generic IMAP with app passwords, plus iCloud and Fastmail presets. Consumer Gmail is deferred (restricted-scope OAuth). Attachments are fetched on demand. Not in the first release: compose/send, threading, flag, trash/delete, move to an arbitrary folder, multiple accounts, unified inbox (mailternal roadmap 0.0.2/0.0.4). - Q4: **optional Core plugin, ON by default.** Owner principle: make as many plugins optional as possible, but enable them by default. An admin can disable Mail per Instance. - Q2: open. The owner asked what .eml is and whether mail can be stored as plaintext or Markdown; the explanation and the options follow in round 2.
Author
Owner

Owner corrections to round 1 (2026-09-28). These override the earlier comment:

  • Accounts: unlimited. A User can add as many IMAP (and SMTP) accounts as they want. The single-account limit from mailternal 0.0.1 does NOT carry over.
  • Full-text sync and indexing is extremely important. Sync the full history of every account and index all text. Search must still feel extremely fast at the size of several large mailboxes combined. The performance targets are set in round 2.
  • Gmail is NOT deferred. Consumer Gmail works over IMAP with an app password (Google offers app passwords to accounts with 2-Step Verification). Add a Gmail preset beside iCloud and Fastmail. No OAuth is needed for the first release. The restricted-scope review applies only if OAuth is added later.
  • Open (round 2): does the first release send mail over SMTP? Unified inbox across accounts? Search targets.
Owner corrections to round 1 (2026-09-28). These override the earlier comment: - **Accounts: unlimited.** A User can add as many IMAP (and SMTP) accounts as they want. The single-account limit from mailternal 0.0.1 does NOT carry over. - **Full-text sync and indexing is extremely important.** Sync the full history of every account and index all text. Search must still feel extremely fast at the size of several large mailboxes combined. The performance targets are set in round 2. - **Gmail is NOT deferred.** Consumer Gmail works over IMAP with an app password (Google offers app passwords to accounts with 2-Step Verification). Add a Gmail preset beside iCloud and Fastmail. No OAuth is needed for the first release. The restricted-scope review applies only if OAuth is added later. - Open (round 2): does the first release send mail over SMTP? Unified inbox across accounts? Search targets.
Author
Owner

Owner grill round 2 (2026-09-28):

  • Q19 search scope: B: sender, subject, recipients, full body text, across all accounts. Attachment text comes later (issue filed).
  • Q20 Ask/Agents: B: only on an explicit User action, with citations, and credentials never reach the agent container. The owner says the Agents feature is not refined enough yet anyway.
  • Q22 notifications: B: sender + subject. No notifications while mail is still being downloaded (backfill).
  • Q23: Mail is its own mode. Separately: Settings → Appearance gets a 'number of items in the tab bar' setting, 3 to 7 (see the tab bar issue).
  • Q-A send: yes, the first release sends mail (SMTP per account). The composer must use the block editor, with maximum reuse; design in round 3.
  • Q-B unified inbox: Unified is a pre-made view / smart folder / saved search. A 'Unified' folder section automatically matches similar folders across mailboxes (Inbox, Sent, Drafts, Archive, Junk, Trash…).
  • Q-C search speed: the owner's words: 'our search is more spotlight where everything appears by default so it should be as fast as it can be. benchmark and optimise aggressively.' No soft target: mail joins the one global palette, measured at 1M+ messages, with #258 targets as the floor.
  • Still open: Q2 (storage / .eml / Markdown), Q12 (disconnect; depends on Q2).
Owner grill round 2 (2026-09-28): - Q19 search scope: **B**: sender, subject, recipients, full body text, across all accounts. Attachment text comes later (issue filed). - Q20 Ask/Agents: **B**: only on an explicit User action, with citations, and credentials never reach the agent container. The owner says the Agents feature is not refined enough yet anyway. - Q22 notifications: **B**: sender + subject. **No notifications while mail is still being downloaded** (backfill). - Q23: **Mail is its own mode.** Separately: Settings → Appearance gets a 'number of items in the tab bar' setting, 3 to 7 (see the tab bar issue). - Q-A send: **yes, the first release sends mail** (SMTP per account). The composer must use the block editor, with maximum reuse; design in round 3. - Q-B unified inbox: **Unified is a pre-made view / smart folder / saved search.** A 'Unified' folder section automatically matches similar folders across mailboxes (Inbox, Sent, Drafts, Archive, Junk, Trash…). - Q-C search speed: the owner's words: 'our search is more spotlight where everything appears by default so it should be as fast as it can be. benchmark and optimise aggressively.' No soft target: mail joins the one global palette, measured at 1M+ messages, with #258 targets as the floor. - Still open: Q2 (storage / .eml / Markdown), Q12 (disconnect; depends on Q2).
Author
Owner

Owner grill round 3 (2026-09-28):

  • Q2 storage: the provider is canonical; calternal keeps a rebuildable index projection. Never expose every mail as a file. On demand, save a message (or a thread) as HTML / Markdown / PDF, plus the other formats research finds useful (orchestrator proposal: .eml exact copy, plain text, and .mbox for a multi-selection). Saved files land in Files like any other file.
  • Q24 composer: agreed as proposed (Composer parts: overlay/sheet, draft deck, commit-freeze, ⌘↩; From/To/Cc/Bcc chips + Subject; block editor with a 'mail' profile; HTML + text/plain alternative; reply quote block; the quick composer can expand into a mail). Add a very easy rich text ↔ plain text switch. The owner sends plain text; most people prefer rich text.
  • Q25 signatures: B, signatures are notes, and they are per-app, not per-account. A signature library; you can assign any signature to any account as its default; while drafting, switch to any other signature on demand.
  • Q26 tab bar: phone always shows 3 tabs; desktop shows 3 to 7 (Settings → Appearance). The other modes go to a More slot.
  • Q27 Unified: agreed: SPECIAL-USE roles + case-insensitive same-name folders; appears with 2+ accounts; user saved searches are smart folders.
  • Threads: show them newest first (descending) with a very good design. A Codex research job studies the best designs (issue filed); the owner will grill it.
  • Next round: Q12 disconnect behaviour, the plain/rich default and reply-in-kind, and the thread design after the research.
Owner grill round 3 (2026-09-28): - Q2 storage: **the provider is canonical; calternal keeps a rebuildable index projection. Never expose every mail as a file.** On demand, **save a message (or a thread) as HTML / Markdown / PDF**, plus the other formats research finds useful (orchestrator proposal: .eml exact copy, plain text, and .mbox for a multi-selection). Saved files land in Files like any other file. - Q24 composer: **agreed as proposed** (Composer parts: overlay/sheet, draft deck, commit-freeze, ⌘↩; From/To/Cc/Bcc chips + Subject; block editor with a 'mail' profile; HTML + text/plain alternative; reply quote block; the quick composer can expand into a mail). **Add a very easy rich text ↔ plain text switch.** The owner sends plain text; most people prefer rich text. - Q25 signatures: **B, signatures are notes, and they are per-app, not per-account.** A signature library; you can assign any signature to any account as its default; while drafting, switch to any other signature on demand. - Q26 tab bar: **phone always shows 3 tabs; desktop shows 3 to 7** (Settings → Appearance). The other modes go to a **More** slot. - Q27 Unified: **agreed**: SPECIAL-USE roles + case-insensitive same-name folders; appears with 2+ accounts; user saved searches are smart folders. - Threads: show them **newest first (descending)** with a very good design. A Codex research job studies the best designs (issue filed); the owner will grill it. - Next round: Q12 disconnect behaviour, the plain/rich default and reply-in-kind, and the thread design after the research.
Author
Owner

Owner grill round 4 (2026-09-28):

  • Q12 remove account: A. Delete the stored credentials, cached mail and index entries at once, after one confirm that says what goes and what stays. Saved files and the per-app signatures stay. Adding the account again resyncs from the provider.
  • Q28 format: the default is always rich text, unless the User changes it in Settings → Mail (one app-wide setting). The composer's rich ↔ plain switch is always one action away (proposed ⌘⇧T). The owner did not choose reply-in-kind, so the composer starts in the Settings default. Plain text sends format=flowed; lists and quotes become - and > , and links become text <url>.
  • Q29 save-as: HTML, Markdown, PDF, .eml, plain text, .mbox (multi-select or thread), Print. One 'Save as…' submenu. The PDF and HTML use the reader layout, and remote images stay blocked unless already allowed. A thread saves as one file.
Owner grill round 4 (2026-09-28): - Q12 remove account: **A**. Delete the stored credentials, cached mail and index entries at once, after one confirm that says what goes and what stays. Saved files and the per-app signatures stay. Adding the account again resyncs from the provider. - Q28 format: **the default is always rich text, unless the User changes it in Settings → Mail** (one app-wide setting). The composer's rich ↔ plain switch is always one action away (proposed ⌘⇧T). The owner did not choose reply-in-kind, so the composer starts in the Settings default. Plain text sends format=flowed; lists and quotes become `- ` and `> `, and links become `text <url>`. - Q29 save-as: **HTML, Markdown, PDF, .eml, plain text, .mbox (multi-select or thread), Print.** One 'Save as…' submenu. The PDF and HTML use the reader layout, and remote images stay blocked unless already allowed. A thread saves as one file.
Author
Owner

Owner (2026-09-28), amending Q28 and answering round 5 ('i also like this idea. and i agree with your recommendation too'):

  • Q28: reply in kind is ON. A reply to a plain-text message starts in plain text. A reply to a rich message, and every new message, uses the Settings → Mail default (rich unless changed). The rich ↔ plain switch is always one action away.
  • Q13 actions in the first release: B. Archive, mark read/unread, Move to folder, Trash/Delete, Flag, Junk/Not junk, each with Undo. They use the shared context toolbar and swipe actions. Actions are queued on the server, idempotent and generation-scoped, retried, and survive offline; Undo reverts within the toast's time.
  • Q15 Mail → Task: A. A 'Make task' action; the title defaults to the subject; the task links back to the exact message; the body is not copied.
  • Q16 invites: B. An invite card with a clash check, and Accept / Maybe / Decline updates Calendar and sends the iMIP reply. Never automatic.
  • Q17 contacts: A. Address autocomplete from the mail index (correspondents), no contact records. Contacts gets its own grill later.
  • Q18 sharing: A. Save as a file and share it with the normal Share; a 'Share…' action on a message does both steps. No mailbox Share.
  • Q-S Swift Mailternal: C later. Freeze it now, and later make it a native calternal client over the calternal API. Never run a second sync engine.
  • Orchestrator decisions (technical, recorded): deep links use a stable calternal item ID for each message and thread, with provider coordinates behind it (Q28 in the research doc); build order: accounts + sync → reader + search → send + actions; release gates per research Q31 B and Q32 B (hostile MIME/HTML, cross-User authz, sync recovery, one adversarial round, 'newest mail visible' time and search latency on a large mailbox).
  • Remaining: the thread design (#295 research running).
Owner (2026-09-28), amending Q28 and answering round 5 ('i also like this idea. and i agree with your recommendation too'): - Q28: **reply in kind is ON.** A reply to a plain-text message starts in plain text. A reply to a rich message, and every new message, uses the Settings → Mail default (rich unless changed). The rich ↔ plain switch is always one action away. - Q13 actions in the first release: **B**. Archive, mark read/unread, Move to folder, Trash/Delete, Flag, Junk/Not junk, each with **Undo**. They use the shared context toolbar and swipe actions. Actions are queued on the server, idempotent and generation-scoped, retried, and survive offline; Undo reverts within the toast's time. - Q15 Mail → Task: **A**. A 'Make task' action; the title defaults to the subject; the task links back to the exact message; the body is not copied. - Q16 invites: **B**. An invite card with a clash check, and Accept / Maybe / Decline updates Calendar and sends the iMIP reply. Never automatic. - Q17 contacts: **A**. Address autocomplete from the mail index (correspondents), no contact records. Contacts gets its own grill later. - Q18 sharing: **A**. Save as a file and share it with the normal Share; a 'Share…' action on a message does both steps. No mailbox Share. - Q-S Swift Mailternal: **C later**. Freeze it now, and later make it a native calternal client over the calternal API. Never run a second sync engine. - Orchestrator decisions (technical, recorded): deep links use a stable calternal item ID for each message and thread, with provider coordinates behind it (Q28 in the research doc); build order: accounts + sync → reader + search → send + actions; release gates per research Q31 B and Q32 B (hostile MIME/HTML, cross-User authz, sync recovery, one adversarial round, 'newest mail visible' time and search latency on a large mailbox). - Remaining: the thread design (#295 research running).
Author
Owner

Owner (2026-09-28), confirming round 5 explicitly:

  • Q13: B agreed (full action set with Undo).
  • Q15: Make task works like Apple Mail's 'Remind Me'. Orchestrator reading: the action picks a time (reuse the block-actions reminder presets from #191: in 1 hour, tonight, tomorrow, next week, custom). At that time the message returns to the top of the Inbox with a notification, and a linked task with that due time exists in Tasks. The title defaults to the subject, and the body is not copied. Completing the task clears the reminder, and the reverse is true too.
  • Q16: agreed (invite card + Accept/Maybe/Decline with iMIP reply, never automatic).
  • Q17: agreed; Contacts filed as #297.
  • Q18: agreed. Also, mailbox automations (rules) belong to the Automations feature, filed as #296: an Automations mode plus contextual nudges in every mode. Mail's entry point is 'Always do this for mail like this…'. The first Mail release ships without rules and leaves a clean hook for #296.
  • Q-S: retire Mailternal. Owner: 'when we make the calternal macOS app, it supersedes all mini apps.'
  • Still open: Q-Cat (categories), the thread design (#295).
Owner (2026-09-28), confirming round 5 explicitly: - Q13: **B agreed** (full action set with Undo). - Q15: **Make task works like Apple Mail's 'Remind Me'.** Orchestrator reading: the action picks a time (reuse the block-actions reminder presets from #191: in 1 hour, tonight, tomorrow, next week, custom). At that time the message **returns to the top of the Inbox** with a notification, and a linked task with that due time exists in Tasks. The title defaults to the subject, and the body is not copied. Completing the task clears the reminder, and the reverse is true too. - Q16: **agreed** (invite card + Accept/Maybe/Decline with iMIP reply, never automatic). - Q17: **agreed**; Contacts filed as #297. - Q18: **agreed**. Also, **mailbox automations** (rules) belong to the Automations feature, filed as #296: an Automations mode plus contextual nudges in every mode. Mail's entry point is 'Always do this for mail like this…'. The first Mail release ships without rules and leaves a clean hook for #296. - Q-S: **retire Mailternal.** Owner: 'when we make the calternal macOS app, it supersedes all mini apps.' - Still open: Q-Cat (categories), the thread design (#295).
Author
Owner

Owner (2026-09-28), Q-Cat: B as written. Automatic categories (People, Newsletters, Notifications, Receipts) as pre-made smart folders, and the Inbox shows only People by default (ON by default, not the orchestrator's 'off by default'). 'Always put this sender here' fixes a wrong guess. The classification uses standard signals only (List-Id/List-Unsubscribe, sender domain, receipt patterns), with no AI model and no mail sent anywhere. Owner principle: 'we need to find the balance between easy to use and being extremely bare bones. we want opinionated, easy-to-use approaches to all our plugins!'
Status: every Mail question is decided except the thread design (#295 research running).

Owner (2026-09-28), Q-Cat: **B as written.** Automatic categories (People, Newsletters, Notifications, Receipts) as pre-made smart folders, and **the Inbox shows only People by default** (ON by default, not the orchestrator's 'off by default'). 'Always put this sender here' fixes a wrong guess. The classification uses standard signals only (List-Id/List-Unsubscribe, sender domain, receipt patterns), with no AI model and no mail sent anywhere. Owner principle: 'we need to find the balance between easy to use and being extremely bare bones. we want opinionated, easy-to-use approaches to all our plugins!' Status: every Mail question is decided except the thread design (#295 research running).
Author
Owner

Grill complete. All decisions are in docs/DESIGN.md §45 (c99fc31d). Implementation starts with #313 (M1: accounts + full-history sync), queued for the next free capacity; M2 (reader + search) and M3 (send + actions) follow. Voice transcription for mail/notes: #304 (grill pending).

Grill complete. All decisions are in **docs/DESIGN.md §45** (c99fc31d). Implementation starts with **#313 (M1: accounts + full-history sync)**, queued for the next free capacity; M2 (reader + search) and M3 (send + actions) follow. Voice transcription for mail/notes: #304 (grill pending).
Author
Owner

Stale research brief: the later owner decision in DESIGN §45 (2026-09-28) specifies the Mail product, sync, storage, actions, composer, reader and first release. DESIGN §53 defines the CalternalDAV Mail proxy, and §56 defines separate app email. This research-only grill is superseded. Recommend using implementation issues #397, #486 and #575 for the remaining work. Do not close this issue in the audit.

Stale research brief: the later owner decision in DESIGN §45 (2026-09-28) specifies the Mail product, sync, storage, actions, composer, reader and first release. DESIGN §53 defines the CalternalDAV Mail proxy, and §56 defines separate app email. This research-only grill is superseded. Recommend using implementation issues #397, #486 and #575 for the remaining work. Do not close this issue in the audit.
Author
Owner

The research and design decisions are recorded in DESIGN §§45, 53 and 56. Remaining Mail implementation has separate tracking.

The research and design decisions are recorded in DESIGN §§45, 53 and 56. Remaining Mail implementation has separate tracking.
kayg closed this issue 2026-10-03 11:55:09 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#257
No description provided.