Skip to content

Client modes and the email experience

The owner approved this design and authorized implementation as Milestone 26 on 2026-09-11, including USD 20 per UTC day and USD 200 per rolling thirty days for aggregate automatic email work. This canonical mechanism expands the engineering plan's Milestone 26 under ADR-0092. It is an independent parallel workstream; the verified ceiling remains Milestone 12. Gate registration alone is not implementation evidence.

1. Product decision

Email mode is a focused interface to the same Veetbot. Chat and Email share the owner's identity, agent, persona, memories, learned preferences, policy, and execution infrastructure. A mode selects the interface and relevant task context; it does not select a separate assistant or memory namespace.

The owner's confirmed requirements are:

Area Requirement
Clients Launch together on iPhone, iPad, and Mac. No terminal-client feature work.
Accounts Use the existing personal and work Gmail integrations.
Retrieval Start when Email mode opens and refresh automatically while it is active.
Attention Keep the priority list short and optimize for a very high signal-to-noise ratio.
Drafts Prepare a response when relevant; edit and send inside Veetbot; explicitly approve every send.
Learning Use received and Sent email within the latest ninety days for now (ADR-0096).
Memory Share useful learning and memories across both accounts and Chat.
Important relationships Regular reply partners, close collaborators, portfolio founders/CEOs, founders/CEOs of prospective investments, fellow portfolio board members, and venture investors.

The approved defaults below govern implementation. They are not claims that existing behavior already satisfies this design; changing a requirement needs explicit owner approval rather than an implementation-driven relaxation.

2. The experience

Mode navigation

Add a persistent Chat / Email selector. On iPhone, Email uses a list-to-thread navigation flow. Mac and regular-width iPad use a list and detail layout, with the draft visible alongside or below its source conversation. Preserve the deployment floors ADR-0211 sets and the adaptive layouts.

Switching modes preserves the active Chat run, unsent chat text, selected mail, scroll position, and unsent email edits. Memory, Persona, Schedules, and Settings remain global. Adding a future mode requires a small mode descriptor and a presentation module; this feature does not build a dynamic plugin system.

Priority inbox

The initial view combines both accounts and normally displays up to five threads. It displays fewer when fewer meet the relevance threshold. Account badges and an account filter make the source clear without dividing the assistant's knowledge.

Each row shows the correspondent, subject, a concise statement of what needs attention, and a state such as Needs reply, Draft ready, or For your attention. An expandable explanation gives the strongest supported reasons for inclusion. Numeric ranking scores and internal model details stay out of the main flow.

The list is a selection, not a quota. There is no obligation to fill five slots. If more threads clear the threshold, a More important action expands the view to ten, then provides pagination over the remaining qualifying threads. Find mail and Review other mail make false negatives discoverable without cluttering the default list. On foreground refresh, new qualifying threads appear automatically after surviving visible rows, which retain their relative order. Preserve the open conversation and unsent edits; no reveal action or automatic scroll is required. Use a subtle insertion animation, disabled when Reduce Motion is enabled. This owner-approved behavior (2026-10-03) replaces the new-mail announcement button.

Viewing a thread does not automatically mark it read in Gmail. On servers advertising archive support, the checkbox on each row and in thread detail means Archive in Gmail. Checking it removes INBOX from that conversation in its originating account; unchecking an archived conversation explicitly moves it back to the same account's Inbox. The clearly labelled gesture supplies single-action owner consent under ADR-0095. Remove the row immediately while the archive request and durable operation finish asynchronously. Normal progress and successful completion are silent; other rows remain actionable. Keep the last confirmed mailbox state separate from this optimistic presentation, retain draft edits, and restore the row with an actionable error if admission, status retrieval, or execution fails or the outcome is uncertain. Archiving the open conversation, from its row or its detail, immediately clears that detail and opens the next visible conversation in the current list order, falling back to the previous conversation at the end of the list or an empty pane when none remain. Loading the next conversation does not wait for archive admission or completion. A later archive failure restores its row without replacing the owner's new selection. Archiving another row and move-to-Inbox keep the selection. Projection refreshes must not resurrect a pending row; reopening Email resumes status reads without another archive request. Preserve unread state, other labels, and importance feedback. New correspondence is assessed again. After a confirmed reply, the thread leaves Needs reply unless another unanswered request remains.

Legacy clients' Mark handled command remains a local, reversible dismissal of that source revision. It does not archive Gmail and is not evidence that a thread is archived. New clients must not silently substitute this command when the server or selected account lacks archive support.

Freshness is shown per account. A failed or unfinished scan says so. The UI must not equate an incomplete scan or model failure with an empty priority inbox.

Thread, feedback, and reply

Thread detail contains the original messages, a grounded summary, any request or deadline, the importance explanation, and an editable draft when one is appropriate. Render sanitized text initially; external images, tracking pixels, and active HTML do not load. Show attachment metadata and the fact that an attachment was not read. A draft that depends on an unread attachment requests the missing information rather than claiming to have reviewed it.

The reading view derives inert Markdown from each retained message on the scoped thread read, after source retention is enforced. It removes invisible preheader padding and recognizable promotional controls, collapses a forwarded envelope to its source attribution, embeds source URLs as labeled links, and restores article headings and paragraph flow. Explicit HTML advertisements and hidden/active HTML are omitted; ambiguous prose, author attribution, substantive disclosures, and postscripts remain. No link is fetched or resolved during cleanup. Only the reader makes a link, from an HTML anchor or a source URL whose destination it checked: brackets, angle brackets and backslashes in the sender's own text are escaped, so that text cannot author a Markdown link, image or markup, while its emphasis and lists still format. Native text renders the result at a comfortable line width, with external HTTP(S) links opened only by a user gesture and a Show original toggle for the unchanged retained body. This is presentation, not a summary or rewrite: it makes no model call, stores no extra body copy, and never replaces drafting or memory evidence. The additive reader_body message field is absent on older servers; clients then show the original text. Source incompleteness and attachment notices remain visible in both views. Formatting cannot recover links or structure already lost during provider normalization.

Feedback offers Important and Less important, with an optional target: this thread, this person, or this kind of content. Needs reply / No reply needed is a separate judgment. Natural-language feedback such as “Her board updates matter, but not the newsletter” reaches the same learning service from Email or Chat. Show the interpreted change, its scope, and Undo. Ask a focused question only when a natural-language instruction cannot be assigned a safe, clear scope.

Discuss in Chat opens the ordinary session associated with that thread. It carries the selected thread and draft references into the conversation, uses the same memories, and returns to the same Email state. Merely browsing Email does not replace the owner's previously selected Chat conversation. The owner can manually file that discussion in a Chat folder, including an existing discussion made visible by an owner message, while preserving its thread and draft association. Draft-only email sessions remain ineligible; the folder proposal pass still excludes email-linked sessions (ADR-0184).

Existing content-free approval notifications and pending-action recovery use the server-owned thread/session association to open the correct Email draft, including after a cold launch. Resolving an approval on another device updates both modes without discarding unsaved input. No mail content is added to push. An outcome alert for a server-marked operational email session opens the Email list without adding the ingestion session to Chat history or replacing its draft. Approval and question alerts retain their existing resolution paths.

3. Retrieval and historical learning

Active use, not a mailbox monitor

Refresh immediately on entry to Email and on foreground return while Email is selected; refresh every 60 seconds while visible, with manual refresh too. Clients stop requesting refreshes when Email is hidden or backgrounded. The server coalesces overlapping requests from multiple devices, with one active refresh per principal/account and a single historical-learning slice per principal. Repeated requests cannot build an unbounded backlog.

An empty inbox does not restart its initial loading indicator on each projection poll. A failed refresh remains visible across successful reads of cached projections until a later refresh succeeds; it is not reported as an empty successful priority result. Transient operation-status failures retry the same admitted operation with bounded backoff while Email remains active, without admitting another task. Leaving Email or replacing the connection cancels obsolete reads silently. Foreground reads retain the identity of their activation through projection, operation, thread, draft and approval reads. Returning to Email starts a new activation; results from an earlier activation cannot update the newly opened view. A successful thread read clears an earlier read error without clearing an unresolved draft-edit or send error. Thread detail reads apply retention only to the requested conversation and its draft. They do not sweep unrelated threads, drafts or draft histories, and neither does admitting an operation or endorsing a writing example. Inbox scans read thread summaries without message content and do not hold the principal mutation lock; pending archive reconciliation re-reads its one target under a short lock before applying an outcome. No email command starts MCP servers while holding that lock (ADR-0103). Refresh, archive and source-exclusion audit sessions render no skill catalog, so they record an empty one without starting any MCP server. When the worker executes a typed run, it starts only the servers that task kind calls (ADR-0104): each admitted account's read server for refresh, read and write for archive, read and send for send, and none for a draft with complete cached evidence. Under ADR-0205, a draft with expired or incomplete evidence lazily prepares only its account's read server and refetches within the existing read, deadline and budget limits. It validates mailbox identity and requires a complete result before generation; a failed read preserves existing drafts and edits. A thread-bound session opens its full catalog before the lock is taken and releases an unused one after the lock is released. Clients reuse an in-flight initial thread read during foreground polling within the same activation, and display received messages without waiting for a separate draft response. Archive navigation clears the old content before loading its successor. An account awaiting its first update is shown as waiting; a recorded failure remains distinct even when that account has never completed synchronization.

Each foreground request may admit a bounded slice. The server does not schedule the next slice itself: it requires a fresh active client request. On leaving Email, stop admitting new slices; an admitted slice may finish its bounded work and persist its checkpoint. Reopening resumes. This adds no recurring schedule, Gmail push subscription, notification digest, or general device-presence router. An explicitly requested email operation in Chat remains available through the ordinary agent independently of the Email refresh timer.

Reliable synchronization

Extend the first-party read MCP contract with the metadata and change retrieval needed for durable synchronization. Existing search remains bounded to 25 results per page. Preserve provider message identity, thread identity, history cursor, labels, sent/received direction, dates, and reply headers in a versioned normalized contract. The client never calls Gmail directly.

sync_changes.max_results bounds normalized changes, not Google's history records, which can each contain many changes. Overflow uses an opaque local cursor bound to account, starting history ID, original provider page and page size, flattened offset, and page digest. A continuation re-fetches that bounded provider page; drift or expiry requests resynchronization without advancing the cursor or silently dropping changes. Ordinary provider continuations remain unchanged when no local overflow remains. Local wrappers are bounded to 64 KiB and contain no mail content; provider tokens remain bounded to 4,096 characters.

The backend owns independent account cursors. Persist a cursor only after all corresponding projection updates commit; process duplicate or reordered change observations idempotently. A bounded lookahead coalesces each message's final change across history pages before reading live projections, so a later deletion can cancel an earlier addition even when that message is no longer retrievable. Within one slice the lookahead continues through further pages, each spending one of that account's full-thread reads. Retain at most 1,000 message change records and read at most ten history pages per lookahead; exceeding either ceiling starts the existing bounded inbox resynchronization while preserving cached mail and independent historical learning. A thread read reports thread_missing when Gmail no longer has the conversation; it leaves the change, catch-up and history queues as removed mail, as deletions of its messages would. A pinned body revision Gmail no longer has is a changed source. Any other provider rejection is not evidence of deletion, so that work stays pending. Handle new mail, replies sent elsewhere, label changes, removed messages, throttling, revoked credentials, and partial account failure. An expired history cursor triggers a bounded resynchronization. Gmail explicitly requires a full resync when its retained history no longer covers the client's cursor. Google synchronization contract

Verified mailbox identity is distinct from a manifest default or capability name. A changed default/capability binding creates a fresh operational session for new work while retaining the immutable binding on old history. It must not reinterpret existing source receipts or send through another Google identity.

Refresh current inbox threads within the latest ninety days first (ADR-0096). Every refresh reads new Gmail changes, the most recently changed conversation first, before continuing that catch-up. An exhausted change page with pending thread reads still admits the next delta from its captured watermark and coalesces it with the retained work before reading those threads. While catch-up is unfinished, new mail takes at most four of the slice's full-thread reads and catch-up the rest of eight, so neither waits for the other. A resynchronization resumes changes from a watermark read before its inbox re-listing. The first display may be partial while pagination completes; report that state and keep useful results visible. Invalidate assessments when the source or a relevant feedback/profile/model revision changes. Rerank affected existing mail immediately after learning changes, even if Gmail has not changed. A poll with unchanged sources and already-current assessment versions does no new model work; unfinished eligible historical analysis can still resume. Reuse completed features when the relevant owner feedback, correspondent evidence and shared memories are unchanged, even if the global profile revision advanced. Rescore them deterministically under the current profile. Writing examples are supplied to drafting only; style churn does not cause importance model calls. Style changes affect new or explicitly regenerated drafts, never overwrite edited drafts. Extractor upgrades use governed replay with deduplication, not recurring re-extraction.

Historical reach

Process Inbox/archive and Sent history within the latest 90 days only for now, as the owner authorized in ADR-0096. Older historical windows are deferred. Discard old discovery cursors once when adopting this policy, preserving cached mail and source receipts. Advance the discovery cutoff when no Inbox or history query pagination is active; an active traversal retains its query-bound cutoff. Completed coverage stays complete as that window narrows, while later Inbox resynchronization searches use the refreshed cutoff. Preserve cached source identities and learning across those updates. Apply the current ninety-day boundary to automatic assessment and learning even for already-cached mail and mixed-age threads. Older context may remain readable but must not enter automatic model assessment or drafting; a partial recent view cannot authorize a complete-context draft. Spam and Trash remain excluded from automatic learning. Include a clear coverage view showing processed date ranges, discovered/processed counts, exclusions, and whether further history remains. Each account reports the cutoff (catch_up_since) and the oldest inbox mail the current pass has read (inbox_reached_at).

Use cheap structured metadata first to discover interaction patterns and select useful source messages. Read relevant current threads and diverse historical Sent examples next. Maintain forward progress through eligible history so a busy inbox cannot permanently starve it. Metadata-only coverage is not reported as semantic understanding of all mail. Within available budgets, continue body analysis over remaining eligible history rather than silently ending after a small sample.

Approved admission bounds are 100 thread summaries per account per slice, 10 full-thread reads per account per slice, and a 120-second execution deadline. These are upper bounds: inherited tool, output-byte, context, token, and cost limits may stop work sooner. Checkpoint between pages. Oversized threads need explicit continuation or a partial-content marker; truncation must never be mistaken for complete conversation evidence.

Large threads use a rolling retained window. At 100 messages or 512 KiB, finish bounded analysis of the retained passages before advancing to the next source window. A window may include one additional bounded MCP page, but does not grow without bound. Completed passages can leave the cache while original message, body-offset and source-event receipts survive. Analysis walks 8,192-character passages; a rolled window remains explicitly partial and cannot authorize a draft that requires complete thread context. These thresholds bound one working window, not lifetime historical coverage. Discovery waits on a full window's analysis, so a slice assesses a window its discovery waited on before any other pending conversation of the same Inbox or historical class, one passage per slice.

Interactive Chat and reading/editing email take priority over historical work. Every run retains its existing finite limits. Add explicit aggregate automatic email admission, using the existing reservation/accounting pattern rather than assuming scheduled-run cost ceilings already cover foreground work. Approved defaults are USD 20 per UTC day and USD 200 per rolling thirty days, across both accounts/devices and all automatic classification, history, style, drafting, and formation work. Reserve each slice's maximum cost transactionally before admission, settle actual usage afterward, and retain unresolved reservations through crash recovery. Settlement requires every started model attempt to have a completed response or a proven pre-generation rejection and the durable usage count to match all started attempts. HTTP 400 invalid_json_schema permanent failures are proven request rejections; transport failures, timeouts, missing outcomes, and incomplete accounting keep actual usage unknown. Live runs retain their full reservations, regardless of age. Once work has ended, ADR-0173 replaces the live hold with a separate conservative budget charge: the greater of the full reservation and recorded run cost, never less than a prior conservative charge. A missing run receives the full reservation charge and preserves any greater charge already recorded. Count these charges once in the task's original UTC-day and rolling-thirty-day buckets. Preserve the reservation, charge and reason for audit; never represent uncertain usage as a proven zero-cost settlement. Later complete evidence can settle actual usage, which then takes precedence over the retained estimate. Admission reconsiders terminal or missing unsettled tasks under the same principal lock before checking the aggregate budget, so old holds recover idempotently without increasing allowances. No age-based release applies to queued, running or suspended work. Admission queries select unsettled tasks and, for budget calculation, settled tasks created within the rolling thirty-day window before pagination. Older settled task records remain available for audit and idempotent command replay. Dependent extraction calls cannot escape the originating reservation. A finite reservation is not renewed by retry. Email model calls cap provider sends at one and disable runtime model retries. A failed or empty result ends the slice; a later admitted slice can retry only with its own reservation. Raw provider transport failures produce typed failure events so reconciliation retains their uncertain accounting evidence.

Meter every model stage through the existing gateway. Lower applicable runtime or principal limits prevail. Show a pause reason when a ceiling is reached; cached browsing, editing, and feedback remain available. Return spent and reserved amounts, both limits and a next budget-check time. Counted spending includes conservative charges; daily_estimated and monthly_estimated identify the estimated subset without claiming it is known provider billing. Clients retain one pause across foreground visits, continue cached projection reads, and suppress automatic admission until that time; manual refresh can explicitly recheck. Connection replacement clears the pause. Measure mailbox volume, throughput and cost before changing these approved limits or any existing spend ceiling. The numbers are reviewable configuration defaults, not permission to spend outside the approved implementation and evaluation scope. “Maximum learning” means broad, resumable evidence use, not an unbounded parallel import or automatic budget increase.

The shipped runtime/limits.yaml values email.daily_cost and email.monthly_cost hold these defaults. A reviewed operator overlay under AGENT_CONFIG_DIR can set owner-authorized allowances; both values must be finite numeric dollar amounts of at least USD 0.01. The composition root injects the effective values into email admission. Changing them does not renew prior usage or reservations, raise the USD 1 slice reservation, or bypass lower applicable limits.

4. Importance learning

Evidence and relationships

Maintain a shared, versioned owner preference model. Distinguish relationship importance, content relevance, urgency, and whether a reply is needed. Evidence records include the source account/message, time, source type, confidence, profile revision, and any owner correction.

Priority signal Supporting evidence Limitation
Regular reply partner Distinct substantive threads the owner replies to, reply rate, reciprocity, recency, sustained history Count messages once; exclude automated replies and quoted copies.
Close collaborator Repeated substantive project work, shared context, explicit feedback High send volume alone is insufficient.
Portfolio founder/CEO Owner statements, supported shared memories, company and role evidence from correspondence A title or domain alone does not establish portfolio membership.
Prospective-investment founder/CEO Supported active investment discussions and owner engagement A cold pitch does not establish likely investment.
Fellow portfolio board member Evidence of the shared board and company relationship An unrelated board title is insufficient.
Venture investor Attributed role/firm evidence and relevant interactions Role alone does not make every newsletter important.
Important content Active projects/deals, material changes, decisions, commitments, direct requests, owner feedback A sender's “urgent” claim is evidence to examine, not an instruction.

Use existing shared memory and historical mail to discover these relationships. Allow corrections in either mode. Associate addresses with the same person only on reliable alias evidence or owner confirmation; matching display names are insufficient. A small correspondent/organization evidence index supports this feature without introducing the deferred general-purpose temporal entity graph.

Selection and adaptation

A bounded semantic assessment extracts supported topics, relationship evidence, actionability, deadlines, and reply need from changed mail. A deterministic, versioned selection layer combines those features with owner rules and learned preferences, applies the calibrated threshold, and orders qualifying threads. Record the factors and input/profile revisions so decisions can be explained and reproduced. Version model prompts and feature schemas too.

Assess current attention against the server's explicit current time, resolving relative event dates against the original message date. A passed meeting or expired reminder alone supplies neither current importance nor reply need. The versioned assessment may record a source-supported, timezone-aware attention_expires_at only when all attention and reply relevance ends at that time. Uncertain dates, unresolved requests, overdue obligations, follow-up work and lasting informational value do not acquire an automatic expiry merely because a due date passed. There is no blanket two-week message cutoff.

Apply that expiry when projecting lists, thread detail and automatic draft eligibility, before explicit owner feedback. Expiry does not change Gmail, erase history, revise source identity or discard existing drafts. It needs no new model call on an unchanged poll. Source changes replace the old assessment; prompt/schema revision changes re-assess cached mail in the existing bounded foreground slices, so pre-expiry assessments are not cached indefinitely.

Explicit owner preferences dominate inferred patterns. A thread-only judgment immediately changes that thread and contributes limited evidence to broader preferences; it does not become an unconditional sender rule. Person/topic feedback deliberately changes that target. Contradictory feedback, undo, and reset rebuild the derived profile from its surviving evidence rather than applying an irreversible hidden adjustment.

Important people do not make every message important. Infrequent board contacts remain eligible through relationship evidence. New relevant senders can qualify through content and shared project/deal context even with no reply history. Conversely, bulk messages from known investors can stay below the threshold.

Do not train on the system's own rankings or summaries. Opening, dismissing, ignoring, or archiving mail is not by itself a negative preference label. Historical reply behavior is a useful inferred signal; explicit feedback has higher authority. Sending a Veetbot-generated response must not feed back as independent proof that Veetbot selected the right thread.

This is application personalization from evidence and feedback. No model fine-tuning, reinforcement learning, new embedding infrastructure, or changes to the deferred general memory-policy learning workstream are included.

5. Drafting in the owner's style

Shared style with contextual variation

Build one versioned owner writing profile, shared with Chat, with supported variation for recipient relationship and purpose: for example, an investment introduction, a board decision, a close colleague, or a personal reply. Account is useful context, not a separate persona. Learn brevity, greetings, sign-offs, formality, paragraph structure, directness, and characteristic phrasing.

Historical Sent messages supply examples after removing quoted correspondence, forwarded chains, signatures, templates, automated notices, and boilerplate. Sample across recipients and time so one campaign cannot dominate. Historical Sent mail is communication evidence, not proof of personal authorship or trusted configuration. Mark ambiguous/delegated authorship and reduce its influence.

Use owner edits and explicit “write like this” feedback as stronger evidence. Separate factual changes from stylistic changes. Record generated-draft lineage; an unchanged Veetbot draft that is later sent does not become an independent style example. A deliberate “use this as an example” action can endorse it. The system must not gradually learn to imitate its own unreviewed output.

Persist a normalized digest in the thread-scoped draft_lineage record so ordinary MIME whitespace changes and sent-body expiry cannot turn Veetbot's own output into an independent historical example. The digest remains until source exclusion. Owner-authored edit deltas and explicitly endorsed examples outrank inferred historical examples in the bounded shared profile. The explicit POST /v1/email/drafts/{id}/style-example command requires the current revision and account authority, retains an excerpt of at most 2,000 characters, marks authorship as owner-endorsed but not independent, and preserves a learning pause.

Style observations remain data supplied as bounded task context. They never auto-edit the trusted global persona. Existing persona promotion continues to require owner affirmation.

When a draft is prepared

Automatically prepare drafts for up to the three highest-priority reply-worthy threads after the fresh assessment. Also allow Draft reply on any selected thread. Determine whether a reply is still needed using the complete available thread, including replies made in Gmail or another client. Abstain when the request is unclear, an attachment is essential, or a consequential decision is missing. A draft can request the missing decision; it must not invent one.

An explicit Draft reply or refinement request takes precedence over the earlier automatic reply-need assessment, without changing that assessment. Its draft still requires complete, current source content and verified reply recipients; the same limits on missing decisions and invented commitments apply. Failed or cancelled generation runs show an error beside the draft action, including when no draft exists yet, and preserve any existing draft and edits.

Use relevant shared memory, thread facts, the learned style profile, and a small set of appropriate examples. Broad memory sharing permits relevant context; it does not authorize inserting unrelated private facts into outgoing email. Never invent availability, investment intent, promises, amounts, or completed actions merely to make a draft sound natural.

Keep drafts inside Veetbot's server-owned store. Autosave there does not change Gmail and does not require a Gmail-write approval. Gmail draft synchronization is not required for this release. The existing Gmail draft tool remains available through its ordinary approval path when explicitly requested.

Editing and sending

Each draft is bound to a mailbox identity, thread, reply target, source revision, style/profile revision, and immutable draft revision. One current proposal is active per reply target. Repeated refreshes do not create duplicate drafts or replace owner edits. Refinement and regeneration create a new revision while preserving the owner's previous version.

Autosave uses optimistic concurrency. A second device can continue from the latest saved revision; competing edits produce an explicit conflict preserving both versions. Unsaved local edits remain visible on a network error. New incoming mail marks the draft stale and requires review of the changed source before sending.

The send flow is:

  1. Review & Send freezes the exact current revision and presents From account, To/Cc/Bcc where present, subject, and full body.
  2. Final Send explicitly resolves the ordinary approval for that exact action.
  3. Perform a fresh governed Gmail thread read, then recheck account authority, policy, draft/source revision, and action content. A failed freshness read defers sending and preserves the draft. An observed new message, external reply, edit, or recipient/account change invalidates the previous approval. Atomically claim the frozen local action/draft revision for dispatch so a concurrent local edit cannot slip between validation and the send claim.
  4. Dispatch through the existing account-specific send MCP tool. Persist the confirmed result and update the thread and draft state across devices.

Sending by value preserves the existing approval guarantee; an approval cannot point to a remotely mutable Gmail draft id. Cross-device double taps reuse one logical action identity. A failure proven before dispatch can retry through the governed lifecycle. Once dispatch may have occurred, display Send status unknown, retain the record, and require reconciliation; do not automatically resend or claim exactly-once delivery from Gmail.

The freshness guarantee covers source changes observed before dispatch. Gmail does not provide an atomic check-thread-and-send transaction; another party can send after the final read. Document this residual race and never imply that a local revision lock prevents concurrent changes in Gmail.

Correct reply handling is part of this work. Preserve Reply-To, Message-ID, References, and In-Reply-To; calculate reply/reply-all recipients, remove the owner's verified addresses, and visibly confirm the outbound account. Maintain the matching subject and thread id. Gmail requires reply headers as well as the thread id for proper threading. Google threading requirements Initially support only verified account identities/aliases. Do not infer a sender alias or switch the sending account from message body text.

6. Memories from email interactions

The existing communication adapter creates bounded, attributed thread excerpts. The desired feature additionally needs semantic memories: relationships, portfolio/deal context, projects, decisions, commitments, changed deadlines, and useful preferences. This is an explicit, evaluated extension to ADR-0090, not a claim that excerpt storage already provides it.

Add a separately versioned communication extractor that admits validated first-party email evidence through the existing formation, conflict, lifecycle, and retrieval machinery. Require exact message/source-span grounding and keep author, account, thread, message identity, and event provenance. Distinguish “Alex says the vote moved to Friday” from an owner-confirmed fact or completed commitment. Store separate supported facts when a thread contains several; avoid a raw email dump disguised as one memory.

Bulk mail never forms communication memory (ADR-0116). Before any provider work, a thread the unsubscribe census indexes (email-unsubscribe.md) is bulk; after the assessment, a bulk verdict is bulk. A bulk message registers no semantic source, forms no semantic or People memory, and creates no provisional person. The formation service's source validation refuses the census-indexed thread and the thread whose persisted assessment carries a bulk verdict, so the refresh, the historical import, and any replay meet the same rule; the refresh also records a content-free email.semantic.skipped event naming the reason, so memory diagnostics can explain why nothing formed. Importance assessment is unchanged, and the assessment prompt is not revised for this rule. Memories that bulk mail already formed leave through governed deletion of each derived belief; agent email exclude-bulk previews and, with --confirm, applies source exclusion across every census-indexed retained source, which is a coarser operation because it also drops those threads from the mailbox view.

Each observed exchange in People history also carries a short generated summary (ADR-0126). - When. After assessment, drafts and subscription verification, a refresh summarizes at most four correspondence records, newest first. It stops when less than thirty seconds of the slice deadline remain. A failed call or a spent slice ends the summaries, never the refresh. - From what. Each message's verified retained passage, not the body cache. - What it skips. Bulk, excluded and suppressed sources; mail outside the ninety-day boundary; any refresh while learning is paused. - What it must pass. An exact supporting quote, and the People display hazard checks. One invalid result retries once; a second abstains.

The assessment prompt and revision are unchanged.

Correspondent and historical Sent evidence remain attributed, inferred, sensitivity-governed, and unable to supersede an owner assertion. Current tentative communication facts retain the existing thirty-day evidence horizon. Historical import measures validity from the original evidence date, not from import time. Preserve dated historical records for deliberate historical/as-of recall through the existing memory model: an old board appointment can answer who served then without becoming a claim that the person serves now. Extend the import/source contract and tests to preserve historical validity even when the current-state belief is already expired. Historical records persist until their source or owning records are erased; they do not occupy the current-fact snapshot. No new temporal entity graph is required.

Distinguish profile lifetimes too. Owner-authored standing rules persist until corrected or removed. Historical interaction aggregates remain dated history; their recency contribution decays with a 180-day half-life. An inferred role/affiliation older than 180 days, or active investment discussion older than 90 days, becomes historical-only evidence unless refreshed by independent evidence. Older evidence can suggest relevance but cannot label a role or deal current. Historical style examples remain eligible with context/date weighting. Owner confirmation uses the existing governed promotion/corroboration path. Re-reading and self-citation never renew evidence. Chat corrections take precedence through backfill, retry, and policy upgrades.

Sharing also requires an explicit portability decision. Preserve the current adapter's LOCAL behavior. The new evaluated semantic email policy may emit CONTEXTUAL records within the same owner so relevant evidence can participate in project-scoped Chat as well as Email; it may not emit universally portable instructions or change tenant/principal/sensitivity boundaries. This deliberately widens ADR-0090's relevance restriction for the new policy and must be gated on cross-project relevance and attribution evidence. Relevance determines what enters a draft. Account tags describe origin and sending authority, not separate minds.

Memory inspection links back to the source thread when still available. Existing memory browsing and Chat correction remain the initial owner surfaces. Email learning controls can remove sender/topic/style evidence and selected imported sources through a narrow governed email-source lifecycle; they do not add arbitrary writes under the existing GET-only memory API. A source exclusion or deletion must also remove its derived influence and prevent automatic recreation from that same source. Independently supported owner memories remain intact.

7. Architecture and persisted state

Reuse and new components

Component Plan
Apple shell Small app coordinator and stable Chat/Email descriptors; independent presentation state and shared connection/authentication.
Email application service New typed inbox, refresh, feedback, learning, and draft operations in the modular monolith.
Mail access Existing isolated account-specific Gmail MCP servers; add bounded normalized read/change/reply metadata contracts.
Execution Existing durable queue, run/session/event records, tool policy, credential broker, gateway, budgets, and approvals.
Personalization Shared owner feedback ledger, relationship/content projections, and versioned style profile.
Memory Existing service with a separately versioned and evaluated semantic email source/extractor extension.
Client APIs Typed projections and commands; clients never parse chat transcripts into mailbox state or invoke MCP directly.

Keep all Gmail network/OAuth code in gmail_mcp; preserve two-way import isolation with agent_core. Add provider-neutral application values and an MCP-backed capability adapter where necessary. This widens ADR-0071's original tool-only application scope on the record; it does not add a second mail provider or duplicate the Gmail credential stack.

Synchronization is deterministic orchestration, not a model deciding which pages exist. Finite application-generated tasks use the existing worker and normal tool invocation/policy pipeline. Document their typed task intent and audit ownership before implementation. Do not execute Gmail directly from API handlers or invent principal-authored prompts to smuggle imported mail into trusted memory. Mail tool results retain their existing untrusted labels.

Use ordinary operational sessions for bounded ingestion tasks, with server-owned metadata distinguishing them from user conversations. The default conversation index must not fill with polling sessions. User interaction or draft generation for a thread lazily creates/reuses its ordinary conversation session; Discuss in Chat selects it. This introduces task associations and an index filter, not a new session trust class or separate agent loop.

Release operational refresh MCP transports in the admitting process before dispatch, then release the worker's transports when the run becomes terminal, including failures awaiting cost reconciliation. Discard the matching ephemeral catalog cache so worker execution/recovery performs discovery normally. Durable sessions, pinned catalogs, events, and source receipts retain their existing lifecycle. Saving an automatically generated draft can create a separate thread session with its own MCP roster. Release that new roster after activation, including activation failure, while retaining the durable session and its pinned catalog for later Email or Chat use. Saving into an existing thread session does not interrupt its transport. Ordinary run teardown and the MCP idle timeout also apply to draft and Chat sessions.

Assessment and draft response schemas require all declared properties, including nested semantic facts, for strict structured output. Nullable values and empty lists express absence explicitly. Domain defaults and local evidence validation remain unchanged, and token estimation uses the actual transmitted schema.

A completed response that local validation rejects (malformed or out-of-bound JSON, tool calls, or no final message) is neither assessment nor draft evidence, and its text is not stored or reported. During a refresh it affects only its own thread. A rejected assessment records the explicit abstention that ungrounded evidence receives: no importance, urgency or reply need, and a generic review summary. The next slice retries the same passage once. A second rejection is reused until the ordinary invalidation rules reassess the thread. A rejected automatic draft leaves the thread undrafted. The slice continues and its attempt settles as a completed response. An owner-requested draft still fails its task. Provider failures and request rejections still end the run.

If a source, learned profile or junk protection changes during assessment, the guarded save rejects that stale result. Foreground refresh defers only that thread and continues its bounded slice, retaining the attempt's cost. No stale assessment or derived memory is saved, and no immediate model retry is made; a later foreground slice reassesses current evidence. Unrelated conflicts and provider failures still end the run.

The conversation index excludes the server-owned operational marker and draft-only email thread sessions within each repository query before cursor limits apply. An email thread session becomes visible after an owner user.message.created event or an explicit Discuss in Chat action, recorded once per session as a content-free, principal-authored email.discussion.opened event. Existing draft sessions without either event remain accessible to Email but absent from Chat; existing owner conversations remain visible. Native Chat also rejects operational Email sessions from notification and shared-session deep links and prunes any previously cached operational rows during history reconciliation. This removes only local Chat entries; the server session and email evidence remain intact. No draft or session is deleted. Feedback evidence queries are scoped to the current run before fetching events, retaining the same principal and owner-authorship checks.

State ownership

Record Required content and invariants
Account reference Stable principal-scoped mailbox identity, display label, verified provider identity, capability binding, connection health. Never a credential.
Sync/import state Per-account cursors, processed coverage, version, checkpoints, bounded active-job identity, failure/freshness.
Thread/message projection Account-qualified provider ids, normalized headers, source revision, labels, direction, dates, bounded content, completeness, source event links.
Assessment Importance factors, reply need, concise evidence-grounded reason, source/profile/model versions, confidence, attention state.
Feedback ledger Authenticated author, target/scope, judgment, explanation, idempotency key, correction/undo relationship.
Personalization Rebuildable correspondent/topic evidence and shared style profile with source lineage and revisions.
Draft Account/thread/reply target, immutable revisions, owner edits, provenance, send/approval identity, terminal or uncertain outcome.
Source lifecycle Exclusions, erasure/suppression markers, supporting-memory links, retention state.

Apply tenant/principal predicates, forced PostgreSQL RLS where used by existing repositories, and account-qualified identities throughout. Identical thread ids in different accounts must not collide. Changing the manifest default account must not reinterpret historical default-tool evidence. Resolve and persist the actual stable account at event ingestion. Reauthorization to a different Google identity creates a new mailbox identity rather than attaching old learned state.

Gmail is authoritative for external messages and send results. Veetbot is authoritative for its own attention state, feedback, draft revisions, profiles, and memory. Device-local state is presentation and unsaved input, not a second mailbox or memory source of truth. Initial delivery requires online access for authoritative changes; durable offline mailbox caching/edit queues are deferred.

HTTP contract

Finalize schemas and exposure lists before the affected implementation. The authorized scope includes these route families, with exact names subject only to normal contract naming review:

Route family Operation
/v1/email/accounts Read configured account availability, verified display identity, freshness, learning and catch-up coverage.
/v1/email/threads and thread detail Cursor-paginated priority/all-mail views, account filters, safe search, normalized thread and assessment.
/v1/email/refresh and operation status Coalesced foreground refresh/history admission with stable idempotency.
/v1/email/feedback Create, inspect, correct, undo scoped owner feedback and attention judgments.
/v1/email/learning Inspect profile/coverage, pause/resume learning, reset derived preferences or exclude a source.
/v1/email/threads/{id}/drafts and draft detail Start/refine a governed generation run; read/save revisions; discard internal proposals.
/v1/email/drafts/{id}/send-proposal Freeze an exact outbound action and return the existing run/approval references.
/v1/email/threads/{id}/archive Admit an explicitly requested archive or move-to-Inbox operation for one account-bound thread.
/v1/email/threads/{id}/spam and /spam-flag Milestone 31 (ADR-0165), behind its flag: admit an owner's Report spam or Not spam with Archive's gesture; clear or restore a suspected-spam flag without a mailbox change.
/v1/email/threads/{id}/discussion, /dismiss, and /exclude Open the thread in Chat once (email.discussion.opened, which also needs session.write); set or clear handled state at the revision the client saw; exclude the thread as a learning source.
Existing approval routes Resolve every send once; no alternate direct-send HTTP route.

The exact platform scopes are email.read and email.write for application state, plus the existing run/approval scopes when those operations are invoked. These never replace account-specific MCP use scopes. A client request cannot mint account authority; the service checks current principal/account scope at admission and before tool dispatch. Source erasure/reset is an explicit authenticated owner command, never a learned preference or autonomous action. Draft-generation and send-proposal commands also require email.read because their responses contain private thread or draft content; check that authority before admitting the durable task. Learning pause/reset return their command result under email.write without requiring a subsequent read permission.

Use optimistic revisions for writes and idempotency for commands. Document validation, authorization, unavailable/partial state, conflicts, cancellation, and uncertain results using the existing error vocabulary where possible. SSE continues to carry ordinary run progress; email projections refresh by version after completion. No custom streaming transport is required.

POST /v1/email/threads/{id}/dismiss accepts expected_revision and an optional strict boolean dismissed (default true for existing clients). true marks the current source revision handled; false clears that attention state. Repeated same-state requests are idempotent. A stale source revision conflicts without changing state. Clients derive Handled from the existing dismissed_revision == revision projection, so new source material reopens it and the same state is visible on every device. This command changes no Gmail labels, importance feedback, reply-need judgment, or draft content.

POST /v1/email/threads/{id}/archive accepts an expected source revision, a strict archived boolean, and an idempotency key. The server fixes the action to one provider thread and the INBOX label; callers cannot supply another account, tool, or label. Account projections advertise archive_supported and write_server_id; missing support disables this action without disabling Email browsing. Thread projections retain Inbox state and a durable archive operation with its run identity, target state, and pending, completed, failed, or uncertain outcome so another device can recover the presentation.

Archive admission requires current email.read, email.write, run.write, session.write, approval.resolve, and the account's exact read/write MCP scopes. The typed task performs no model work and consumes no automatic-email dollar reservation. The owner is waiting on the gesture, so the task is queued in the interactive class (event-log-and-persistence.md:698) and never waits behind refresh, draft or send work, which stays asynchronous. It uses the existing tool pipeline and approval floor, validating the immutable, expiring owner request against the exact pending action before ordinary one-time resolution. Preserve requested/resolved audit ordering, policy revalidation, worker fencing, and uncertain-effect rules. Identical command retries replay their durable result. Labels alone are not new source content: synchronization must not stale a draft, reopen handled attention, or renew learned evidence solely because Inbox membership changed. The tool's pre-effect dispatch guard checks current owner authority and exact consent again after approval resolution. Recorded-status reads recover durable outcomes without calling Gmail; an uncertain effect remains uncertain until an authoritative fresh mailbox read establishes the requested state. These reads never redispatch an uncertain write. Archive freshness is an account and thread identity check, not a requirement to download every message body or match cached Gmail history. A bounded first-page read verifies the account-bound conversation; Gmail history changes on label writes, so that change must not block an immediate move back to Inbox. The command applies to the entire conversation, including mail that Gmail knows about but the client has not yet loaded, just as Gmail's conversation archive does. Expected local content revision is still checked before admission and dispatch; a concurrent local update invalidates that gesture. Never replace cached source or draft content with a partial archive preflight. Exact-content freshness for sending remains unchanged.

8. Privacy, retention, and controls

The owner's approval authorizes shared learning as a product requirement. Before activation, update the existing data-use disclosure to describe historical analysis, derived preferences, semantic memory, and the hosted-model processing that already serves Veetbot. Keep personalization distinct from generalized model training.

Never place raw mail, addresses, subject lines, draft bodies, or learned personal details in operational logs, metrics, notifications, or checked-in evaluation fixtures. Use private owner-reviewed evaluation data only through the governed local/live evaluation workflow and existing export controls. Shared memory remains scoped to this owner, not other principals or tenants.

Normalized API, tool and run failures log only their error class and opaque request/run/tool identity. Omit exception text and tracebacks at these boundaries because validation and provider errors can embed private mail, model input, or credentials. Every Email HTTP response, including an unhandled server failure, carries Cache-Control: private, no-store.

Use a 30-day last-access cache window for full message bodies; refetch older mail as needed. Retain minimal message ids/cursors and aggregate lineage needed for safe resumption while the account is enabled. Keep a bounded representative set of up to 500 style excerpts total, each at most 2,000 characters, replacing redundant examples as history expands. These limits bound retained examples, not the historical evidence eligible for analysis. Retain unsent owner drafts until discarded; remove sent/discarded draft body revisions after 30 days.

Structured feedback and derived profiles remain until reset or erased, with the freshness rules above and obsolete observations retired on correction. Selected mail and draft content in ordinary tool events, checkpoints, and session/run history retain the existing until-session/source-deletion policy. This can outlast the thirty-day cache and draft-store windows, including for operational ingestion sessions. Encrypted backups retain deleted copies for at most the existing 35 days. This is an explicit retention choice, not a promise that all email copies expire after thirty days. People history's source view reads that retained first-party read event, verified against its passage digest, so Chat can quote a message after its cached body expires (ADR-0126). The cache, email.context and the thread view keep the thirty-day rule. The source-lifecycle action must remove matching retained source content and derived influence across these stores, including duplicate observations, while retaining independently supported owner assertions. Canonical mechanics enumerate those copies before activation. Gmail remains the original mailbox; no separate lossless mailbox mirror is required.

Disconnect/disable stops new account work and hides inaccessible live content; it does not silently delete shared memories. Offer a distinct authenticated clear-learning/source action, with a precise description of what is removed. Existing principal erasure includes every new table, exemplar, draft, job, and derived-source link. Suppression markers retain only the minimum opaque source identity needed to prevent reimport; principal erasure removes those too.

Treat incoming mail and historical examples as untrusted data. Subject lines, signatures, forwarded instructions, and embedded text cannot grant authority, change recipients, weaken approvals, or alter trusted persona. The existing secret/material restrictions and policy gates remain mandatory.

9. Delivery sequence

Owner approval authorizes this feature scope, not a silent declaration that earlier milestones have completed. Milestone 26 is assigned and its thirty-two gates are registered. Preserve the current verified milestone ceiling and record existing email activation gaps separately.

Phase Deliverable Exit evidence
0. Design contracts Canonical email-experience design, approved ADR scope, state/map/gate registration, API/events/schema/retention contracts, test corpus and evaluation definitions Documentation checks; all affected requirements reconciled before code.
1. Shared mode shell Chat/Email navigation on all three Apple clients; shared connection, preserved Chat state; capability unavailable fallback Mode-switch, streaming/composer preservation, accessibility and native navigation tests.
2. Inbox foundation Account identity, reliable bounded sync/history checkpoints, typed projections, safe thread rendering, freshness and other-mail access Fake-provider contracts, API boundaries, PostgreSQL parity/migrations, two-account failure/recovery tests.
3. Personalized attention Historical interaction evidence, role/topic profiles, feedback/undo, high-precision selection, automatic history progress Chronological replay, source/identity deduplication, correction tests, declared ranking benchmarks.
4. Shared semantic memory Evaluated email extraction with exact provenance and useful shared recall, owner-correction precedence, source lifecycle Existing memory gates unchanged plus new semantic corpus, cross-mode recall and erasure tests.
5. Adaptive drafting and send Shared style learning, internal drafts, edit/conflict/revision lifecycle, correct reply threading, exact-message approvals and outcomes Style/reply-need evaluations, API and multi-device journeys, adversarial/uncertain-send tests.
6. Integrated validation Performance/cost calibration, long-history resumption, all Apple journeys, real-mailbox smoke on both accounts Complete local checks and private owner acceptance evidence.
7. Release Hosted verification and, only when separately authorized, PR/review/merge and production delivery Final-head CI/CodeRabbit, merged-revision release identities, delivery and installed-client smoke.

Backend contracts and the client shell can proceed in parallel after phase 0. Ranking and semantic-memory work can proceed independently after source provenance exists. Drafting depends on thread/reply contracts and profile access; final delivery requires all five requested feature areas, not merely an inbox shell. Do not enable unfinished autonomous drafting or unevaluated semantic extraction as if the complete mode has shipped.

Likely implementation surfaces are clients/apple/Veetbot and its shared tests, src/gmail_mcp, the API/composition/runtime application seams in agent_core, new email domain/repository services, memory formation/context/lifecycle, migrations, and evaluation fixtures/gates. Keep changes limited to these requirements; no unrelated framework migration or general provider rewrite.

Canonical documentation surfaces include the engineering plan, project state/current milestone/map/readiness, the new design, email integration, Apple client, API/policy scope tables, memory/context/lifecycle, deployment and data-use documentation. Add verified evidence as phases complete, never in anticipation of a passing result.

10. Acceptance and validation

Behavior changes use Lane A and strict red-green-refactor. Add the smallest documented behavioral test first, run it to an expected behavioral failure, then implement and rerun its partition and risk-relevant checks. Environment/import errors do not count as red evidence. New adapters start with shared contract tests; every public command includes validation, authorization, failure, and retry coverage.

Functional release requirements

Requirement Required proof
Modes are presentation Switching preserves Chat, memory/persona identity, drafts, active streaming, and selections; thread handoff reuses an ordinary session.
Active-only retrieval Entry/foreground/timer refresh works; hidden/background clients admit no new slices; multi-device requests coalesce; in-flight work finishes within bounds; aggregate reservations survive concurrency/recovery and cannot renew limits per batch.
Complete and honest source state Pagination/change replay/cursor expiry/revocation/partial account failures are correct; no false fully-current claim.
Historical power Progress covers the permitted ninety-day window under repeated available active slices; older history remains paused, and import restarts/duplicates do not double-count evidence.
Learning Person/topic/thread feedback have distinct effects; Chat and Email use the same profile; corrections/undo/reset remove prior influence and rerank already-loaded mail on another device.
Relevant drafts Auto-draft only when a reply is appropriate; no duplicate proposal, overwrite of edits, already-answered reply, or attachment hallucination.
Multi-device editing Revision conflicts preserve both edits; stale source requires review; saves are visible on another client.
Sending Exact account/recipients/subject/body require owner approval; fresh Gmail read detects an external reply since polling; failed freshness defers send; edits invalidate review; post-dispatch uncertainty cannot auto-retry; threading/recipients are correct; cold/warm notification recovery opens the associated draft safely.
Shared memory Email facts affect relevant project-scoped Chat and other-account tasks with provenance; owner corrections prevail; old board/deal evidence remains historically useful without asserting a current role; import time does not renew it.
Isolation and trust Zero foreign-principal exposure, credential storage, instruction promotion, unsupported owner-authority escalation, or cross-account credential dispatch in the adversarial corpus.
Lifecycle Cache expiry, source exclusion, profile reset, and principal erasure cover projections, examples, jobs, revisions, lineage, and formation replay.

Approved quality targets

Freeze a private representative corpus before tuning: at least 200 labeled threads, spanning both accounts and the six relationship categories, ordinary personal correspondence, new relevant senders, bulk mail, and low-value mail. Use a chronological holdout with at least 30 independent inbox snapshots and 100 held-out priority judgments; build profiles from earlier evidence only. Keep all messages of a conversation out of cross-split leakage, and do not form memories from held-out future correspondence before assessing it. Label the complete candidate pool for every holdout snapshot so missed important mail has a valid denominator; count repeated appearances separately from independent threads when reporting uncertainty.

Measure the following as release gates, not current capability claims:

  • Priority precision: at least 90% of displayed top-five items are judged worth attention; neither account below 85%. Report sample counts and confidence intervals rather than hiding uncertainty behind one percentage.
  • Useful coverage: on snapshots with at most ten owner-labeled important threads, at least 90% appear within the first ten. Include important FYIs and material founder/board updates, and report actionable and informational subsets and misses by relationship class. For overloaded snapshots report ranked recall and overflow separately; the five-row presentation is not a promise that only five important messages exist.
  • Personalization: compare with newest-first, Gmail-important, and the same ranker without history/feedback. Target ten percentage points of top-five precision lift over the strongest baseline, or at least 95% absolute precision without coverage regression when the baseline already exceeds 85%. Declare this ceiling rule before evaluation; do not invent an exception after a miss.
  • Draft relevance: at least 90% of auto-drafted threads are judged to need a reply. Draft for at least 80% of unambiguous, fully supported reply opportunities within the top-three automatic-draft slots. Assess at least 50 reply/no-reply cases with at least 30 emitted drafts across both accounts; report abstentions and capacity exclusions so doing almost nothing cannot pass.
  • Style usefulness: in at least 30 blind paired draft comparisons, the owner prefers the personalized version at least 70% of the time, and at least 70% need no substantial rewrite. Score factual faithfulness separately; a fabricated commitment or recipient is a blocking correctness failure.
  • Semantic memory: at least 95% precision and 85% recall on labeled useful email facts, zero attribution/authority violations, and demonstrable utility beyond the current excerpt adapter. Preserve every existing memory benchmark requirement; bind activation evidence to the exact new policy tuple.

The owner reviews small batches through the feedback experience; labels and examples stay private. Synthetic fixtures cover deterministic and adversarial behavior, but cannot substitute for the owner's style/importance judgments. If a target fails, diagnose and improve the implementation or explicitly revise the requirement before retesting; never weaken an existing acceptance rule.

Performance targets are a cached priority view within one second and a fresh two-account view within fifteen seconds under the representative test workload. Report p50/p95, mailbox size, provider latency and partial-result behavior. Historical work must not block an opened thread, draft editing, or Chat. These are controlled-load targets, not guarantees against provider outages. Record historical throughput, model calls, spend, cache hit rates, and estimated remaining work before activation under existing budgets.

Run make docs-check, make check, make test-integration, make test-apple, and make test-apple-ui for implementation delivery, plus the focused email/memory suites, migration round trips, and governed evaluation commands registered by phase 0. Run make citations-fix after cited document edits and inspect the result. Native UI evidence must cover iPhone, compact and regular iPad, and Mac; retain the existing Chat/SSE regressions.

Real-mailbox acceptance proves both account identities and retrieval, historical progress, meaningful feedback, one useful draft on each account, shared Chat recall, and an explicitly owner-approved test send with verified recipients and threading. Live model calls and sends occur only during authorized execution, only under the applicable explicit authorization. Existing Milestone 18 owner-smoke gaps remain visible until their own evidence is recorded.

11. Authorized scope and delivery boundary

Milestone 26 includes the mode shell, active priority inbox, broad historical learning, shared sender/content/style personalization, relevant internal drafts, native editing and approved sending, and useful semantic email memories.

It excludes another provider, self-service account onboarding, terminal UI work, autonomous sending, automatic Gmail writes, Gmail draft synchronization, background monitoring/digests, attachment processing, calendar operations, permanent mailbox deletion, offline-authoritative editing, a separate email persona, model routing changes, and a general learned memory policy. Existing Chat tools remain available under their existing contracts.

Owner approval authorizes this implementation scope and its canonical specification/gate work. It does not authorize creating a pull request, publishing these documents, merging, deployment, live test sends, or increased provider budgets. Those actions follow the user's existing authorization boundaries when the concrete work is ready.

12. Source basis

Hard gates

These thirty-two blocking gates belong only to Milestone 26. Pending check references remain pending until matching implementation and verification exist. Private quality and owner-smoke evidence cannot be replaced by synthetic tests.

  1. Shared mode state. Chat and Email preserve the same principal, agent, persona, memories, active Chat run, composer and selections across switching and thread handoff. Registered as gate.email.experience_shared_mode_state. M26.
  2. Native layouts. iPhone, compact and regular iPad, and Mac expose accessible adaptive navigation and editing while retaining supported operating-system floors. Registered as gate.email.experience_native_layouts. M26.
  3. Scope and feature confinement. Email application routes and capabilities require the email experience feature flag, exact email.read or email.write scope, and current account-specific authority; disabled composition exposes no email application routes. Registered as gate.email.experience_scope_and_flag. M26.
  4. Stable account identity. Verified principal-scoped mailbox identity survives default-account changes; reconnecting another Google identity cannot reinterpret old evidence or dispatch with another account credential. Registered as gate.email.experience_account_identity. M26.
  5. Foreground admission. Entry, foreground return and sixty-second visible refresh admit bounded coalesced work; hidden or background clients admit no new slices and repeated multi-device requests cannot create an unbounded backlog. Registered as gate.email.experience_foreground_admission. M26.
  6. Synchronization contract. The first-party MCP contract returns bounded versioned account-qualified identities, history changes, labels, direction, dates, reply headers and completeness with shared fake-provider contract coverage. Registered as gate.email.experience_sync_contract. M26.
  7. Synchronization recovery. Projection updates and cursors commit consistently; duplicate or reordered observations, pagination, cursor expiry, deletions, revocation and partial account failure preserve accurate recoverable source state. Registered as gate.email.experience_sync_recovery. M26.
  8. Historical progress. Active bounded slices resume through eligible history within the latest ninety days (ADR-0096) without starvation, duplicate evidence or claiming metadata-only coverage as semantic understanding. Older history remains paused. Registered as gate.email.experience_history_progress. M26.
  9. Governed task execution. Typed deterministic ingestion uses ordinary durable worker leases, checkpoints, cancellation, policy and budgets without direct API-handler Gmail calls, fabricated owner messages or model-selected pagination. Registered as gate.email.experience_governed_tasks. M26.
  10. Aggregate automatic-email cost. Atomic reservations and actual usage cover all automatic email stages across accounts and devices, enforce the configured ceilings (shipped defaults USD 20 per UTC day and USD 200 per rolling thirty days) with lower limits prevailing, and release or reconcile reservations on every terminal path. Registered as gate.email.experience_aggregate_cost. M26.
  11. Feedback scope. Thread, person, topic and reply-need feedback have distinct authenticated effects; explicit owner corrections dominate inferred evidence across Email and Chat. Registered as gate.email.experience_feedback_scope. M26.
  12. Feedback replay and undo. Idempotent feedback, undo, correction and reset rebuild derived influence from surviving evidence without replaying removed influence or learning negative preference from mere viewing or dismissal. Registered as gate.email.experience_feedback_replay. M26.
  13. Assessment invalidation. Source and relevant profile or extractor revisions invalidate affected assessments; unchanged current polls spend no classification work while unfinished historical work remains resumable. Registered as gate.email.experience_assessment_invalidation. M26.
  14. Priority quality. Frozen chronological holdouts satisfy the approved precision, coverage and personalization gates, per-account floors, no future-evidence leakage and reported uncertainty against declared baselines. Registered as gate.email.experience_priority_quality. M26.
  15. Relationship evidence. Correspondent and organization linkage requires supported identity and relationship evidence; role or domain alone cannot establish portfolio membership, ownership or personal importance. Registered as gate.email.experience_relationship_evidence. M26.
  16. Style lineage. Historical SENT examples exclude quoted, forwarded, automated and ambiguous-author material as specified; user edits dominate, generated-draft lineage prevents self-training, and learned style never edits trusted persona. Registered as gate.email.experience_style_lineage. M26.
  17. Style quality. The private paired evaluation satisfies the approved style preference and rewrite thresholds with separately blocking factual and recipient faithfulness checks. Registered as gate.email.experience_style_quality. M26.
  18. Draft relevance. At most three relevant automatic reply proposals are prepared, with approved reply-need quality and abstention for answered threads, missing decisions, essential unread attachments and unsupported commitments. Registered as gate.email.experience_draft_relevance. M26.
  19. Draft revisions. Server-owned internal drafts preserve owner edits and immutable history, avoid duplicates, use optimistic concurrency and retain both conflicting device edits without a Gmail write. Registered as gate.email.experience_draft_revisions. M26.
  20. Exact send approval. Every send requires approval of the exact mailbox, recipients, subject and body; edited content or observed source changes invalidate prior approval, and no direct send API bypass exists. Registered as gate.email.experience_send_approval. M26.
  21. Atomic send claim. A fresh bounded thread read precedes a serialized local frozen-action claim; concurrent edit or double-send cannot reuse stale approval, while the unavoidable external post-read race is stated honestly. Registered as gate.email.experience_send_claim. M26.
  22. Send uncertainty. Confirmed sends persist outcome; pre-dispatch failures follow governed retry, but any possibly dispatched non-idempotent send becomes uncertain and never automatically resends after crash or lost response. Registered as gate.email.experience_send_uncertainty. M26.
  23. Reply headers and recipients. Reply and reply-all use verified sender identities, correct Reply-To and owner exclusion, matching subject/thread and RFC reply headers without trusting message-body recipient instructions. Registered as gate.email.experience_reply_headers. M26.
  24. Semantic memory grounding. A separately versioned evaluated email extractor yields exact source-span-grounded useful facts with account, message, author and event provenance and the approved precision and recall floors. Registered as gate.email.experience_semantic_grounding. M26.
  25. Semantic memory authority. Correspondent and historical SENT evidence stay attributed, inferred and sensitivity-governed, cannot overwrite owner assertions, and leave existing evaluated extraction policies unchanged. Registered as gate.email.experience_semantic_authority. M26.
  26. Semantic memory lifecycle. Historical source age and freshness govern current beliefs; import, repeated reads and self-citation cannot renew stale claims, and deliberate historical recall remains dated. Registered as gate.email.experience_semantic_lifecycle. M26.
  27. Shared recall. Eligible new-policy contextual email memories and profiles are relevantly usable across the same owner's accounts and Chat without broadening tenant, principal, sensitivity or credential authority or the old adapter's LOCAL contract. Registered as gate.email.experience_shared_recall. M26.
  28. Source erasure. Scoped authenticated exclusion and erasure remove source copies and derived influence across projections, examples, events, checkpoints, profiles and memory with replay suppression while preserving independently supported owner evidence. Registered as gate.email.experience_source_erasure. M26.
  29. Retention. The thirty-day body-cache and sent/discarded-draft windows, five-hundred bounded style excerpts, until-deletion audit copies and backup expiry are enforced without misrepresenting cache expiry as universal deletion. Registered as gate.email.experience_retention. M26.
  30. Privacy and trust. Adversarial mail cannot confer action authority, alter persona or leak foreign-principal data or secrets; raw personal content stays out of logs, notifications and public or checked-in evaluation material. Registered as gate.email.experience_privacy. M26.
  31. Application API boundaries. Every new public command has happy-path, validation, authorization, conflict, failure and retry coverage with scoped repositories, PostgreSQL RLS, migration parity and current account authority. Registered as gate.email.experience_api_boundaries. M26.
  32. Integrated release evidence. All functional and quality gates, native platform lanes, cost/performance calibration and authorized two-account mailbox smoke pass; final-head hosted review and merged-revision production evidence remain required before milestone completion. Registered as gate.email.experience_release_evidence. M26.

Persistence contract

The first implementation stores independently revisioned email application records in email_records, keyed by tenant, principal, kind and opaque key. The closed application DTO supplies each JSON payload; revision, created time and updated time are explicit columns. The repository never returns mutable aliases into stored payloads. Key-ordered pagination accepts an exclusive after key and a limit from one to one thousand. A thread-summary read pages the same thread records with messages omitted; PostgreSQL drops them in the query, so listing neither transfers nor decodes message bodies. put compares the exact existing revision (zero means absent), requires the new revision to be expected plus one, and raises a conflict without mutation on a mismatch. Delete likewise requires the exact positive existing revision; missing or foreign records conflict.

The shared repository contract runs on both in-memory and PostgreSQL adapters. PostgreSQL uses explicit principal predicates and forced tenant RLS. A principal lock serializes budget admission, source mutations and dispatch claims inside a short unit of work; PostgreSQL holds the advisory lock through transaction end, and the deterministic adapter holds an async lock to context exit. No network call is made inside either lock. PostgreSQL supplies rollback; the ordinary in-memory unit of work retains its documented deterministic, non-transactional scope. Immutable draft and feedback histories use independently keyed records, not destructive rewriting of a prior revision's content.

The existing maintenance worker performs mailbox-wide body retention, at most once an hour. It scans in pages of at most 100 records outside the mutation lock, then re-reads each expired candidate in a separate short locked transaction before erasing it. Draft-history deletion also rechecks the current parent draft's retention state. Request paths never run this sweep. Instead, every reader of cached content enforces the thirty-day rule on what it reads: point reads of a thread or draft, the refresh task's selection of mail to fetch again or assess, and an import's comparison with its cached copy. A delayed maintenance sweep therefore cannot expose or renew an expired body.

Source-content erasure mechanism

The owner-authorized email-source erasure operation is a narrow content-erasure exception to append-only event payloads. It preserves event identity, sequence, actor, time, tool/action identity, approval decisions, and argument hashes; it replaces only the selected source's content with an explicit erasure marker and appends an email.source.erased audit event. It does not rewrite user messages or reinterpret an earlier action. The original Gmail account-to-server binding is server-owned session metadata and must never be refreshed from a later manifest. The builtin email.context result instead carries server-authored source wrappers in its ordinary runtime event. These remain erasable in an existing Chat session that has no Gmail-specific metadata; the source's account, thread and message identities must match. Independent context and writing-example wrappers preserve unrelated accounts and owner-authored content.

Both persistence adapters expose source erasure through the existing lifecycle repository. Before mutation they reject an active associated run or unfinished potential producer: target-account operational sessions, runs with Email or Gmail-read authority, and pending email.context invocations. These must first be cancelled and allowed to settle, even before their first source result exists. This conservative owner-scoped guard may wait for another email-capable Chat run; it does not delete that run or its unrelated content. Session/run locks serialize the check with admission, and exclusion tombstones prevent new formation after commit. Source-qualified normalized result objects are removed from tool events and invocation result copies. Terminal checkpoints and derived summaries are invalidated so a replay cannot restore the content; other messages in a mixed thread and owner-authored conversation events remain. Independently supported owner beliefs remain. Source tombstones survive content removal and prevent reimport. Exported artifact copies require the existing artifact deletion/retry machinery; a pending byte deletion is reported as pending, never as successful erasure. No generic API gains event editing.

People-derived copies in other sessions also receive a durable read/write fence and bounded cleanup receipts. Snapshot trace references are captured before source deletion, so frozen plans, later replies, and generated documents remain discoverable after the original People rows disappear. Receipts retain only opaque suppression keys and cleanup cursors; owner-confirmed beliefs stay outside Email-derived belief suppression. The source response stays cleanup_pending while either byte deletion or People copy cleanup remains outstanding, including on retries after its source rows are gone.

Semantic activation and passage provenance

AGENT_EMAIL_SEMANTIC_EVIDENCE optionally names an operator-reviewed JSON activation artifact. No path means the semantic policy stays off. The loader requires email-semantic@1, the actual provider/model, the exact deployed build reference, a corpus digest, at least 95% supported precision and 85% useful-fact recall, a positive improvement over labeled excerpt-baseline comparisons, preserved existing memory benchmarks, zero fabrication/authority/relevance failures, positive attribution/cross-project/historical cases, and the current implementation digest. That digest covers the assessment prompt/application, source validator, formation and retrieval code; changed code invalidates an older artifact. Synthetic test fixtures are not activation evidence.

The pure agent_core.evals.email_quality.score_email_quality scorer validates an anonymized label-only corpus and emits aggregate counts, uncertainty and threshold results. Missing owner evidence and synthetic corpora remain pending. Its report is distinct from an operator-reviewed activation artifact; unit tests of the scorer never satisfy the private quality gates.

ADR-0110 admits one offline, non-activating evaluation beside it. An importance replay builds, for each frozen snapshot, the evidence the production assessor sees, ranks the candidate pool once through the unchanged production assessment and once through a typed-judgment arm (typed-judgment.md) that feeds the same priority formula and clamps, and scores both rankings with the unchanged label-only scorer. Its source bundle stays outside the repository, its outputs carry opaque identifiers and aggregates only, and its decision rule is declared before a run. It changes no production behavior, no production module imports it, and its report can neither satisfy nor waive a gate of this milestone; model routing changes stay excluded below, and any production use of a judgment provider for email needs a later ADR amending ADR-0092.

The replay is agent_core.evals.email_importance_replay, driven by agent eval email-importance with exactly one of --check-bundle, --run, or --compare. Its inputs are the owner's label-only corpus and a private source bundle bound to that corpus by digest and refused if it sits inside the repository, repeats or invents a thread, omits a replayed thread, or holds a candidate with no message at its observation time. Each snapshot is replayed in a fresh in-memory composition with its clock fixed at the observation time: profile threads are imported only up to profile_evidence_through and never assessed, and a candidate's messages after observed_at are not imported, so no future evidence is visible. Both arms run the production assess() unchanged; the judgment arm replaces only the model call, mapping three Scores and three Nouls onto the production assessment, so passage selection, evidence assembly, the priority formula, its clamps, and the list order are production code in both. The ranking is production's own list order over the complete candidate pool.

Four arms are reported rather than two, because a judgment provider cannot emit a timestamp: production, production with expiry stripped, judgment with a Noul for "every reason to attend has already ended", and judgment without it. The report header states the limits instead of hiding them: the judgment arm quotes nothing, so production's grounding check is vacuous for it; a future expiry cannot be represented; owner feedback and shared memories are not replayed from the bundle; and drafts, style, and semantic memory are not evaluated. A run needs RUN_LIVE_MODEL_TESTS=1, a committed tree, a non-production deployment mode, a model policy for the production arm, and a positive --max-cost-usd that both arms spend against through one journal; its output directory is created 0700 and never reused, so one holdout run exists per question-set digest. The decision rule is declared in the run header before any provider call: a judgment arm merits a follow-up ADR only if production passes the three importance checks, the arm passes them too, the paired top-five precision difference has a 95% lower bound of at least minus five points, at most a tenth of snapshots regress, and failures plus abstentions are at most two percent. If production itself misses, no conclusion is drawn, and a synthetic corpus is always pending. Reports and artifacts carry opaque identifiers and aggregates; a malformed bundle is refused without quoting it, because validation errors repeat their input. The privacy gate exercises the replay as one of its boundaries.

The semantic source contract accepts bounded passages from get_thread_page and get_message_body. A continuation carries its byte offset and the original header event/session, so the service checks the same account, message, provider revision, sender, and original evidence date against real governed tool events. It stores passage hashes and source receipts, not a second body copy. Source facts deduplicate across passages and re-reads. A passage processed successfully does not mark an unread remainder as assessed. Coverage remains explicit until all resumable work is complete. Semantic writes append a content-free current-run audit under the worker lease in the same transaction; cached older evidence keeps its original source session/date. A receipt whose source or header session has since been deleted has no governed event left to check, so it is rejected like a missing event: that passage forms nothing and the task continues.

Historical import order cannot supersede newer evidence, and several supported facts in one source remain distinct. Source erasure also removes selected inferred beliefs from shared recall traces and invalidates terminal checkpoints that carried them. Any active dependent run must settle before these retained copies are purged; independently confirmed owner beliefs remain.

Milestone 31 unsubscribe extension

ADR-0112 authorizes email unsubscribe assistance as a separate parallel milestone that builds on this one. Its census rides the foreground refresh slices defined here and adds no query, model call or automatic-email dollar reservation; its typed tasks follow the Archive task's interactive class, server preparation and owner-gesture consent; and its records are new kinds in email_records under this document's retention, source-erasure and privacy rules. Nothing in it monitors a mailbox or writes to Gmail without an owner act, so this milestone's exclusions and thirty-two gates are unchanged.

ADR-0165 extends that milestone into this one's assessment and projection. The assessment gains a spam verdict, its prompt and both revisions change, and each message carries Gmail's DMARC result as sender_check metadata outside the content fingerprint. Behind the Milestone 31 flag, a grounded verdict on an unprotected sender marks the thread suspected spam: it projects with priority zero and no reply needed, so it leaves the priority view and automatic drafts, and memory source validation treats it as bulk. Report spam on a thread is the Archive gesture above with a fixed SPAM delta. The rules are specified and gated in email-unsubscribe.md; this milestone's thirty-two gates are unchanged.

Cheap junk screening

ADR-0203 admits a bounded, cached TypeSafe Jev first pass before the full assessment. It defaults off; shadow mode preserves normal assessment, and filter mode needs independent reviewed calibration matching its pinned policy/model. Protected or uncertain mail uses the existing assessment. Obvious promotional bulk remains distinct from spam, stays readable in Other and All mail, and Important restores priority. Judgment attempts consume the existing aggregate and run allowance through durable reservations; unknown outcomes remain conservatively charged. There are no automatic Gmail writes and no change to the quality gates.

The separately approved ADR-0204 pilot uses only a valid raw spam/bulk choice under the reviewed questions and pinned model, without the general filter's score cutoff or topic veto. A selected-sample packet requires 43 unique owner labels, twenty correct junk nominations, seven legitimate keeps and zero false-positive nominations, bound to the exact policy, owner, tenant, work account and a maximum seven-day window. Invalid or expired pilot evidence stops new calls; bypassed mail returns to normal assessment through the existing bounded refresh. The principal lock atomically commits each bypass with a content-free daily counter and hashed-conversation admission. At most fifty new conversations per UTC day are admitted; replay costs no further slot and evidence revisions never reset the allowance. A concurrent last-slot loser uses ordinary assessment. Expiry is rechecked before dispatch and at save. No other first-pass protection, budget, quality gate, or mail mutation authority changes.