Skip to content

MVP end-to-end evidence (MAIL-50 / MAIL-103 / MAIL-126)

Live vertical slice on one Microsoft mailbox. No secrets and no mail bodies are recorded here.

HostURL
Productionhttps://duckbill.jasonkoch.io
Previewhttps://duckbill-dev.jasonkoch.io
Mailboxjason@jasonkoch.io
Account468d17be-082a-47a5-9324-245bb928b669
SandboxSupabase hijznwisiirxdknbnvyz, schema inbox_agent

A second HEALTHY Graph account (JSKOCH@msn.com) exists from earlier connect work. This checklist does not use it.

How to re-run

npm test -- tests/application/mvp-e2e.test.ts
npm run mvp:e2e

Authenticated live probes use Sign in with Apple or POST /api/session/owner (ops escape). Mutating calls need x-csrf-token. Bounded live Graph sync is POST /api/sync/initial with { "pageSize": 10, "replay": true }. Cardinality is GET /api/sync/cardinality. Both return counts only.

Checklist

StepResultEvidence
1. Connect / sessionpassOwner session POST /api/session/owner returns 200 on production and preview (userId 4887d5b6-fb4e-4f20-bf73-33fe6adf1522). GET /api/me is 200 with that session and 401 without. /login (Sign in with Apple) and /connect return 200. Mailbox authorizationStatus=HEALTHY on /api/health and /api/v1/health.
2. Syncgap then wiredBefore this change, initial_sync_status=pending, mail_messages=0, message_instances=0, no sync_checkpoints. Connect succeeded (MAIL-21); nothing hosted called runInitialSyncPage. POST /api/sync/initial now runs one bounded Graph page for the owned account. Re-run after preview deploy and record counts only.
3. Cardinalitypass (empty set)Schema has no mail_messages.is_read. 0:0 holds (orphans=0, instances ≥ messages). Live non-zero counts require step 2. Fixture replay in npm run mvp:e2e asserts the 1:N rule on synthetic mail.
4. AIgapNo ai_inferences / current_ai_inferences rows. After a sync page, UNPROCESSED classifications and isolated CLASSIFY work items are expected. No worker host runs classify yet, so mail must remain visible (UNPROCESSED / queue empty is OK). Enqueue failure must not hide instances.
5. Triagepass/inbox, /inbox/needs-me, /inbox/settings return 200. Authenticated GET /api/v1/home includes provider_health_summary with mailbox jason@jasonkoch.io and provider microsoft-graph, not subject-only. Needs Me / Waiting / Respond / Alerts are empty arrays; empty state is explained in the UI (attention queues, not unread count).
6. Correctionpass with gapAuthenticated POST /api/corrections returns 200 (createdActiveRule=false). The web route uses an in-process store, so inbox_agent.human_corrections stayed 0. Path exists; durability is process-local.
7. Safe write-backmixedPOST /api/mail/send and /api/mail/delete are 403 POLICY_DENIED with or without a session. POST /api/commands with Send/Delete is 403. MarkRead is not waived: with ENABLE_MICROSOFT_WRITES off the API returns FEATURE_FLAG_OFF; live preview/production accepted a MarkRead command as PENDING (flag appears on) but a fake instance hit a FK. API success is not healthy. Convergence requires later incremental sync (SUCCEEDED or RECONCILE_NEEDED). This change binds a live Graph adapter only when the write flag is exactly true.
8. Replaypass (fixtures)replayFixtureSync re-pages initial sync and incremental delta on synthetic fixtures. Instance count does not grow; incremental replay created=0. fixtures/production-mail/ still contains only POLICY.md. Do not copy production bodies into fixtures/.

Live HTTP (2026-09-26)

Unauthenticated: /api/me and /api/v1/home → 401 UNAUTHENTICATED. /api/health → 200, database/configuration ok, mailbox HEALTHY, initialSyncStatus=pending, stale lag noted. Send/delete → 403.

Authenticated owner session on production and preview: /api/me 200; home/queues 200 with mailbox+provider on the health summary; corrections 200; send/delete 403.

Cardinality (sandbox, counts only)

MetricCount
schema_migrations head0020 (0001–0020 applied)
mail_messages0
message_instances0
orphan instances0
UNPROCESSED classifications0
current_ai_inferences0
human_corrections0
mail_commands1 (MarkRead PENDING from the probe)

Update the count row after a successful POST /api/sync/initial — still counts only.

Out of scope (not expanded)

Custom mail client, iOS, calendar, contacts, composer, NFR gates (MAIL-51/109), release docs (MAIL-49/62), epic MAIL-12.