fc623db27a
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>
49 lines
2.5 KiB
Markdown
49 lines
2.5 KiB
Markdown
# 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.
|