Mindbridge Β· track status

CONNECTOR-TRUTH

Whether a customer can connect a source and stay connected β€” and why, while the OAuth app sits in Google's testing mode, every Gmail source dies within 7 days of consent.

Re-measured 2026-08-16 from gh issue list. Verify before starting β€” issue checklists decay as boxes are ticked, and the dead-connector count is a live production figure. This page has never been published; publish fresh per docs/status/artifacts.json.

The liveness half of this track is tracked on MEASUREMENT, not here. "Nothing tells us when a connector dies" is connector-liveness (#3060), ruled p1 on 2026-08-14 and still p1 β€” do not re-ask. This page covers the compliance pair that makes the dying stop and does not duplicate it.

Next β†’#1248 CASA Tier 2 Assessment β€” Preparation & Completion Β· the externally-gated step, because the vendor's turnaround can't be compressed.
5 of 9 production Gmail connectors dead Β· 2 open p1 issues β€” the Google verification pair.
6open issues
5 / 9Gmail connectors dead
7 dtoken lifetime in testing mode
Open work Why this order Rulings History

Open work

CASA Tier 2 Assessment β€” externally gated

p1 #1248 chore: CASA Tier 2 Assessment β€” Preparation & Completion externally gated β€” lead time fixed Mizo
Sub-issue of the OAuth verification (#1241): Google requires a CASA Tier 2 assessment (OWASP ASVS Level 2 β€” 73 requirements) of the restricted gmail.readonly scope before verification completes. Triage, the OWASP ZAP dynamic scan and the Fluid Attacks static scan are already done; what remains is the manual review of architectural requirements, fixing every gap, and a final clean run before submitting to the Google-assigned vendor. The body forbids submitting until those steps are complete β€” findings during the official assessment extend the timeline and may incur re-assessment fees. Cost is not free: $150–$500 depending on the vendor Google assigns (the parent #1241 says CASA Tier 2 is "typically free"; budget from this sub-issue).

Connector identity β€” a connector that ingests but links nobody

p2 #3418 google_calendar writes no person_sources rows β€” 2,766 messages, 5 attributed persons, zero connector-identity links ingests fine, links nobody All

Moved here from VIEWER-TRUTH and retitled on 2026-08-30 β€” it is not a viewer-resolution question. Census: 4 sources across 4 tenants, 2,766 messages, 0 distinct persons in person_sources, while every other connector produces identities (maildir 4,094 persons, whatsapp_live 1,164, gmail 70, whatsapp 50).

The ambiguity the old framing carried is resolved. The messages do carry person ids β€” 5 people, one with 1,471 messages β€” so attendee attribution is not failing. What is missing is the person_sources write that links those identities back to the connector, which is what makes them invisible to every source-membership census. The old title ("attributes zero persons") sent readers into the calendar parser; the defect is downstream of it.

Google OAuth Verification β€” the parent

p1 #1241 chore: Complete Google OAuth App Verification for Gmail (Restricted) + Calendar (Sensitive) Scopes the parent β€” leaves testing mode Mizo
The parent of the CASA sub-issue. Until Google completes verification the app stays in testing mode β€” capped at a hundred manually added test users β€” so the Gmail connector cannot leave the mode that limits it. Scopes: gmail.readonly (Restricted) and calendar.events.readonly (Sensitive). Remaining checklist: the demo video, the OAuth 2.0 client IDs, the consent-screen scope justifications, and the submission for verification. The CASA step may require the app to be deployed and accessible; coordinate with infra if a staging environment is needed.

Why is it in this order?

Ordering criterion β€” and what it ignores

CASA (#1248) sorts above its OAuth parent (#1241) because it is the externally-gated prerequisite on the critical path to leaving testing mode: Google assigns the vendor and the assessment has turnaround, so starting it later delays the whole verification one-for-one. The parent's remaining items β€” demo video, client IDs, scope justifications, submission β€” are not externally gated and can advance in parallel.

What it ignores: issue age and filing order (both are months old and would tie), priority labels as a tie-break (both are p1), checklist completion (ticked boxes say nothing about what remains blocked), and cost/ease (neither reorders a customer-facing deadline).

What the audit found β€” generated

⚠︎ The prose above cites 3 issues that are no longer open

#3060, #1248, #3418. A closed issue named as current work is the drift this section exists to surface β€” re-derive the prose, or move the reference into History.

Evidence below is generated from the read-only audit of 2026-08-29 and reconciled against the open issues at build time. Every claim cites the issue it came from. Nothing here is a decision β€” rulings stay in the prose above, which is written by hand.

#1241 chore: Complete Google OAuth App Verification for Gmail (Restricted) + Calendar…prodhigh confidence

Prod `public.tenant_connector_secrets` (Supabase MCP execute_sql, 2026-08-29): 3 rows connector_type='mizo_gmail' (first 2026-07-04, last updated 2026-08-29), 1 'maildir'. Nothing in the repo records a completed verification: `git ls-tree -r --name-only origin/main | grep -iE 'casa|asvs|oauth.?verif|zap|fluid'` returns ZERO files over the whole origin/main tree. The only shipped artifact describing the state is docs/status/connector-truth.html:10 and :48 on origin/main, which states the app 'sits in Google's testing mode' and 'every Gmail source dies within 7 days of consent', and :74 records 'No merged PR and no post-mortem yet'. Also docs/reference/mizo-infra-checklist.md:326 and :346 on origin/main instruct NOT to enable Gmail/Calendar APIs on the Mizo GCP project and NOT to add gmail.readonly/calendar.readonly, because doing so triggers CASA β€” i.e. Phase B of the issue body ('Enable the Gmail API and Google Calendar API' marked [x]) contradicts the checked-in infra checklist. Phase C items are all unchecked and no evidence of a Google submission exists in-repo.

#2875 fix(source): Exchange and Slack live drains write one ingestion_runs row per messagecodehigh confidence

origin/main backend/app/services/connectors/exchange_handler.py:616 β€” `await self._orchestrator.ingest(messages=messages, source_name=source.name, source_type="exchange", ingestion_mode=INCREMENTAL)` with NO run_id, called from `_drain_one_full_email` inside the per-event loop `for event in events:` (~line 478). Same shape at slack_live_handler.py:557 β€” `ingest(messages=[message], ...)`, no run_id, inside `for event in events:` (~line 311). `git grep -n run_id` over both files returns only exchange_handler lines 153/179/236 (an unrelated logging param) and NOTHING in slack_live_handler. Prod (execute_sql, ingestion_runs, 14 days, pipeline_type='ingestion', grouped by source_type): maildir 9,232 runs / 33,711 parsed; whatsapp_live 1,073 / 4,737; gmail 479 / 830; exchange and slack rows = ZERO. The issue's premise holds exactly: latent defect, no production rows.

#3658 sweep reconciliation residuals: no-join-key and rail-crossing armed rows still miss a…codehigh confidence

origin/main:backend/app/services/connectors/connector_deletion_sweep_service.py:188-245 β€” `_whatsapp_live_again` returns False immediately when `instance_id is None`, and its SQL matches a live row only by (a) same instance id, (b) armed.phone_number = live.phone_number, (c) armed.owner_jid = live.owner_jid, or (d) armed.owner_user_id = live.owner_user_id gated on one side being identity-blind. An armed row with phone_number NULL, owner_jid NULL and owner_user_id NULL satisfies no leg (residual 1), and an owner_user_id mismatch across the admin/end-user rails satisfies no leg (residual 2) β€” exact owner equality is required. The docstring at lines 216-225 states both residuals verbatim as current semantics and cites #3658. Residual 1 is pinned by origin/main:backend/app/slices/source/tests/integration/test_sweep_reconnect_reconciliation.py:376 `test_null_identity_null_owner_armed_row_still_purges`; residual 2 has no test (git grep over the file found none). Reachability of the defective path: `_connector_live_again` (line 171) is called from the sweep driven by `_connector_deletion_sweep_tick` in origin/main:backend/app/worker.py:121-128, plus the manual ops endpoint backend/app/api/endpoints/connectors/internal_deletions.py:33 β€” the pg_cron job `run-due-connector-deletions` was unscheduled by supabase/migrations/20260827192813_unschedule_run_due_connector_deletions.sql and the drain loop is the driver. Blast radius measured in prod (evidence_class=prod, schema-qualified, run 2026-08-29): public.scheduled_deletions = 0 rows total, 0 pending, 0 whatsapp; public.whatsapp_instances = 5 rows (note: the 6-row figure in the session facts is stale), of which 0 have phone_number IS NULL AND owner_jid IS NULL, 0 have owner_user_id IS NULL, 0 have all three NULL. So both residuals are live in code and reachable, with zero current production instances able to trip them.

Open questions β€” what the audit could not settle
  • #1241 Whether the OAuth consent screen has actually been submitted to Google, and the current publishing status of the GCP project, are outside the repo and the DB. Close with: `gcloud alpha iap oauth-brands list --project=<mizo-project>` plus the Google Cloud Console 'OAuth consent screen' page (Publishing status field) β€” or `curl -s 'https://accounts.google.com/o/oauth2/v2/auth?client_id=<id>&scope=https://www.googleapis.com/auth/gmail.readonly&response_type=code&redirect_uri=<uri>'` and check whether the unverified-app interstitial renders.
  • #2875 Whether an Exchange/Slack tenant onboarding is imminent (that flips priority). Closing command: `select tenant_id, connector_type, count(*) from public.tenant_connector_secrets group by 1,2` β€” today it returns mizo_gmail x3 and maildir x1 only, per the schema census, so no Exchange/Slack credential exists.
  • #3658 Whether the admin rail can actually mint a whatsapp_instances row with owner_user_id set to the admin's user id (residual 2's premise) is not closed here β€” I read the sweep's join, not the arm-time writers. Close it with: `git grep -n "INSERT INTO whatsapp_instances\|whatsapp_instances" origin/main -- backend/app | grep -v tests` and read every writer that sets owner_user_id, then `mcp__supabase__execute_sql: select owner_user_id, count(*) from public.whatsapp_instances group by 1` cross-joined against the admin user ids in public.auth_profiles to see whether any prod row is admin-owned. Also unclosed: whether the worker drain loop's connector_deletion_sweep tick is actually executing in the deployed fleet (the pg_cron driver was removed and no prod cron row remains) β€” close it with `python3 .claude/tools/flylogs/flylogs.py mawja-api --since 24h --grep connector_deletion_sweep --context 5`.
Not yet assessed β€” filed after the audit
  • #3792 messages.message_hash includes the DERIVED sender name, so any change to sender…
  • #3794 [L1-g1-w4] prod: the google_calendar live sync is non-functional β€” no connector state row…
  • #3805 [L1-g1-w4] prod: the WhatsApp live-poll fleet is stale on four tenants β€” newest messages…

7 assessed issues closed since the audit and are not shown β€” a closed issue's evidence is history, and printing it as current is how a page starts lying.

Standing rulings

None for this track β€” track-order.json's decided list is empty. The adjacent ruling that connector-liveness (#3060) stays p1 is decided on the MEASUREMENT track, not here, so it is cross-referenced above rather than restated.

History

Filed dates Β· no merges yet

Both issues are months old: the verification parent (#1241) filed 2026-05-20, the CASA sub-issue (#1248) filed the next day, 2026-05-21. No merged PR and no post-mortem yet; the only measured state is the production census the measurement track holds β€” 5 of 9 Gmail connectors dead (cross-referenced to #3060).