The new Crypto tab on the CRM’s /polymarket surface — a lane-health monitor for the crypto short-term strategy plus the operator’s per-wallet bet-size control (pmv2_wallets.size_scale). Shipped to master as commit 73acb90 (2026-07-08, feat(polymarket): crypto lane-ops tab + per-wallet size_scale control) → auto-deploys to Cloudflare Pages on push. Cloned from the Copy Lane tab. This was @crm’s first cross-agent build coordinated over babylon (channel pmv2-ui, task #1085, handed off by @crypto and resolved by @crm) — not the old file-based CRM_AGENT_HANDOFF.md channels.

For Agents

  • Route: 5th tab on the /polymarket shell (web/src/pages/polymarket/index.tsx) — alongside Scout / Copy / Longshot / Bets.
  • Page: web/src/pages/polymarket/crypto/CryptoDashboard.tsx + crypto/components/* (LaneHealthTable.tsx, WalletScalingTable.tsx, WalletScaleControl.tsx).
  • Hooks: web/src/lib/hooks/polymarket/useCryptoLanes.ts, useCryptoWallets.ts, useSetWalletScale.ts.
  • Reads: two public views — crypto_lane_health, crypto_wallets — that come from @crypto’s migration 0009_crypto_ui_views.sql (NOT in the CRM repo). Same single CRM Supabase (mkofmdtdldxgmmolxxhc “mgmt”, useSupabase()).
  • Writes: exactly one column — pmv2_wallets.size_scale — via useSetWalletScale.
  • Untyped tables: crypto_* / pmv2_* are absent from database.types.tsas never casts (Scout/Bets precedent; see infra-gotchas).

Cross-agent origin — the first babylon-coordinated CRM build

The crypto side (@crypto, project crypto-shortterm) had already scoped this exact deliverable: crypto-shortterm-empire-ui-crm-topology-2026-07-06 established that the “empire UI” IS the Levandor CRM, so a crypto lane-ops surface is “one more tab, not a new app” — @crypto ships public SQL views + a build-prompt, @crm writes the React. That note’s open question — “is @crm even on babylon yet?” — was closed the same day by crm-babylon-onboarding-2026-07-08; task #1085 on channel pmv2-ui is the first request @crm picked up over babylon rather than a file channel.

Architecture (repo-standard Page → hook → Supabase)

Reads — lane health + wallets

  • useCryptoLanes reads crypto_lane_health, sorted by toxicity_gap = signal_win − fill_win (the adverse-fill signal — how much edge is lost between the signal price and the actual fill); rows are severity-colored by that gap. See @crypto’s lane-state note for the upstream semantics.
  • useCryptoWallets reads crypto_wallets (per-wallet roster + safe display columns only).
  • Both mirror usePmv2Runs: as never casts, a Raw → parse numeric-string coercion step (PostgREST serializes numeric as strings), and a 30s poll.

Write — the one authoritative control

  • useSetWalletScale writes pmv2_wallets.size_scale, clamped to [0.5, 3], behind an AlertDialog confirm. On an RLS denial it surfaces an inline “not authorized” rather than a toast/throw.
  • WalletScaleControl is the UI; WalletScalingTable lists each wallet’s current scale.

Secret-column protection — why the write is a bare .update().eq() with NO .select()

pmv2_wallets also stores enc_private_key / key_nonce — the armed wallets’ encrypted secrets. The write is deliberately a bare .update().eq() with no .select(), so it needs no SELECT grant on the base table. Combined with a column-scoped grant update (size_scale) on pmv2_wallets to authenticated (update-only, no select), the key columns stay unreadable via the authenticated role. The CRM reads wallet data only through the crypto_wallets view (safe columns only) — it never selects from the pmv2_wallets base table. This is the write-side application of the CRM RLS write-auth convention.

Why the write is labeled “authoritative”

Writing pmv2_wallets.size_scale used to be a silent no-op on the running executor: @crypto’s crypto-shortterm-per-wallet-size-scale-2026-07-06 proved (via a live 2026-07-06 experiment) that size_scale was boot-loaded — captured once at position_manager start, so a DB write did nothing until a restart. That was raised as babylon pmv2 #1065. As of deploy 7cd41b0, PM’s size_scale hot-reload is LIVEposition_manager now re-reads size_scale per order entry, so a write from this tab takes effect on the next order without a restart. That resolution is what makes this control authoritative rather than advisory.

Operational gotcha inherited from @crypto

A size_scale bump should be paired with a matching daily_spend_cap_usd bump on that wallet — the cap is a reject-gate, not a resizer, so a bigger scale burns a fixed daily cap faster and silently yields fewer fills. Full detail in crypto-shortterm-per-wallet-size-scale-2026-07-06.

Pending dependencies (other agents — not @crm)

The tab is shipped and green, but two upstream applies are needed for it to be fully live. Until then it degrades gracefully (renders empty, write shows “not authorized”):

BlockerOwnerEffect when applied
Apply migration 0009_crypto_ui_views.sql to the shared Supabase (mkofmdtdldxgmmolxxhc)@deploycrypto_lane_health / crypto_wallets go live → the read tables render (tab is empty until then)
Apply the RLS policy + column-scoped grant update (size_scale) on pmv2_wallets@positionmanager (Andras gates applying it)the useSetWalletScale write succeeds (else RLS-deny → inline “not authorized”)

Verification

  • 27/27 vitest tests pass (CryptoDashboard.test.tsx, useCryptoLanes.test.tsx, useCryptoWallets.test.tsx, useSetWalletScale.test.tsx).
  • tsc -b clean; full vite build green.