04 · doors · mixed

The 4 doors — deep dive

Each door is independent. Different storage backend, different signals, different UI surface, different status. Click a tab below to drill into a single door.

At a glance

DoorStorageUI surfaceSignal sourceStatus
Zeus fitness Supabase Lovable verocity SQL view + helper live · v0.1
Athena nutrition Supabase none (zombie) stub (Phase J.2) scaffold
Lakshmi finance TradingV postgres TradingV /rx-finance panel SQL queries on TradingV live · v0
Ganesh learning markdown only none (editor) markdown helper live · v0 · empty corpus

One headline fact per door

The tabs below go deep; if you read nothing else, read these four.

Zeus · fitness

The most mature door — the only one with a two-layer drift composite (90% SQL view + 10% body-recomp helper, D-022). Recs surface on the operator's phone.

Athena · nutrition

Zombie storage, on purpose: Supabase accepts nutrition rows but no UI reads them (D-047). The generator stays in scaffold mode until the operator picks a disposition channel.

Lakshmi · finance

Laptop-only postgres, never the cloud — and the only door whose loop closes all the way to realised P&L via trades.related_rec_id (D-046).

Ganesh · learning

Markdown only, forever in v0.x (D-047). No database, no UI surface — the editor is the app. Vault corpus currently empty, and the command tolerates that.

Zeus — fitness

The most mature door. Live since v0.1. Workouts logged through the verocity phone app land in Supabase. A SQL view exposes a 7-component drift score. A separate helper covers body composition. Recommendations write to Supabase and surface in the verocity app.

Fitness is also the door where the new deep-enrichment surfaces are confirmed rendered: the verocity Coach rec-detail view (a separate fitness-UI repo, merged) shows the deep retrieval, contradiction, and external-counter results — governed by a TypeScript port of the same thresholds the finance app enforces in Python (see Deep tech and Maintenance).

Drift composite (D-022 layering)

Fitness is the only door using a two-layer composite:

composite = 0.90 × view.drift + 0.10 × body_recomp_sub
7 view components (sum to 1.0) → ×0.90, plus body_recomp ×0.10 pace 0.30 block_lag 0.20 gap_breach 0.15 load_drop 0.15 rpe_drift 0.10 recovery 0.05 conditioning 0.05 view.drift weight 0.90 SQL view v_drift_signals body_recomp_sub weight 0.10 helper script measurements.yaml
The 7 in-view weights sum to 1.0. The composite blends view (90%) with body_recomp (10%).

Storage + UI

MarkdownZeus/rx/rx-YYYY-MM-DD-NN.md
DB rowSupabase recommendations WHERE domain='fitness'
UILovable verocity → Recommendations tab
DispositionUI button → Supabase UPDATE → next /rx-fitness step 0.7 patches markdown
State files03_execution/current_plan.md, measurements.yaml
Slash commands/rx-fitness, /rx-fitness-status, /rx-fitness-history

Frontmatter schema (minimum required keys)

id: uuid
created_at: ISO8601
goal_ref: string (e.g. lift-vol-PR-2026Q3)
block_week: int
trigger: {type, signals_fired: [string]}
drift_score: float (0..1)
drift_breakdown: {pace, block_lag, gap_breach, load_drop, rpe_drift, recovery, conditioning, body_recomp}
confidence: int (0..100)
confidence_breakdown: {data_recency, signal_clarity, vault_corpus, dedup_distance}
status: open | snoozed | acted | dismissed
acted_disposition: acted_as_prescribed | acted_modified | skipped | dismissed
acted_at: ISO8601 | null
phase_w_synced_at: ISO8601 | null
verocity_rx_id: uuid (Supabase row pointer)
source_refs: [{path, score, quote}]
prior_open_recs: [{id, relation: reinforce|supersede|independent}]

Athena — nutrition Scaffold mode only. Storage is wired (Supabase recommendations accepts domain='nutrition' rows), but the generator is unwired and there is no UI surface. The /rx-nutrition command runs in scaffold mode and emits a decision menu instead of a real rec.

Why it's a zombie

D-045 originally routed both fitness and nutrition through Lovable verocity. D-047 (operator directive, 2026-05-17) reversed that: Lovable surface = fitness only. So nutrition row format and table are ready, but no app reads them. Result: write-only storage with no consumer — a zombie.

Storage + UI

MarkdownAthena/rx/SPEC.md + planned rx-nutr-*.md
DB rowSupabase recommendations WHERE domain='nutrition' (planned, currently unused)
UInone (D-047) — disposition channel UNRESOLVED (D-048 + Athena SPEC banner: 4 resolution options pending operator pick)
DispositionTBD (Phase J.2) — see Athena/rx/SPEC.md 2026-05-19 banner. Pragmatist recommendation: markdown-only (mirror Ganesh learning).
State filesstate.md, profile.md, rules.md, meals.md, levers.md
Slash command/rx-nutrition — scaffold/decision-menu mode

Planned 5 signals (dormant until Phase J.2)

  • adherence_trend — compliance with macro plan over rolling window
  • macro_drift — actual macros vs target ratios
  • timing_consistency — meal-slot timing vs schedule
  • rotation_drift — repetition fatigue from same staples
  • appetite_drift — subjective hunger signal vs baseline

All five depend on a daily-log source the operator has not committed to yet. Phase J.2 ships when that decision is made.

Lakshmi — finance

The most data-rich door. Reads from the TradingV FastAPI app's local postgres — never Supabase. Hypotheses, opportunities, drift alerts, positions, and watchlist all feed the composite. Recs surface in a FastAPI /rx-finance panel.

The dual-auth split. Write and read paths carry different credentials: the ingest token can write recs but cannot read or disposition them, while the API key can read and disposition but cannot mass-ingest. The /rx-finance command runs with the ingest token only; the operator's UI uses the API key. A compromised generation pipeline therefore can't rewrite history, and a compromised UI can't flood the store.
Known limitation (D-046). Hypothesis ↔ rec linkage uses a substring heuristic on body_md, which is false-positive prone — a rec mentioning "NVDA" matches every hypothesis whose slug contains "nvda", even unrelated ones. The fix (explicit operator-tagged frontmatter linking) is deferred; see Maintenance.

Drift composite (6 components)

Six weighted signals (rx-finance.md). Two documented reweights shaped them: D-025 added position_thesis_match (the signal once called position_concentration — renamed, not a second signal) at 0.20; D-026 demoted watchlist_orphan 0.15 → 0.05 as housekeeping, not capital-at-risk.

6 components (rx-finance.md) hypothesis_expiry 0.20 stale_opportunities 0.20 hypothesis_invalidation 0.20 position_thesis_match 0.20 (D-025) drift_alerts 0.15 watchlist_orphan 0.05 (D-026)
Operator-tuned weights (composite is normalised). Ground truth is ~/.claude/commands/rx-finance.md — they have evolved before and can again.
Tie-break priority — capital-at-risk dominates freshness. When several signals fire near-equally, the driving signal is chosen in a fixed order: position_thesis_match > hypothesis_invalidation > drift_alerts > hypothesis_expiry > stale_opportunities > watchlist_orphan. Signals about money already at risk outrank signals about staleness or coverage gaps.
A second axis: operator attention (surfaced, not yet fused). Alongside the drift composite, the system computes an attention_score per ticker from TradingView context items — weighted by kind (screenshot 1.0, note 0.7, idea 0.5, event 0.4, webhook 0.2), decayed on a 7-day half-life, and taken as the MAX across a rec's tickers (not the sum, so one heavily-watched name doesn't get diluted by quiet ones). It is stored on the rec (attention_score + attention_breakdown) and shown today, but is not yet folded into the drift composite — an honest "second signal, still maturing".

Why these weights changed is itself an evaluation story — see Feedback: watchlist_orphan looked sensible offline but the operator kept dismissing orphan-driven recs as "not capital-at-risk yet" (D-026), and position_thesis_match was added (D-025) after positions kept being held while their theses quietly expired under the old weights.

Storage + UI

MarkdownLakshmi/rx/rx-fin-YYYY-MM-DD-NN.md
DB rowTradingV postgres recommendations WHERE domain='finance'
Write authPOST /v1/rx/recs with header X-RX-Ingest-Token
Read authGET /v1/rx/recs with header X-API-Key
UITradingV FastAPI panel at /rx-finance
DispositionUI button → POST /v1/rx/recs/{id}/disposition → next /rx-finance step 0.7 patches markdown
Outcome attributiontrades.related_rec_id FK closes loop into P&L (D-046)
Slash commands/rx-finance, /rx-finance-status, /rx-finance-history, /rx-deep-retrieve, /rx-contradiction-check, /rx-counter-external

New rec-card surfaces (deep-retrieval & de-biasing)

The finance rec now carries several new fields from the deep-retrieval / de-biasing work, in rough order of visual prominence. Specified in the write-back contract; finance-panel rendering not yet confirmed (the fitness Coach view is the confirmed renderer):

  1. Contradiction banner — "⚠ sources conflict", shown only when the severity governor raises it (else a quiet "sources checked").
  2. citations_statushas_mismatch is the alarming one (a quote that isn't in its chunk).
  3. External counter — an off-vault disconfirming source, shown beside the in-vault counter-thesis with a governed credibility chip.
  4. Deep-retrieval affordance — "run /rx-deep-retrieve" when thesis_match is weak.
  5. P&L vs action-rate divergence flag — in the /rx-analyze report, not the card.
  6. attention_score — pre-existing, unchanged.

Ganesh — learning

Empty corpus today. The learning vault scope is wired but contains zero ingested content. The vault bundle returns 0 results until the operator drops URLs in Videos/learning/_ingest_queue.md. The command sets thesis_match=0 and continues anyway.

Hard rule (D-047). Learning rx will never touch Lovable. If a local UI surface is built in the future it will be a separate store, not the Supabase recommendations table.

The newest and lightest door. Markdown only forever in v0.x per D-047. No database, no Lovable surface, no FastAPI panel. The editor reads the file; the operator edits frontmatter to disposition.

Drift composite (5 components, flat)

Unlike finance and fitness, the /rx-learning command carries no drift formula — it delegates entirely to a learning-state helper and is instructed to use the helper's output as-is. The weights below live in that helper.

5 components, computed from markdown by a learning-state helper slot_drift 0.30 deadline_pressure 0.20 cross_domain_unapplied 0.20 inbox_rot 0.15 mode_monoculture 0.15
Signals derive from heterogeneous markdown — active_sprints, _inbox, learning_log, notes.

Storage + UI

MarkdownGanesh/rx/rx-learn-YYYY-MM-DD-NN.md
DB rownone — markdown is authoritative
UInone — editor edits frontmatter directly
Disposition/rx-learning-status <id> <disposition> edits frontmatter
State files02_library/active_sprints.md, _inbox/, 03_execution/learning_log.md, 02_library/notes/
Vault scopeIndexer Port 4, cache the learning cache
Slash commands/rx-learning, /rx-learning-status, /rx-learning-history

The cross-cutting commands

Four commands span doors. One is gated by action-rate data (rx-analyze).

CommandPurposeDomain scopeGate
/rx-board Cross-domain timeline view — open recs, aging, forced-decision flags fitness + finance + nutrition + learning 4-domain since D-048 none
/rx-digest Weekly raw activity summary → rx-meta/digest-YYYY-WW.md 4-domain per-branch aggregation (finance via TradingV postgres post-D-046; degraded fallback to Lakshmi markdown) none (cron-friendly)
/rx-analyze Per-signal predictive power + source quality + weight-adjustment proposals fitness Supabase + finance TradingV + learning markdown (3-domain since D-048; nutrition omitted until Phase J.2) ≥5 dispositioned recs per domain — domains under that are skipped as "insufficient data". The 30% action-rate is a health band, not a run-gate (D-018, D-048)
/rx-goals new D-049 Cross-door goal-progress meta tracker — elapsed vs progress drift per goal; READ-ONLY status, NOT recs Zeus goals.yaml + Lakshmi goals_by_horizon.md + net_worth_snapshot.md + Ganesh learning_goals.md + active_sprints.md (3 doors; Athena explicitly skipped — body_recomp tracked via Zeus) none — runs as a scheduled weekly notification (Mondays 09:00 CET)
← prev
03 · Pipeline