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:
- Review & Send freezes the exact current revision and presents From account, To/Cc/Bcc where present, subject, and full body.
- Final Send explicitly resolves the ordinary approval for that exact action.
- 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.
- 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¶
- Engineering plan, especially Milestones 18, 21, 22 and the shared-core requirement in Section 29.
- Email integration, ADR-0071, and ADR-0085.
- Attributed communication memory, adaptive memory design, persona design, and memory retrieval design.
- Apple client contract and ADR-0049.
- Current source and test inspection: Gmail normalization/tool roster, communication source admission/formation, Chat-owned root navigation, versioned API and native test patterns. Existing implementation is a baseline, not evidence that Milestone 26 capabilities pass.
- Glen history: “Finish Gmail Integration Runbook” (Andy, 2026-09-03), “Complete Gmail account authorization” (Andy, 2026-09-09), and “Veetbot Memory Formation Issue” (Andy, 2026-09-10). Current repository contracts take precedence over historical recollection.
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.
- 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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.