07 · maintenance · technical

Rot vectors and where to look first

The system is single-operator and accreted across many decisions. Some coupling is deliberate (storage routing), some is the cost of not having a v2 refactor yet. This page documents both — what to watch for, and where to start when something changes.

Adding a new domain — the 6 touch points The 6-touch-point rule is conservative. Some changes cascade beyond these six. A storage backend change for an existing door (e.g. moving learning to a DB) would also require migration schemas, a new reconciler branch, and possibly an RLS policy.

The model implies "just add an entry to _domains.yaml." Reality is six places, of which only one is declarative:

Add a new domain = edit 6 places _domains.yaml declarative · the only one launchd plists 2 per domain · manual slash commands 3 files · manual /rx-board filter allowlist · manual /rx-digest per-domain branch · manual /rx-analyze domain pair · manual
Adding one domain means editing six places — only _domains.yaml is declarative; the other five are manual edits.

Hardcoded coupling — find before refactoring

Renaming a domain (e.g. finance → markets) requires touching every place the literal string appears. Inventory of known sites:

SiteLock type
TradingV migration — CHECK (domain = 'finance') on the recommendations tableSQL constraint
TradingV Pydantic schema — Literal["finance"]Python type
Supabase recommendations.domain CHECK (domain IN ('fitness','nutrition'))SQL constraint
Lovable WHERE-clause filter — frontend + server filter on domain IN ('fitness','nutrition')App code
Phase W reconciler branch — if domain == 'finance' selects backendSlash command
/rx-*-status regex — filename pattern checkSlash command
Operator UUID hardcoded as an env var in the laptop .envEnv var
kb-mcp sources.yaml port routingMCP config

Known limitations to plan around

Substring hypothesis ↔ rec linkage (finance) — now MITIGATED

D-046 originally resolved hypothesis_id by searching the rec's body markdown for the hypothesis slug. False-positive prone: a rec mentioning "NVDA" matched every hypothesis whose slug contained "nvda" — even unrelated ones.

Mitigated. Recs now carry explicit linked_hypothesis_ids named at compose time (match_type="explicit"). The substring heuristic is demoted, not removed: it only runs as a fallback suggestion, any already-explicitly-linked hypothesis suppresses its substring match, and remaining hits are tagged match_type="substring_fallback". The landmine ledger (below) tracks removing the fallback entirely at the second user.

Embedding model immutability

embedding_dim is baked into the SQLite cache schema. Changing EMBEDDING_MODEL env without deleting the cache crashes startup. There is no automatic re-embed sweep.

Procedure: stop indexer → rm cache-<domain>.db → restart → wait for full re-index (minutes to tens of minutes depending on corpus size).

Learning empty corpus

Indexer Port 4 is live but the vault has no learning content. bundle(scope="learning") returns 0 results. The /rx-learning command tolerates this by setting thesis_match=0 and continuing.

Mitigation: operator must populate Videos/learning/_ingest_queue.md and run an ingest.

Snooze auto-revive timezone drift

D-039 noted a timezone bug in finance auto-revive — snoozed_until compared against NOW() at the application layer vs NOW() at the DB layer in inconsistent ways. The fix lives inside /rx-finance.md step 0.5 but is not codified as a regression test. Risk: silent regression on any rewrite of the command.

Cross-cutting commands learning-aware (resolved by D-048, 2026-05-19)

Historical: when D-047 shipped the learning slice on 2026-05-18, /rx-board, /rx-digest, and /rx-analyze were left enumerating {fitness, finance, nutrition} only — invisible breakage for any learning rec that landed.

Resolved by D-048 (Phase Q.5, 2026-05-19): all three commands are now 4-domain. /rx-board + /rx-digest also switched finance source from Lakshmi/rx/*.md filesystem read to TradingV LOCAL postgres recommendations table (D-046 contract). Degraded fallback to Lakshmi markdown mirror if TradingV daemon unreachable. Per-domain auto-revive split across writable surfaces (fitness Supabase UPDATE, learning markdown Edit, finance read-only display-only).

Lovable RLS is permissive

Phase U/V shipped with USING (true) RLS policy on the Supabase recommendations table — single-tenant assumption. Multi-tenant safety deferred post-v1.x. Frontend + server both filter by owner_user_id client-side. If the verocity app is ever shared with another operator, this is the first thing to harden.

Nutrition zombie data — disposition channel unresolved

Supabase recommendations accepts domain='nutrition' rows but no UI surface reads them (D-047 removed Lovable's nutrition tab). If /rx-nutrition is ever wired in scaffold-to-live mode without first re-routing storage or building a UI, the rows will sit invisible in the table.

D-048 surfaced this as two blockers, not one: daily-log data source (default Athena/daily_log.md per D-027, never confirmed) AND disposition channel (no UI). Athena/rx/SPEC.md banner now enumerates 4 resolution options:

  1. Markdown-only mirror of Ganesh learning pattern (Pragmatist recommendation)
  2. Supabase write + manual disposition (status quo, friction)
  3. Build local nutrition UI (Critic veto until ≥5 dispositioned recs with sustained ≥30% action-rate)
  4. Reverse D-047 + add Lovable nutrition tab (operator directive change)

Until operator picks, /rx-nutrition stays in scaffold mode: reports state + decision menu, does NOT generate recs.

What the deep-retrieval & de-biasing program moved

The 2026-06-13 retrieval-depth program retired or mitigated several of the rot vectors this page used to list as open:

Rot vectorNow
Substring hypothesis mislinkingMITIGATED — explicit linkage, substring is fallback-only
Confident-wrong from un-contradicted sourcesMITIGATED — contradiction pass flags conflicts (governor-gated)
Fabricated / drifted citationsMITIGATED — deterministic citation verification at ingest
Self-influence flywheel (P&L credited to recs it caused)MITIGATED — influenced trades excluded from predictive-lift

New rot vectors the program introduced

Every fix has a cost. These are the new coupling/fault-lines to watch:

Two-language governor drift. The deep-result governors (contradiction severity + disconfirmation credibility) exist in two implementations that must stay in lockstep: one in the app (Python, finance ingest) and a twin in the fitness UI (TypeScript). Both are test-pinned, but a threshold change must touch both or the two doors will silently disagree. The TS twin exists because the fitness UI is the deterministic enforcement point for the non-TradingV stores (it recomputes the verdict rather than trusting an LLM-written one).

Landmine ledger — deferred debt with explicit triggers

Tracked in the rx repo's tech-debt log; each entry is fine at n=1 and has a named condition that should force the rework:

LandmineTrigger to fix
L1 · substring hypothesis-linkage fallbackremove at 2nd user OR >50 hypotheses
L2 · single hard-coded operator UUIDre-architect auth at 2nd user (the master n=2 trigger)
L3 · global (not per-cohort) thresholdsper-cohort + Bayesian smoother at 2nd user
L4 · /rx-analyze lift/P&L math lives in the command, not the appendpoint-ify when it drifts or needs unattended runs

Where the doors-storage mapping actually lives The mapping {fitness: supabase, nutrition: supabase, finance: tradingv_local, learning: markdown} is not declared in any single yaml or json file. It is reconstructable only by reading D-045 + D-047 in rx-meta/DECISIONS-LOG.md plus the trading app's internal project docs. A maintainer wishing to verify "where does door X write?" must read those prose sources, then grep slash commands.

The closest thing to a canonical declaration is the facts inventory in the project README.md (alongside this site) and the source-of-truth table in AGENTS.md §3. If you change the mapping, update those tables first — they are the only spots designed to be re-quoted.

How this site stays honest — the re-verification workflow

This explainer is documentation about other systems, so it rots the moment a source repo changes. Rather than trusting the pages, every update runs a re-verification pass. First, each upstream surface is independently re-researched: the trading app (tables, ingest sources, endpoints), the four door workspaces (rx artifacts and the decisions ledger), the knowledge-vault (_domains.yaml, embedding pipeline, taxonomy), and the Supabase schema behind the verocity app.

The findings then face an adversarial review from four deliberately opposed perspectives: a skeptic hunting factual errors, an architect hunting missing edges and failure modes, a user advocate checking clarity for non-technical readers, and a maintainer checking rot vectors and hardcoded coupling. Only pages whose facts actually changed get edited, every load-bearing fact keeps a pointer to its canonical source, and the source repos themselves are never touched from here — this site is additive only.

Source-of-truth quick reference

QuestionWhere to look
Latest decisions?rx-meta/DECISIONS-LOG.md (operator workspace)
Current phase status?rx-meta/ROADMAP-v1_*.md
Domain registry?knowledge-vault/_domains.yaml
TradingV tables?TradingV repo migrations/ (Alembic SoT, not create_all)
TradingV module docs?TradingV repo .claude/modules/
Slash commands?~/.claude/commands/rx-*.md
Embedding config?the vault indexer's config module (TradingV repo)
Chunking?the vault indexer's chunking routine
Hybrid retrieval?the vault indexer's hybrid / lexical / graph modules
Lovable schema?Supabase schema introspection on the verocity project
Reconciler logic?Step 0.5 + 0.7 of ~/.claude/commands/rx-<door>.md

The deletions log — what got removed

Architectural simplifications worth remembering:

WhatWhenWhy
Lovable nutrition surfaceD-047 · 2026-05-17Operator-directed scope reduction. Lovable = fitness-only.
Railway deployADR 018 · 2026-05-17Cost + complexity not justified for single-operator app. Local-only since.
Auto-cron snooze reviveD-032Surprised operator with unexpected rec re-appearance. Made lazy.
BackendToggle component2026-05-18 cleanupVestigial from when Railway was an alt-backend. No purpose post-ADR-018.
Original D-045 (Lovable for nutrition)Overridden by D-047D-045 still describes storage routing correctly; only the UI surface for nutrition was changed.

Future v1.x candidates (per ROADMAP-v1_x) What is intentionally not in scope. Multi-operator support, fancier RLS, real-time cross-domain orchestration, scheduled auto-rec firing without command invocation, a v2 of the embedding model. Each of these has a v1.x or v2 placeholder but is deferred deliberately until the action-rate signal proves the system is worth investing in further.

← prev
06 · Deep tech