Files
ducoterra dbf2af26c6 refactor(agents): migrate .agent/ planning tree to .agents/
Standardize on the .agents/ directory (shared with project skills):
phases/, user_stories/, reports/, screenshots/, validate.sh, and
phase-sessions/ + pipeline.log all move to .agents/ (git mv preserves
history; runtime artifacts move alongside).

Updates every reference in AGENTS.md, README.md, .gitignore, app
docstrings, and test story headers. Historical KB content in data/
and the runtime pipeline.log transcript are left untouched.
2026-09-05 10:57:07 -04:00

50 lines
2.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Story: Tune How Brain Answers (Steering Notes)
**Phase:** `15_steering_notes.md` · **E2E:** `tests/e2e/test_steering.py`
## Narrative
As **a user**, when an answer **isn't quite right** (too chatty, wrong
assumption, missing context), I want to **tune** Brain right there: a
short instruction ("be more concise", "assume I'm on NixOS") that is
**stored in the database** and **read into the system prompt** so every
future reply follows it.
- **Given** an answer I don't like
- **When** I click "Tune" under it and save an instruction
- **Then** the note is persisted in Postgres, shown in the tuning
panel (where it can be removed), and injected into the system prompt
of every subsequent turn — verifiable in the answer.
## Acceptance criteria
1. New `steering_notes` table (id, note, created_at) via Alembic
migration 0003.
2. Stateless API: `GET /api/steering` (newest first),
`POST /api/steering` (1–2000 chars, 201 / 422),
`DELETE /api/steering/{id}` (204 / 404).
3. System prompt: when notes exist, a `<tuning>` section (numbered notes,
char-budgeted) is appended to **both** HIGH and DEFLECT prompts; when
none exist, the prompt is byte-identical to today's.
4. Per-turn log line gains `tuning=N`.
5. UI: "Tune" button under every completed brain answer (deflected too)
→ inline form → save → confirmation; header "Tuning" toggle lists
notes with per-note delete; all labeled, live regions, ≥44px targets,
Phase-08 tokens.
6. Notes render as text (XSS-safe).
7. Unit + integration green, `app/` coverage >90%, story E2E green in
isolation, one `--no-gpg-sign` commit.
## Playwright Mapping Rule
**Test Scenario → `tests/e2e/test_steering.py`** (mock LLM, seeded KB —
the mock echoes the first tuning note into its answer when `<tuning>` is
present, so prompt injection is observable in the UI):
1. `test_tune_under_answer_persists_and_steers` — answer → Tune → note
"STEEER-MARKER be concise" → saved → next answer contains the marker
(note reached the system prompt) → note visible in panel with count.
2. `test_delete_note_stops_steering` — delete from panel → count 0 →
next answer has no marker.
3. `test_note_rendered_as_text_xss_safe` — note with `<script>` renders
as visible escaped text, never executes.
4. `test_tuning_panel_a11y` — toggle aria-expanded, region label,
delete buttons labeled, live region announces save/delete.