05 · feedback · mixed

What happens after you act on a recommendation

A rec is written. You read it, act on it (or don't), and mark its disposition. From that point on, two things happen: the markdown file gets patched to reflect your action, and the system's overall action-rate updates. If action-rate falls below 30%, the system declares itself broken.

The full feedback loop

Hover any node to see its connections highlighted. The reconcile step is lazy — it fires when you next run a rx command.

Phase W reconciler in plain language

Phase W = the reconciler. After you act on a rec in the UI, the database row is updated. But the markdown file on disk still says status: open. The reconciler is the step that fixes that mismatch. It runs at step 0.7 of the next /rx-<door> invocation: queries recent rec rows, compares each to its on-disk markdown, and patches the frontmatter where the DB has newer state. If everything is already in sync, it does nothing.

What gets patched into frontmatter

FieldPatched when
statusDB row moved from open → snoozed / acted / dismissed
acted_dispositionoperator picked acted_as_prescribed / acted_modified / skipped / dismissed
acted_atany non-open transition stamps this
subjective_fit_1_5operator gave a 1–5 rating in the UI
outcome_noteoperator typed a free-text reflection
snoozed_until · snooze_countoperator snoozed the rec
phase_w_synced_atstamped every time the reconciler touches the file (idempotency marker)

The disposition options

acted_as_prescribed

Did the exact thing the rec said. Strongest positive signal.

acted_modified

Took the direction but altered the specifics. Still counts as acted for action-rate.

skipped

Made an explicit choice not to act but agreed with the framing. Distinct from dismiss.

dismissed

Rejected the rec entirely. Counts against action-rate.

The subjective fit score (1–5)

Optional. Operator rating of how well the rec fit the actual moment. Useful for /rx-analyze later — recs from sources or signals that consistently get fit ≤2 are candidates for weight reduction.

Outcome attribution — the finance closed loop

Finance is the only door where the loop closes all the way to P&L. The trades table has a related_rec_id FK (D-046) — when a trade is journalled, the operator can link it back to the rec that triggered it. /rx-analyze later joins trades to recs to compute per-signal hit-rate weighted by realised P&L.

rec id, drift, signal acted trade entry + related_rec_id FK close realised P&L exit_price - entry_price attributed per-signal hit-rate weights tunable
Closed-loop attribution lives in finance only. Fitness equivalent is "did the workout actually happen" — proxied by next-session log existence.

Action-rate as system-death signal

D-003 · the < 30% rule

An open-loop recommender that nobody acts on is broken regardless of the quality of its content. The /rx-*-history commands surface this metric per domain. The threshold:

Example: 58% — healthy.

The red tick at the top marks the 30% floor. Anywhere below = system-death. The gauge fills the moment the metric is computed; if it lands below the tick, /rx-analyze and the future cross-domain meta-rec both refuse to run.

action_rate = count(status='acted') / count(status IN ('acted','dismissed'))

Per-door surface comparison

DoorHow operator dispositionsReconcile direction
Zeus Lovable verocity UI button → Supabase UPDATE Supabase → markdown (step 0.7)
Athena no surface (D-047) not applicable (no DB writes)
Lakshmi FastAPI panel → POST /v1/rx/recs/{id}/disposition TradingV postgres → markdown (step 0.7)
Ganesh /rx-learning-status <id> directly edits markdown not applicable — markdown is authoritative
The reconciler is split. Phase W has two implementations: one reads Supabase (handles fitness rows), the other reads TradingV postgres (handles finance rows). They share no code. This is fine because the doors don't share storage anyway.
← prev
04 · Doors