Calendar cert/invoice check marks (owner-only)

Added: 2026-08-10 (entirely-api a8b8931, entirely-portal de5f04f; owner-gate + legend c5ff538 / 190414f)

Visibility: up_owner only (Dustin + Jacob), gated in BOTH layers — the backend returns cert_emailed=False (and skips the lookup) for every other role, and SubChecks returns null for non-owners. A legend row under the branch legend (gold ✓ cert emailed / green ✓ invoice sent) is gated the same way. Note: a future third up_owner account would inherit visibility.

Month, week, and day calendar views (plus the month-view day panel) show sub-workflow completion at the head of each project line, before the branch color dot:

  • Gold ✓ (#c9a227) — certification has been emailed
  • Green ✓ (#5a8a5a) — invoice Sent
  • Black ✓ (var(--ink)) — invoice Paid (replaces the green check; invoiceCheck() two-state resolver, portal ffd1bdd)

Nothing renders until the sub-workflow completes. Colors match SubTrackIndicator (cert gold / invoice green).

Data flow

  • Invoice: already in every event’s description blob (Invoice Status: Sent|Paid), parsed client-side. No backend change.
  • Cert: new cert_emailed + cert_emailed_at fields on GET /portal/calendar/events, computed by _cert_emailed_map() in routes/intake.py. Merges three delivery sources, newest first:
    1. email tracking DB (/data/portal-auth/email_tracking.db) — one batch query via email_tracking_service.get_cert_delivery_info
    2. portal state file (cert_invoice_state/{record_id}.json, cert_state == "sent")
    3. CERTS/sent_log.json append-only delivery log

Backfill

No data migration: sources 2 and 3 predate the tracking DB, so historical sends surface automatically. At ship time: 257 projects show the gold check (246 from the tracking DB, 11 only from file sources). Certs delivered outside the portal pipeline (legacy blkhse-era manual Gmail sends) have no delivery record anywhere and stay unchecked — flipping those would require inference from project status, deliberately not done.

Frontend

portal-v2/src/pages/CalendarPage.tsxSubChecks component + invoiceSent() helper; render sites: month cell rows, day panel rows, WeekGridView, DayGridView. CalendarEvent interface gained cert_emailed/cert_emailed_at.