Files
project-lyra/docs/DECISION_LOG.md
T
serversdown fc623db27a feat: decision-log data layer for Decide mode (learning layer)
Storage + tools so Decide mode can learn instead of one-shot tie-breaking: log
the call Brian makes, resolve it later with the outcome, recall similar past calls
to ground new recommendations in his own track record.

- memory: decisions table + Decision dataclass + log/resolve/get/list/recall_decisions
  (embedding over situation+choice; embed failure never blocks a log)
- tools: log_decision / resolve_decision / recall_decisions handlers + specs
- tests: 9 covering roundtrip, resolve, open-only filter, similarity rank, tool layer

Data layer only — NOT wired into any mode's allow-list or the Decide card yet
(the prompt/taste part is left for Brian; see docs/DECISION_LOG.md). No behavior
changes until the tools are added to _DECIDE_TOOLS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 05:19:47 +00:00

2.5 KiB

Decision log — Decide mode's learning layer

Built overnight on feat/decision-log. This is the data layer + tools only. The prompt/mode wiring (the taste part) is left for you on purpose — no persona/card edits were made.

The idea

Decide mode is currently a one-shot tie-breaker. The learning layer gives it memory: log the call Brian actually makes, record how it turned out, and recall similar past calls so a new recommendation leans on his own track record instead of generic advice.

Lifecycle: log (when the call is made) → resolve (later, with the outcome) → recall (next time something similar comes up).

What's built

Storage (lyra/memory.py):

  • decisions table — situation, options, choice, rationale, confidence (1-5), tags, embedding (over situation+choice), outcome, outcome_rating (-1/0/+1), resolved_at.
  • Decision dataclass (with a .resolved property).
  • log_decision(...) -> id, resolve_decision(id, outcome, rating) -> bool, get_decision(id), list_decisions(limit, open_only), recall_decisions(query, k) (cosine over embeddings, each hit carries .score).
  • Embedding failures never block a log (blob just stays NULL).

Tools (lyra/tools.py) — handlers + specs, wired into dispatch:

  • log_decision (situation, choice, options?, rationale?, confidence?, tags?)
  • resolve_decision (decision_id, outcome, rating?)
  • recall_decisions (query, k?) — returns past calls with their verdicts

Tests (tests/test_decisions.py) — 9, covering roundtrip, resolve, open-only filtering, similarity ranking, and all three tool handlers. Full suite green, ruff clean.

What's left for you (the wiring)

  1. Allow-list — add the three tools to _DECIDE_TOOLS in lyra/modes.py (and decide whether recall_decisions also belongs in Study). One-liner, but it's the gate that lets her actually call them.
  2. Decide card guidance — tell her when to use them: recall similar decisions before recommending, log once Brian commits to a call, and circle back to resolve open ones. This is the part I didn't want to touch without you (no bandaids).
  3. Optional surfacing — open/unresolved decisions are a natural thing for her to raise (thought loop / ping), and a small UI panel could list them. Not built.
  4. Optional auto-prompt to resolve — the dream loop could notice decisions that have been open a while and nudge for an outcome.

Nothing here changes behavior until step 1 — the tools exist but no mode offers them.