chore(agent): track .agent/ planning tree in git
Build and Push Containers / build-and-push-app (push) Successful in 12s
Build and Push Containers / build-and-push-db (push) Successful in 10s

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.
This commit is contained in:
2026-09-01 10:18:22 -04:00
parent 5fa620fde5
commit 4971e2859d
818 changed files with 23964 additions and 4 deletions
@@ -0,0 +1,41 @@
# Phase 49 — Retry the Last Answer (Redo)
**Source:** `TODO.md` L4 — "Need a retry button to retry the last answer, like a redo button"
**Story:** n/a (TODO-derived — owner roadmap confirmation 2026-08-29)
**Context:** `frontend/assets/app.js` — `handleSend` owns the whole turn (user-bubble append + conversation push → fetch/SSE → save points), `appendTuneButton` is the house pattern for a per-bubble meta-row action (reuses the `.msg-meta` row, the `role=list` → `role=listitem` rule, the admin gate), `conversation`/`rememberBrainTurn` hold the persisted records, and phase 48's Stop button means the last brain bubble can be a **stopped partial** — the natural retry candidate.
## Objective
A **Retry** button on the last brain answer re-asks the preceding question in place — the old answer is removed (DOM + persisted record), the fresh answer streams into its place, and the conversation never duplicates the question.
## Dependencies
- `48_stop_generation` (todo) — the button/state-machine work it builds on (the meta-row button family, the in-flight guard) and the stopped partials it makes retryable.
- `14_chat_persistence` (complete) — the conversation records the retry edits in place.
## Tasks
1. `01_retry_button.md` — the `runTurn` extraction + the Retry button + redo-in-place semantics + CSS.
2. `02_e2e_retry_answer.md` — the story Playwright suite + regressions + commit.
## Testing & Quality
- Frontend source pins (house pattern): the `runTurn(text, {reask})` extraction (reask skips the user append/push), last-bubble-only Retry management, the redo-in-place record edit, the in-flight no-op.
- Coverage: **>90%** on `app/` (unchanged — frontend-only phase).
- E2E (mandatory, A16): `tests/e2e/test_retry_answer.py`, run in isolation.
## Completion Criteria
- [ ] The last brain bubble (completed, deflected, empty-fallback, or stopped partial — admin and anonymous alike) carries a Retry button; earlier brain bubbles do not.
- [ ] Clicking Retry removes the old answer (DOM + `bor.chat.v1` record), re-sends the preceding question without duplicating it, and streams the fresh answer into its place; the new answer carries the Retry button (it is the new last).
- [ ] While a turn is in flight, Retry does nothing (no double turn).
- [ ] `uv run pytest` green; coverage TOTAL unchanged (>90%).
- [ ] `uv run pytest tests/e2e/test_retry_answer.py -v --no-cov` green in isolation (DB up).
- [ ] Regression E2E suites green in isolation: `test_chat_rag.py`, `test_chat_persistence.py`, `test_stop_generation.py`.
- [ ] `uv run ruff check . && uv run pyright` clean.
- [ ] One `--no-gpg-sign` commit; phase dir moved to `.agent/phases/complete/`.
## Locked decisions
- **Owner-locked (2026-08-29, roadmap confirmation):** redo-in-place (the old answer is replaced, the question is not duplicated); only the **last** brain bubble is retryable; Retry is available to **all** visitors (chat is public — unlike Tune, which is admin-only); inert while a turn is in flight.
- **A10 untouched** — retry is a fresh `POST /api/chat` (the API stays stateless).
- **A16/A17 honoured** — one story E2E suite, one atomic commit.
## Commit
```bash
git add -A .agent/ frontend/ tests/ && git commit --no-gpg-sign -m "feat(chat): retry the last answer — redo-in-place Retry button on the latest brain bubble"
```