For Agents

Living index of themes for the Levandor CRM project (React + Supabase). Each H2 is a topic; bullets are wikilinks to related notes. Updated by obsidian-documenter when documenting work. Read by historian at bootstrap. Topics kept alphabetical.

Auth & Security

  • security — CF Access ZTNA + Supabase anon model; cf-access.ts JWT parsing; cf-access-auth Edge Function
  • incident-2026-05-10-sw-cf-access-lockout — P0: Workbox NavigationRoute blocked CF Access cookie-refresh dance, locking all users out

Brand & Design System

  • levandor-brand — Copper / warm-black tokens, typography, mobile vs desktop application rules

Dashboard & Forecasting

  • dashboard-forecastdashboard_summary_v1 RPC, the avg_hrs-per-project gotcha, mobile/desktop hero alignment

DRY & Refactoring

  • dry-refactor-2026-05-11 — The 2026-05-11 Tier 0+1+2 simplification pass (9 commits, d035235..ccf6f57, ≈5k net lines removed). Catalogues the new shared abstractions future sessions should reuse rather than re-roll (web/src/components/crm/: PageHeader/ExportCsvButton/CreateButton/ErrorBanner/CardSkeleton+SkeletonLines/ResourceTable/TaskChips/getTaskCardModel/CreateEntityDialog as the standard form-dialog shell; web/src/lib/hooks/: createTableQueryWithArgs/createMaybeSingleQuery*/createRpcMutation/makeStorageAttachmentHooks/useUrlListState/useCreateTaskFromBlock/useBudgetTransactionLookups/useProjectFiles/supabase-builders.ts; web/src/lib/: on-call-rates.ts rate helpers, foreign-resource.ts, colors.ts typed status-color fns; userspace/src/lib/project-status.ts), the deliberately-deferred items (edge-function _shared/ consolidation, the useDayPlanSuggestions/useOnCallMonthly UTC-drift bugs, packages/tender-pipeline, Tier 3 god-components, billingo 1.27-VAT bug, non-atomic useCreateInvoice/useCreateBudgetTransfer, mobile/lib/formatTime.ts TZ, possibly-dead sync-outbound), and three behavior changes (TaskCard priority pill → @/lib/colors palette unification with the Kanban dots; TaskCard empty-AvatarFallback bug fixed; InventoryTab name-match → FK-id-match). CAVEAT: deleted the legacy notify EF from the repo — confirm no Supabase-dashboard Database Webhook still points at it before removing server-side.

Day Planner & Standup

  • day-planner — Desktop day planner architecture (current implementation)
  • mobile-day-rituals — Phone-native day-rituals (Morning, Standup notes, Lunch, Evening reflection)
  • evening-checklist — Manifestation tracker v1 (mobile sheet + desktop card); also documents the timed evening-checklist nudge
  • evening-checklist-day-boundary-fix-2026-05-11 — Bug + data migration: a post-midnight evening checklist filed onto the new calendar day’s standup row. Fix (commit 1bf861a): new eveningStandupDate() in web/src/lib/date-utils.ts — an “evening-standup day D” spans 15:00 on D → 14:59 on D+1 (uses local getHours()); MobilePlan’s evening checklist + reflection sheets now target that row (eveningStandupId + on-demand useEnsureStandup), gated on its morning_completed_at/evening_completed_at; morning/lunch/day-plan/standup-notes unchanged (calendar-date). MobilePlan.date/eveningDate un-frozen — recompute on visibilitychange. Desktop /standup not changed (its UTC toISODateString accidentally cancels the bug for late CEST nights). Data migration moved the 4 mis-filed datapoints back. Open: not deployed yet — needs pnpm --filter web deploy. Gotcha: daily-ritual “today” must be (a) local not UTC, (b) not mount-frozen in a PWA, (c) for evening rituals, post-midnight = previous day (15:00 cutoff).
  • mobile-autosave — Debounced field-level autosave for the standup sheets
  • scheduled-reminders — Phase 3 sync_day_plan_block_reminder() trigger (split into trg_dpb_reminder_iud + trg_dpb_reminder_upd) fires “Up next” reminders 5 min before a PLANNED block’s start_time — which is a timestamptz (absolute instant), so fire_at = start_time - interval '5 min', no standup.date/tz math; reminder owner is NEW.person. Live & smoke-tested. Also: the evening-checklist reminder (process_evening_checklist_reminders pg_cron job, shipped 2026-05-11) — a 21:00/22:00-Budapest nudge to finish the Evening Checklist if < X of today’s EVENING_CHECKLIST datapoints have a non-empty value (done = filled-in answers, not evening_completed_at); CUSTOM_REMINDER notification with entity_type='standup', tap → /standup (auto-creates today’s standup).

External Integrations

  • integrations — Index of CRM external integrations (Billingo, GitHub/GitLab, Jira, Linear, Apple Reminders, Tailscale, Cloudflare, Yahoo Finance, Polymarket)
  • polymarket-fetch — External Rust pipeline writes pm_* tables in the mgmt Supabase project; CRM consumes via createTableQuery once database.types.ts is regenerated

Incidents & Outages

Mobile / PWA

Mobile Notifications UI

  • mobile-notifications-uiMobileNotificationButton + MobileNotificationsSheet (vaul Drawer, 85vh); MobileHeader rightSlot fallback pattern; PushDiscoveryNudge extracted DRY; bare-render test mock pattern

Notifications

  • mobile-notifications-ui — Mobile in-app list (bell + sheet); reuses NotificationItem and TAB_FILTERS from desktop; extracts PushDiscoveryNudge for sharing; handleClick got a standup/standup branch for the evening-checklist nudge
  • push-notifications — Supabase push notifications research (transports, infra, gotchas, open decisions)
  • scheduled-remindersreminder table + process_due_reminders() pg_cron job + CUSTOM_REMINDER type; reminders bypass emit_notification to dodge the event_fanout push-silence; snooze (P4) re-emits via create_reminder. Also the evening-checklist reminder — a second pg_cron SQL fn (process_evening_checklist_reminders, cron '0 * * * *', self-gating to hours 21/22 Budapest) that direct-INSERTs a CUSTOM_REMINDER notification (entity_type='standup', data.source='evening_checklist', origin='reminder', dedup source_id includes the hour) when the Evening Checklist is incomplete; does NOT use the reminder table; send-push EF v7 + deriveUrl case 'standup'
  • evening-checklist — Documents the evening-checklist reminder from the ritual side (what “done”/N/X means, why it’s filled-in-answers not evening_completed_at)
  • incident-2026-05-10-sw-cf-access-lockout — Push-notifications SW commit caused the auth outage; hard rule: no NavigationRoute in any future push SW

PWA Updates

  • pwa-update-promptregisterType: 'prompt' + MobileUpdateBanner + useUpdatePrompt hook with auto-reshow on newer-update transition; vitest alias gotcha for virtual:pwa-register/react

Scheduled Reminders

  • scheduled-reminders — Phases 1-4, all shipped & applied to remote: ad-hoc one-offs (reminder table, create_reminder RPC, process_due_reminders() pg_cron), recurring rules (compute_*_fire_at SQL helpers, 6-tab dialog), day-plan-block “Up next” trigger (split trg_dpb_reminder_iud/trg_dpb_reminder_upd; start_time is timestamptz), and notification snooze (SnoozePopover, forwards entity_type/entity_id). P3 + P4 each got a 4-perspective review (DB/security/frontend/ops) — fixes shipped (ON CONFLICT re-arm guard, trigger split, reminder_update_guard BEFORE UPDATE trigger restricting authenticated-role direct UPDATEs to status-only). Plus the evening-checklist reminder (shipped 2026-05-11; process_evening_checklist_reminders + process-evening-checklist-reminders cron job) — same pattern, separate feature, does NOT touch the reminder table; revised after a 3-perspective DB/security/ops review (added the in-app-pref gate, count(DISTINCT), RAISE LOGs, 'standup' click-through). Includes the Postgres gotcha cluster (STABLE vs IMMUTABLE, %ROWTYPE cursors, day+timetimestamp, timestamptztime & TS types both as string, WHEN-clause triggers, MCP migration-version drift, UTC '0 * * * *' cron + in-fn AT TIME ZONE hour-gate for local-wall-clock jobs, count(DISTINCT) not count(*) for “how many items have an answer”, self-nudging cron jobs re-check every tick + put a discriminator in the dedup key)
  • evening-checklist — Evening-checklist reminder documented from the ritual side

Service Worker & PWA Build

  • pwa-update-prompt — Removed install→skipWaiting() (was a recovery bridge from cd8394f); kept activate→clients.claim() + SKIP_WAITING message handler for user-initiated updates
  • incident-2026-05-10-sw-cf-access-lockoutinjectManifest + web/src/sw.ts; what NOT to register; recovery via skipWaiting + clients.claim + in-app reset button

Tech Debt & Audit

  • tech-debt — Updated 2026-05-09 with mobile audit follow-ups (UTC drift in 2 more hooks, mount-frozen date selectors, useUpdatePrompt cleanup, stub aggregations, per-user prompts v2, forecast formula cap); 2026-05-11 DRY pass deleted the “No Query Key Constants” debt (factories now centralize keys) and shrank “Direct Supabase Calls in Components” (DayPlanner/KanbanBoard/TaskDetailSheet/CSVImportWizard/TaskCard moved off — AddLoanee/AddConfiscation/CommandPalette still bypass), and added a “Deferred from the 2026-05-11 DRY Pass” section; 2026-05-11 the “Mount-frozen date selectors” item went partly fixedMobilePlan.date/eveningDate now recompute on visibilitychange (MobileHours.active still freezes; desktop standup page still uses UTC toISODateString)
  • evening-checklist-day-boundary-fix-2026-05-11 — The fix that partly closed the mount-frozen-date-selectors debt; also surfaced the “daily-ritual today must be local, not mount-frozen, and evening = post-midnight-is-previous-day” gotcha
  • dry-refactor-2026-05-11 — The DRY pass’s own catalogue of what it deferred (edge-function _shared/, the 2 UTC-drift hooks, packages/tender-pipeline, Tier 3 god-components) — the canonical record of why each was skipped
  • incident-2026-05-10-sw-cf-access-lockout — Stale doc flagged: web/CLAUDE.md and security both still describe the anon-only Supabase model; JWT exchange via cf-access-auth was reintroduced after 769ba5c
  • scheduled-reminders — Open follow-ups (post P3/P4 review): full reminder_update_own RLS rewrite (now partially backstopped by the reminder_update_guard trigger), multi-tenant day_plan_block/standup RLS tightening (lower priority — trigger uses NEW.person), no retention job for reminder/net._http_response/cron.job_run_details, thin test coverage, configurable lead-time, custom snooze picker