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 model implies "just add an entry to _domains.yaml." Reality is six places, of which only one is declarative:

  1. _domains.yaml entry

    Indexer derives include/exclude prefixes from this. Add slug, taxonomy file, review queue file, embedding config, decay mode, lexical weight, recency boost. Declarative.

  2. launchd plists

    Two per domain: com.shourjo.kb-<domain>-indexer.plist (uvicorn port allocation) + ingest plist if applicable. Lives in ~/Library/LaunchAgents/.

  3. Slash commands (3 files)

    ~/.claude/commands/rx-<domain>.md (generator) + -status.md (disposition) + -history.md (action-rate). Each is hardcoded to the door's storage backend.

  4. /rx-board filter list

    The $1 domain filter is a literal allowlist. New domain = edit the file.

  5. /rx-digest per-domain branches

    The digest generator iterates a hardcoded list of doors. Add a new branch for the new domain's storage backend.

  6. /rx-analyze domain pair

    Currently hardcoded to {fitness, finance}. Adding a new domain to the analysis loop is a manual edit.

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 0029_recommendations.sqlCHECK (domain = 'finance')SQL 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
RX_OPERATOR_UUID env hardcoded in .env.laptopEnv var
kb-mcp sources.yaml port routingMCP config
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.

Known limitations to plan around

Substring hypothesis ↔ rec linkage (finance)

D-046 introduced hypothesis_id resolution by searching the rec's body markdown for the hypothesis slug. False-positive prone: a rec mentioning "NVDA" matches every hypothesis whose slug contains "nvda" — even unrelated ones.

Mitigation path (deferred): explicit operator-tagged linked_hypotheses frontmatter field, validated at write time.

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 :8004 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.

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 project CLAUDE.md tables. 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.

Re-verification workflow

When source repos change, run this re-research pass:

1. Spawn 4 parallel explore agents:
   - TradingView app — tables, ingest sources, /rx-finance endpoints
   - Sho's Playgroun — 4 doors, rx artifacts, DECISIONS-LOG (latest D-NNN)
   - knowledge-vault — _domains.yaml, embedding pipeline, taxonomy
   - Lovable verocity — Supabase schema, disposition flow

2. Run an adversarial 4-voice council pass:
   - Skeptic (factual errors)
   - Architect (missing edges, failure modes)
   - User-advocate (clarity for non-technical readers)
   - Maintainer (rot vectors, hardcoded coupling)

3. Diff against this site's views/*.html. Update only affected pages.

4. Do NOT touch any source repo — this is an additive explainer.

Source-of-truth quick reference

QuestionWhere to look
Latest decisions?~/Documents/Sho's Playgroun/rx-meta/DECISIONS-LOG.md
Current phase status?~/Documents/Sho's Playgroun/rx-meta/ROADMAP-v1_*.md
Domain registry?~/Documents/knowledge-vault/_domains.yaml
TradingV tables?~/Documents/Claude/TradingView/migrations/ (Alembic SoT, not create_all)
TradingV module docs?~/Documents/Claude/TradingView/.claude/modules/
Slash commands?~/.claude/commands/rx-*.md
Embedding config?~/Documents/Claude/TradingView/tools/vault_indexer/config.py
Chunking?~/Documents/Claude/TradingView/tools/vault_indexer/vault.py:chunk_body()
Hybrid retrieval?~/Documents/Claude/TradingView/tools/vault_indexer/{hybrid,lexical,graph_compute}.py
Lovable schema?Supabase via mcp__Lovables_verocity__list_objects
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