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-documenterwhen documenting work. Read byhistorianat bootstrap. Topics kept alphabetical.
Auth & Security
- infra-gotchas — GOTCHA: the root
/CLAUDE.mdandsupabase/CLAUDE.mdare WRONG — they say “Clerk handles authentication” for the admin app. It’s Cloudflare ZTNA: CF Zero Trust →CF_Authorizationcookie →cf-access-authedge fn mints a Supabase JWT (sub = person.id) → RLS viaauth.uid()/get_my_person_id(). RLS tables areauthenticated-roleUSING(true)(auth enforced upstream) +service_role FOR ALL. Clerk only inuserspace/.web/CLAUDE.mdis the correct source. - security — CF Access ZTNA + Supabase anon model;
cf-access.tsJWT parsing; cf-access-auth Edge Function - incident-2026-05-10-sw-cf-access-lockout — P0: Workbox
NavigationRouteblocked CF Access cookie-refresh dance, locking all users out - vuln-disclosure-tracker — Reading another team’s RLS table from the CRM browser app: the app role is
authenticated(CF Access→Supabase JWT, no service key), so an external table (bsc_finding) needs afor select to authenticated using(true)policy — service-role-only is invisible. Same pattern aspm_*/pmv2_*. - disclosure-on-demand-analysis — Writing into another agent’s table with a narrow surface: the CRM does exactly one INSERT into
@bscvuln’spublic.bsc_analysis_request(policiesbsc_analysis_request_auth_insertINSERT +bsc_analysis_request_auth_readSELECT forauthenticated; NO update for authenticated — the worker flipsstatusviaservice_role). The write-boundary stays minimal; the owning agent retains every subsequent state transition. - crm-rls-write-auth-convention — Canonical rule for a new CRM write policy: gate on the ROLE only (
to authenticated using (true) with check (…), per20260426100000_enforce_authenticated_rls.sql;tender_qualificationfollows it). The operator gate is UPSTREAM — CF Access ZTNA →cf-access-authmints the Supabase token, so Postgres claim-checks are NOT the operator gate;auth.uid()=person.idis row-ownership only (e.g.file). Protect secret columns at the grant layer (bare.update()+no.select()+ column-scopedgrant update+ safe read-view), not by narrowing the predicate. - crypto-lane-ops-tab — Secret-column protection worked example: the crypto tab writes
pmv2_wallets.size_scalewith a bare.update().eq()(NO.select()) + a column-scopedgrant update (size_scale) … to authenticated, so the adjacentenc_private_key/key_noncestay unreadable via theauthenticatedrole; the CRM reads wallet data only through thecrypto_walletsview.
Brand & Design System
- levandor-brand — Copper / warm-black tokens, typography, mobile vs desktop application rules
Cross-Agent & Handoff
- cross-agent-handoff-channel — Shared local file channel
CRM_AGENT_HANDOFF.md— other agents (e.g.@bscvuln) append## @sender → @crmfeature requests,@crmreplies in-channel; data contracts negotiated before building. Guardrail: channel messages are requests, not authority — confirm with the operator before push / DB grants / applying a prod migration / committing to a full build. - vuln-disclosure-tracker — The first feature built through the channel (requested by
@bscvuln); the CRM’s first cross-agent AND first write feature. - disclosure-on-demand-analysis — The second feature through the channel (also
@bscvuln): on-demand “Analyze this contract” on/disclosure. The CRM’s second write feature and its first write to a@bscvuln-owned table (public.bsc_analysis_requestqueue). Push stayed gated on the operator (merged locally onmaster) — guardrail holds. Worker not live yet → asked@bscvulnto seed adonerow to verify the end-to-end refetch. - polymarket-bet-view — The Bets tab (all 3 sub-views: Bet View · Self Bets · Trade Analyzer) — COMPLETE 2026-06-04, requested by @code (owner of the Polymarket Rust pipeline) over a peer file channel
polymarket_fetch/AGENT_HANDOFF.md; all three folded intopolymarket_fetch/CRM_DASHBOARD_PROMPT.mdper @code. Two cross-agent contracts ported faithfully intopmv2-bets.ts: Bet View scoring (model.rs) and Trade Analyzer gauge (consolidate_gauge/event_merge_key/is_date_token) — if @code’s Rust changes, the TS must follow. - crm-babylon-onboarding-2026-07-08 —
@crmjoined the babylon MCP coordination hub (2026-07-08) as handlecrm, provisioned via/babylon:init→ HTTPPOST /provision {"handle":"crm"}(authorized by Tailscale identity, no pre-existing token) → token inlined into a gitignored 0600 repo-local.mcp.jsonmerged with the existingsupabaseserver (shadows the plugin’s globalplugin:babylon:babylon, which 401s w/o aBABYLON_TOKENenv var — by design). Supersedes the file-basedCRM_AGENT_HANDOFF.md/AGENT_HANDOFF.mdscratchpads (quiet since ~2026-06-04). Gotcha: babylon tools don’t hot-load — need a Claude Code restart; the firstcatch_upsurfaces any messages to@crmsince the fleet’s mid-June cutover. - crypto-lane-ops-tab —
@crm’s first cross-agent build coordinated over babylon (channelpmv2-ui, task #1085), handed off by @crypto — not the old file channels. Exactly the deliverable @crypto scoped (“one more CRM tab, @crm writes the React” from a build-prompt +publicSQL views). Reads @crypto’scrypto_lane_health/crypto_walletsviews (migration0009_crypto_ui_views.sql), writespmv2_wallets.size_scale(a @positionmanager-owned column).
Dashboard & Forecasting
- dashboard-forecast —
dashboard_summary_v1RPC, 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/CreateEntityDialogas 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.tsrate helpers,foreign-resource.ts,colors.tstyped status-color fns;userspace/src/lib/project-status.ts), the deliberately-deferred items (edge-function_shared/consolidation, theuseDayPlanSuggestions/useOnCallMonthlyUTC-drift bugs,packages/tender-pipeline, Tier 3 god-components,billingo1.27-VAT bug, non-atomicuseCreateInvoice/useCreateBudgetTransfer,mobile/lib/formatTime.tsTZ, possibly-deadsync-outbound), and three behavior changes (TaskCard priority pill →@/lib/colorspalette unification with the Kanban dots; TaskCard empty-AvatarFallbackbug fixed;InventoryTabname-match → FK-id-match). CAVEAT: deleted the legacynotifyEF 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
standuprow. Fix (commit1bf861a): neweveningStandupDate()inweb/src/lib/date-utils.ts— an “evening-standup day D” spans 15:00 on D → 14:59 on D+1 (uses localgetHours());MobilePlan’s evening checklist + reflection sheets now target that row (eveningStandupId+ on-demanduseEnsureStandup), gated on itsmorning_completed_at/evening_completed_at; morning/lunch/day-plan/standup-notes unchanged (calendar-date).MobilePlan.date/eveningDateun-frozen — recompute onvisibilitychange. Desktop/standupnot changed (its UTCtoISODateStringaccidentally cancels the bug for late CEST nights). Data migration moved the 4 mis-filed datapoints back. Open: not deployed yet — needspnpm --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 intotrg_dpb_reminder_iud+trg_dpb_reminder_upd) fires “Up next” reminders 5 min before a PLANNED block’sstart_time— which is atimestamptz(absolute instant), sofire_at = start_time - interval '5 min', nostandup.date/tz math; reminder owner isNEW.person. Live & smoke-tested. Also: the evening-checklist reminder (process_evening_checklist_reminderspg_cron job, shipped 2026-05-11) — a 21:00/22:00-Budapest nudge to finish the Evening Checklist if< Xof today’sEVENING_CHECKLISTdatapoints have a non-empty value (done = filled-in answers, notevening_completed_at);CUSTOM_REMINDERnotification withentity_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)
- infra-gotchas — DB plumbing ground-truth for anyone adding a migration or regenerating types: live migrations are timestamped in
web/supabase/migrations/(applied via Supabase MCPapply_migration/supabase db push); the numbered00N_files insupabase/migrations/are a STALE copy (noconfig.toml, no CI applies them). Thepmv2_*/pm_*/bsc_*as nevercasts seen elsewhere are because both generated type files (types.tsANDdatabase.types.ts) must be regenerated, butgen-typesonly writesdatabase.types.ts—types.ts(the onedb-mappings.ts/barrel actually import) drifts unless hand-synced. - polymarket-fetch — External Rust pipeline writes
pm_*tables in themgmtSupabase project; CRM consumes viacreateTableQueryoncedatabase.types.tsis regenerated - paper-trading-dashboard — Read-only Paper Trading performance dashboard at
/polymarket(replaced the old consensus/copy-score section). Correction: thepm_*tables live in the CRM’s single Supabase (the project internally named “mgmt”,mkofmdtdldxgmmolxxhc=VITE_SUPABASE_URL) — there is NO separate mgmt client; useuseSupabase(). - polymarket-bet-view — The Bets tab on
/polymarket(all 3 sub-views complete) consumes @code’spmv2_*tables (pmv2_positions/pmv2_event_category/pmv2_traders/pmv2_leaderboard_entries) and thepm_*self-tracking tables (pm_self_wallets/pm_runs/pm_positions) from the single CRM Supabase; replicates @code’smodel.rsconviction/bet_score scoring AND theconsolidate_gauge/event_merge_key/is_date_tokengauge math exactly. - vuln-disclosure-tracker —
/disclosurereads an external agent’sbsc_findingtable (the BSC vuln scanner at/Users/levander/coding/bsc-vuln-scanner) — first cross-agent integration, requested over cross-agent-handoff-channel; needed afor select to authenticatedRLS policy + a stable backendfinding_keyto survive the table’s daily reload. - disclosure-on-demand-analysis —
/disclosurenow also writes into@bscvuln’spublic.bsc_analysis_requestqueue (one INSERT, auth-gated) to trigger an on-demand exploitability analysis of any BSC contract; a VM worker drains the queue (Slither-judge or open-ended Claude audit) and upserts the verdict intobsc_contract_analysis(already read by the verdict panel). Watch hookuseAnalysisRequestpolls every 8s while pending/running, caps at 15-min staleness, auto-invalidates the contract-analysis query ondone.
Incidents & Outages
- incident-2026-05-10-sw-cf-access-lockout — 2026-05-10 P0: custom SW
NavigationRoute+ CF Access ZTNA = global lockout; fixed incd8394f
Mobile / PWA
- mobile-native-feel — As-built mobile architecture (
web/src/mobile/) — Phase 5-10 shipped + audit + brand pass - mobile-day-rituals — Phone-native ritual sheets
- mobile-autosave — Autosave hook + status pills
- evening-checklist — Manifestation tracker (mobile + desktop)
- evening-checklist-day-boundary-fix-2026-05-11 —
MobilePlanwas mount-freezing the standup date (useMemo(…, [])) and using the raw local calendar date for the evening checklist → a post-midnight session mis-filed onto the next day. Fixed:eveningStandupDate()(15:00 cutoff),visibilitychange-driven recompute, on-demanduseEnsureStandupfor the evening day. Partially closes the Mount-frozen date selectors item (MobileHours.activestill freezes). - mobile-notifications-ui — Bell + bottom drawer mirror of desktop NotificationBell + NotificationPopover
- pwa-update-prompt — Mobile bottom banner for new-version prompt (replaces silent autoUpdate + invisible toast)
- push-notifications — Web Push research (Supabase primitives + iOS PWA gotchas)
- incident-2026-05-10-sw-cf-access-lockout — Why ZTNA +
NavigationRouteis a permanent trap; SW design constraints for this codebase
Mobile Notifications UI
- mobile-notifications-ui —
MobileNotificationButton+MobileNotificationsSheet(vaul Drawer, 85vh);MobileHeaderrightSlot fallback pattern;PushDiscoveryNudgeextracted DRY; bare-render test mock pattern
Notifications
- mobile-notifications-ui — Mobile in-app list (bell + sheet); reuses
NotificationItemandTAB_FILTERSfrom desktop; extractsPushDiscoveryNudgefor sharing;handleClickgot astandup→/standupbranch for the evening-checklist nudge - push-notifications — Supabase push notifications research (transports, infra, gotchas, open decisions)
- scheduled-reminders —
remindertable +process_due_reminders()pg_cron job +CUSTOM_REMINDERtype; reminders bypassemit_notificationto dodge theevent_fanoutpush-silence; snooze (P4) re-emits viacreate_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 aCUSTOM_REMINDERnotification (entity_type='standup',data.source='evening_checklist',origin='reminder', dedupsource_idincludes the hour) when the Evening Checklist is incomplete; does NOT use theremindertable;send-pushEF v7 +deriveUrlcase 'standup' - evening-checklist — Documents the evening-checklist reminder from the ritual side (what “done”/
N/Xmeans, why it’s filled-in-answers notevening_completed_at) - incident-2026-05-10-sw-cf-access-lockout — Push-notifications SW commit caused the auth outage; hard rule: no
NavigationRoutein any future push SW
Polymarket / Paper Trading
- polymarket-bet-view — Bets tab — COMPLETE 2026-06-04 (4th tab on
/polymarket, sub-nav Bet View | Self Bets | Trade Analyzer; merged local, push gated on operator; ~40 unit tests; no migration, read-only). Cross-agent feature requested by @code overpolymarket_fetch/AGENT_HANDOFF.md; all three folded intopolymarket_fetch/CRM_DASHBOARD_PROMPT.md. All share shellBetsDashboard.tsx+ domainweb/src/lib/pmv2-bets.ts. (1) Bet View — sortable/filterable table of the cohort’s top conviction-weighted bets, grouped by(condition_id, outcome_index), ranked bybet_score = Σ(score × conviction). Faithful port of @code’smodel.rs: conviction =clamp((initial_value−avg_lost)/(avg_won−avg_lost),0,1)(0 if either avg null oravg_won≤avg_lost); bands VHIGH≥0.06/HIGH≥0.035/NEUTRAL≥0.02/LOW≥0.008/VLOW; cohort_entry stake-weighted; rank =COALESCE(rank_month,rank_week,rank_day); dust filterredeemable=false AND initial_value>1. Domainconviction/bandOf/cohortEntry/aggregateCohortBets/BAND_TONE; hookuseCohortBets(latest completed run → traders+names+positions+category); UIBetViewTable/BandBadge. (2) Self Bets — operator’s OWN tracked wallets (pm_self_wallets, Balu/Bandi) → latestpm_runs.selector='self_snapshot'per wallet (maxid, wallet inselector_params->>'wallet') →pm_positions(held; dust KEPT — our own book) → each annotated with cohort backing (band + # cohort traders) where(condition_id, outcome_index)matches a Bet-View cohort bet (reusesuseCohortBets); grouped per wallet; empty latest snapshot → “No open positions”, NO stale fallback. DomainlatestSelfRunIds/parseSelfPosition/attachCohortBacking/cohortBetKey; hookuseSelfBets; UISelfBetsView. (3) Trade Analyzer — paste a Polymarket URL/slug →normalizeSlug(sanitize to[a-z0-9-], which also closed a PostgREST.or()injection vector) → matchpmv2_positionsbyevent_slugORslug(held non-dust) → join latestpmv2_traders→consolidateGauge→ per-outcome gauge sides. Faithful port of @code’sconsolidate_gauge/event_merge_key/is_date_token: lowercase slug, split-, drop date tokens (month full-name OR 3-letter prefix; year 2024–2031; all-digit;\d+[-/]\d+), rejoin; group by(event_merge_key, outcome_index); per-sidecohort_score=Σ(score×conviction); sort desc; capMAX_GAUGE_SIDES=8(overflowhidden_sides“+N more”);n_markets=distinctcondition_id; label=modalevent_slug. DomainisDateToken/eventMergeKey/normalizeSlug/consolidateGauge; hookuseAnalyzerEvent; UITradeAnalyzer. Cross-cutting gotchas:pmv2_positionsis NOT run-scoped (live snapshot keyed byproxy_wallet);pmv2_tradershas no names (usebuildWalletNameMapoverpmv2_leaderboard_entries);pmv2_event_categoryjoins onevent_slugnotevent_id;StatusBadgetakestone+label(tones exactlyamber|emerald|rose|gray|sky); allpmv2_*/pm_*tables absent fromdatabase.types.ts→as never; PostgREST numeric→string→alwaysnum/numOrNull. - trader-scout-dashboard — Read-only runs-history (master) + scored-trader-leaderboard (detail) dashboard, now the default
/polymarketlanding tab (commite315dba, push pending; branchfeat/pmv2-scout). Scrapes Polymarket public leaderboards (DAY/WEEK/MONTH/ALL), intersects wallets on ≥min_intervalsboards, scores from resolved-market W/L, ranks./polymarketis now a 3-tab shell (default Scout;?tab=copy/?tab=longshotKEEP the prior dashboards — nothing deleted;resolveTabhelper). Layers: pureweb/src/lib/pmv2.ts(parsePmv2Run/parsePmv2Trader,buildWalletNameMap/mergeTraderNames,deriveRunSummary,formatScore/formatRunDuration/shortWallet/traderDisplayName,LOW_SAMPLE_N=10) →usePmv2Runs/usePmv2Traders/usePmv2RunWallets(30s poll) →scout/index.tsxmaster-detail (defaults to latest run) +RunsRail/RunParamsChips/ScoutSummary/LeaderboardTable. Data: 4pmv2_*tables (runs/traders/leaderboard_entries/market_resolutions) in the single CRM Supabase, all RLS*_auth_read→ read directly, NO migration;pmv2_tradersPK run_id+proxy_wallet, FK→runs CASCADE,scorenumeric nullable,n_resolvedcan be 0. Lessons:scoresemantics drift between runs (run-1wl_rate0–1 vs later SIGNED ≈−0.12..+0.07;formatScore×100renders signed % under a “Score” header — verify per-run vs live data, the spec snapshot went stale); new tables absent fromdatabase.types.ts→as neveron.from()/.eq()+ explicitsupabaseQuery<RawX[]>generic (precedentuseDayPlan/useTimesheetExport; follow-up = hand-add todatabase.types.ts+types.ts); PostgREST numeric→STRING (Number()); small-sample honesty (mute n<10, don’t re-rank — backend owns scoring); name enrichment via second cheap query (pmv2_tradershas no names → join client-side topmv2_leaderboard_entriesmap, ≈49/50, fallbackshortWallet); test-mock trap — hooks build.select().order().limit()eagerly so afrom:()=>({})mock throws → mocksupabaseQuery(the awaited seam) directly. - copy-lane-dashboard — Read-only forward-validation dashboard for the copy-trade lane, now a secondary
/polymarkettab (?tab=copy; was the default landing before Scout) (commitbf66a6f, push pending). Rows =pm_paper_trades WHERE lane='copy'(paper 10 mirrors of 5 tracked Polygon wallets' cheap-outcome buys). `/polymarket` is a segmented-control tab shell (default Copy, `?tab=longshot`); old Longshot dashboard demoted to `pages/polymarket/longshot/LongshotDashboard.tsx` (excludes `lane='copy'`). Pure domain `copy-trades.ts` (`deriveCopy*`, `tStat`, `COPY_FIX_CUTOFF_MS`/`CHASE_KILL=0.017`/`BACKTEST_ROI=0.248`) → `useCopyTrades`/`useCopyConfig`/`useCopyWallets` → page + GateTracker stepper/CopyScorecards/CopyCumulativeChart/CohortTable/ExecQualityStrip. New DB: `pm_copy_config`/`pm_copy_wallets` + 5 nullable cols on `pm_paper_trades` (types hand-added to BOTH `database.types.ts` AND `types.ts`). **Rules**: P&L/win/ROI RESOLVED-only (pending never 0); win strictly >0; gates G1 n≥50 / G2 n≥150 (t≥1.5∧ROI>0∧chase≤1.7¢) / G3 n≥300 (t≥2.0∧ROI≥+8%); exec-quality is a separate POST-FIX-filtered population (3 code regimes) from P&L;window_close_tsepoch SECONDS. Hardening lessons:sd===0guard fails on bit-identical floats (sd≈1.7e-17 → t-stat 1e16 false-pass) → relative-epsilon guard; null/0 size → ±Inf poisons t-stat NaN → filtersizedUsd>0 && isFinitefirst; bucketing needs out-of-range catch-all; formatters guard!isFinite→'—'; fee-% must keep resolved population consistent; UI-polish bar (real segmented control/gate stepper/distinct benchmark line/StatCard delta slot). - paper-trading-dashboard — The read-only Paper Trading performance dashboard that replaced the old “smart-money consensus + copy-score leaderboard” Polymarket section (~138 files deleted) at the same route
/polymarket. Three layers: pure framework-free domain moduleweb/src/lib/paper-trades.ts(parsePaperTradeNumber()-coerces numeric-as-string cols, exhaustivePRICE_BANDS/PHASESbuckets,derive*fns, 25 tests) → TanStack hookusePaperTrades(pm_paper_trades, server-side.gte('signal_wall',…), 30s refetch) → page + KpiCards/CumulativePnlChart/BreakdownTable×3/OpenPositionsTable/RecentBetsTable. Key facts: thepm_*tables live in the CRM’s single Supabase (project named “mgmt”,mkofmdtdldxgmmolxxhc=VITE_SUPABASE_URL— no separate client, useuseSupabase()); flat $10 stake;OPEN ⟺ up_won IS NULL ⟺ realized_pnl_usd IS NULL; lanes data-driven (longshotmain,momentumabsent); out-of-config pre-tuning bets shown in muted “off-config” buckets not hidden. Gotcha:react-hooks/purityflagsDate.now()in render →const [nowMs]=useState(()=>Date.now());tsc -bstricter than--noEmit. - side heatmap + hourly ROI strip (2026-05-30) — 2026-05-30 extension (commit
bea641a): a top-trader benchmark line on the cumulative P&L chart for wallet0x75cc…3ce1, normalized to our flat 2,233 vs our ~+10 normalization is in app code not SQL. New pure helpers inpaper-trades.ts(runningCumulative/benchmarkPnl/parseBenchmarkTrade/mergeSeries/coreStats/deriveByAssetSide/deriveHourly/formatUsdCompact), hookuseTopTraderBenchmark(.limit(5000)). Reusable lessons: normalize a benchmark to your stake before comparing on a shared axis; a second chart series needs its OWN loading/error/empty states (silent-feature-failure) + decouple the chart skeleton from it; unbounded Supabase queries silently truncate at PostgREST’s row cap →.limit()(usePaperTradesstill unbounded = latent); verify a new view’s RLS by simulating the role. - Bug fix — Self Bets hid resolved positions (2026-06-15) — Self Bets bug fix, commit
afd8740(LOCAL on master, NOT pushed/deployed). Page “didn’t get all positions” — hid resolved/redeemable bets (Bandi 9 open/17 resolved, Balu 3/10; resolved all $0 settled). Two-layer root cause: (1) the external pmv2self_snapshotscraper writes ZERO redeemable rows topm_positionsfor self wallets (0 redeemable self rows vs ~49.7K cohort redeemable rows in DB), so resolved self positions never existed in the data; (2)useSelfBets.tsalso.filter(p => !p.redeemable). Fix: re-pointuseSelfBetsto fetch live from Polymarket’s public data-api —GET data-api.polymarket.com/positions?user=<wallet>&sizeThreshold=0&limit=500perpm_self_walletswallet. Gotchas:sizeThreshold=0REQUIRED (default 1 hides small positions); data-api is CORS-open (access-control-allow-origin: *) → browser SPA calls direct, no proxy/edge fn; response is camelCase (proxyWallet/conditionId/outcomeIndex/redeemable/currentValue) → newparsePolymarketPositionmapper,SelfPositiongainedredeemable;SelfBetsViewnow partitions each wallet into Open table + collapsible<details>“Resolved (N)“. Per-wallet fetch failure →[]; wallet-list query error still throws to ErrorBanner. Consequence: page no longer readspm_runs/pm_positions/latestSelfRunIds→ the externalself_snapshotagent now produces snapshots nobody reads (retirement candidate, operator’s call;latestSelfRunIds/parseSelfPositionremain exported but unused). Mirror of self-sync-held-only-fix on the write side. Verifiedpnpm --filter web test pmv2-bets SelfBets→ 54 passed; build green. - integrations — Index entry for the Polymarket integration (its “separate mgmt project” framing is superseded by paper-trading-dashboard)
- polymarket-fetch — The external strategy pipeline that produces the paper-trade bets (dangling/planned note)
- crypto-lane-ops-tab — Crypto lane-ops tab (5th
/polymarkettab, commit73acb90, 2026-07-08; auto-deploys on push), cloned from Copy Lane.@crm’s first babylon-coordinated cross-agent build (channelpmv2-ui#1085, from @crypto). Reads twopublicviews from @crypto’s migration0009_crypto_ui_views.sql—crypto_lane_health(sorted bytoxicity_gap = signal_win − fill_win, the adverse-fill signal; severity-colored) +crypto_wallets— viauseCryptoLanes/useCryptoWallets(as never, numeric-string coercion, 30s poll). Writes exactly one columnpmv2_wallets.size_scaleviauseSetWalletScale(clamp[0.5,3], AlertDialog, RLS-deny → inline “not authorized”); authoritative because PM’s size_scale hot-reload (babylon #1065) is LIVE (deploy7cd41b0, re-reads per order). Pending: @deploy applies migration 0009 (read views); @positionmanager applies the RLS policy + column grant (write). 27/27 tests,tsc -b/vite buildgreen.
PWA Updates
- pwa-update-prompt —
registerType: 'prompt'+MobileUpdateBanner+useUpdatePrompthook with auto-reshow on newer-update transition; vitest alias gotcha forvirtual:pwa-register/react
Scheduled Reminders
- scheduled-reminders — Phases 1-4, all shipped & applied to remote: ad-hoc one-offs (
remindertable,create_reminderRPC,process_due_reminders()pg_cron), recurring rules (compute_*_fire_atSQL helpers, 6-tab dialog), day-plan-block “Up next” trigger (splittrg_dpb_reminder_iud/trg_dpb_reminder_upd;start_timeistimestamptz), and notification snooze (SnoozePopover, forwardsentity_type/entity_id). P3 + P4 each got a 4-perspective review (DB/security/frontend/ops) — fixes shipped (ON CONFLICTre-arm guard, trigger split,reminder_update_guardBEFORE UPDATE trigger restrictingauthenticated-role direct UPDATEs tostatus-only). Plus the evening-checklist reminder (shipped 2026-05-11;process_evening_checklist_reminders+process-evening-checklist-reminderscron job) — same pattern, separate feature, does NOT touch theremindertable; 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 (STABLEvsIMMUTABLE,%ROWTYPEcursors,day+time→timestamp,timestamptz≠time& TS types both asstring,WHEN-clause triggers, MCP migration-version drift, UTC'0 * * * *'cron + in-fnAT TIME ZONEhour-gate for local-wall-clock jobs,count(DISTINCT)notcount(*)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 fromcd8394f); keptactivate→clients.claim()+SKIP_WAITINGmessage handler for user-initiated updates - incident-2026-05-10-sw-cf-access-lockout —
injectManifest+web/src/sw.ts; what NOT to register; recovery viaskipWaiting+clients.claim+ in-app reset button
Tech Debt & Audit
- infra-gotchas — Three checked-in docs that MISLEAD (2026-06-09 validation): (1) two migration dirs, only
web/supabase/migrations/(timestamped) is live — numbered00N_insupabase/migrations/is stale; (2) generated DB types duplicated intypes.ts+database.types.ts, andgen-typesregenerates only the latter (the one most code does NOT import); (3) root/CLAUDE.md+supabase/CLAUDE.mdwrongly claim Clerk auth — it’s Cloudflare ZTNA. Trust order:web/CLAUDE.mdcorrect, root/supabase stale. - 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 fixed —
MobilePlan.date/eveningDatenow recompute onvisibilitychange(MobileHours.activestill freezes; desktop standup page still uses UTCtoISODateString) - 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.mdand security both still describe the anon-only Supabase model; JWT exchange viacf-access-authwas reintroduced after769ba5c - scheduled-reminders — Open follow-ups (post P3/P4 review): full
reminder_update_ownRLS rewrite (now partially backstopped by thereminder_update_guardtrigger), multi-tenantday_plan_block/standupRLS tightening (lower priority — trigger usesNEW.person), no retention job forreminder/net._http_response/cron.job_run_details, thin test coverage, configurable lead-time, custom snooze picker