Remove the blanket .agent/ gitignore so the phase roadmap, user stories, reports, and PLAN.md are versioned with the code. Only runtime artifacts (.agent/phase-sessions/, .agent/pipeline.log) remain ignored. Update AGENTS.md git protocol rule to match.
50 lines
2.4 KiB
Markdown
50 lines
2.4 KiB
Markdown
# 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.
|