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.
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# Phase 50 — Save & View Chat History
|
||||
|
||||
**Source:** `TODO.md` L5 — "Need a way to save and view chat history in a new page, then return to that history with a click"
|
||||
**Story:** n/a (TODO-derived — owner roadmap confirmation 2026-08-29)
|
||||
**Context:** A10 (revised, phase 16) keeps the public **chat** API stateless, and phase 14 locked conversation persistence to browser-local `localStorage` (`bor.chat.v1`). This phase records an **owner-locked extension** (AGENTS.md rule 3, owner permission 2026-08-29): conversations the owner explicitly **Saves** are stored in a new Postgres `saved_chats` table — `/api/chat` itself stays stateless, and nothing is stored about a conversation that was not saved. The admin gate is `app/core/auth.require_admin` (phase 16, the `steering` router's pattern). The localStorage record shape (`{who, text, sources?, deflected?, suggestions?, thinking?, tools?, stopped?}`) is the stored `messages` payload, so a saved chat restores pixel-identical through the existing `renderStoredMessage` path. The new page follows the phase-34 shared-header contract (every page carries the identical nav block; admin-only links ship hidden and `header.js` reveals them) and AGENTS.md rule 5 (a **full-width table** — no skinny wasted-space list).
|
||||
|
||||
## Objective
|
||||
The owner can Save the current conversation, see every saved chat on a new **History** page (full-width table), click one to return to the chat with that conversation loaded, and delete a saved chat.
|
||||
|
||||
## Dependencies
|
||||
- `48_stop_generation` / `49_retry_answer` (todo, sequential) — no shared-file conflicts beyond `app.js`; ordering keeps the chat UI stable while the save/load plumbing lands.
|
||||
- `14_chat_persistence` (complete) — the record shape + the `renderStoredMessage` restore a saved chat reuses.
|
||||
- `16_admin_auth` + `34_consistent_navbar` (complete) — the `require_admin` gate + the one-bar nav contract.
|
||||
- **Owner permission (2026-08-29):** the A10 extension — a new `saved_chats` table for explicitly saved conversations (see Context).
|
||||
|
||||
## Tasks
|
||||
1. `01_saved_chat_model.md` — the `SavedChat` model + migration `0008_saved_chats` (+ migration test).
|
||||
2. `02_chats_api.md` — admin-only CRUD under `/api/chats` + schemas + integration tests.
|
||||
3. `03_save_chat_ui.md` — the chat page: Save button, `?chat=<id>` load, upsert semantics, live-region feedback, absent for anonymous.
|
||||
4. `04_history_page.md` — `history.html` + `history.js` (full-width table, Open + two-step Delete), the `#nav-history` admin-only nav link on every page, the cache-busting page registration, CSS.
|
||||
5. `05_e2e_chat_history.md` — the story Playwright suite + regressions + commit.
|
||||
|
||||
## Testing & Quality
|
||||
- Integration: `tests/integration/test_chats_api.py` (the CRUD contract: 403 anonymous, create auto-title, list order, get, put replacement, delete 404/204, message-shape validation).
|
||||
- Integration: `tests/integration/test_migration_0008.py` (the house migration-test pattern from `test_migration_0007.py`).
|
||||
- Coverage: **>90%** on `app/`.
|
||||
- E2E (mandatory, A16): `tests/e2e/test_chat_history.py`, run in isolation.
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] Admin: Save on the chat page stores the conversation (auto-title = first question, 120-char cap); re-Save on the same conversation updates the same row; New chat unlinks.
|
||||
- [ ] `/history.html` (admin) lists saved chats in a **full-width** table (Title, Messages, Updated, Actions); a row's title opens `/?chat=<id>` and the chat renders the stored conversation (sources, thinking, stopped notes, deflection chips — pixel-identical to the local restore); a subsequent Save updates that row.
|
||||
- [ ] Delete removes the row (inline two-step confirm, no `window.confirm`); an unknown id 404s; `GET /api/chats` + the served `/history.html` carry the cache-busting contract (no-cache + `?v=` rewrite).
|
||||
- [ ] Anonymous: no Save button, no History nav link, `/api/chats*` → 403, `/history.html` shows the gated state without fetching `/api/chats`.
|
||||
- [ ] `uv run pytest` green; coverage TOTAL >90%.
|
||||
- [ ] `uv run pytest tests/e2e/test_chat_history.py -v --no-cov` green in isolation (DB up).
|
||||
- [ ] Regression E2E suites green in isolation: `test_chat_persistence.py`, `test_nav_consistency.py`, `test_shared_header.py`, `test_admin_auth.py`, `test_cache_busting.py`.
|
||||
- [ ] `uv run ruff check . && uv run pyright` clean.
|
||||
- [ ] One `--no-gpg-sign` commit; phase dir moved to `.agents/phases/complete/`.
|
||||
|
||||
## Locked decisions
|
||||
- **Owner-locked extension (2026-08-29, recorded per AGENTS.md rule 3):** `saved_chats` in Postgres stores **only** conversations the owner explicitly saves; `/api/chat` stays stateless; phase 14's local persistence is unchanged (the localStorage session keeps working exactly as before — saving is an additional, explicit action).
|
||||
- **Owner-locked (2026-08-29):** Save/History is **admin-only** (no account system — anonymous rows would be unfindable); absent-not-hidden for anonymous (phase 16); auto-title, no rename UI in v1 (the schema still accepts an optional `title`); re-Save = upsert of the same row; `?chat=<id>` replaces the local conversation and links it; inline two-step delete confirm (no `window.confirm`).
|
||||
- **A16/A17 honoured** — one story E2E suite, one atomic commit.
|
||||
|
||||
## Commit
|
||||
```bash
|
||||
git add -A .agents/ app/ alembic/versions/ frontend/ tests/ && git commit --no-gpg-sign -m "feat(chat): save and view chat history — admin-only saved_chats, History page, open-a-chat return"
|
||||
```
|
||||
Reference in New Issue
Block a user