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.
| Host | URL |
|---|---|
| Production | https://duckbill.jasonkoch.io |
| Preview | https://duckbill-dev.jasonkoch.io |
| Mailbox | jason@jasonkoch.io |
| Account | 468d17be-082a-47a5-9324-245bb928b669 |
| Sandbox | Supabase 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
| Step | Result | Evidence |
|---|---|---|
| 1. Connect / session | pass | Owner 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. Sync | gap then wired | Before 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. Cardinality | pass (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. AI | gap | No 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. Triage | pass | /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. Correction | pass with gap | Authenticated 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-back | mixed | POST /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. Replay | pass (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)
| Metric | Count |
|---|---|
| schema_migrations head | 0020 (0001–0020 applied) |
| mail_messages | 0 |
| message_instances | 0 |
| orphan instances | 0 |
| UNPROCESSED classifications | 0 |
| current_ai_inferences | 0 |
| human_corrections | 0 |
| mail_commands | 1 (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.
