prepping for SPA conversion
Build and Push Containers / build-and-push-app (push) Successful in 14s
Build and Push Containers / build-and-push-db (push) Successful in 11s

This commit is contained in:
2026-09-05 21:50:56 -04:00
parent 0e4651c779
commit 7e567bddf3
7 changed files with 191 additions and 0 deletions
@@ -1,25 +0,0 @@
# Phase 75 — "Save as doc" captures the whole chat session
**Source:** `TODO.md` L4 — "Chat history isn't being passed to the LLM. When the LLM responds and you ask a follow-up question the previous question/answer isn't passed to the model. Since my models support preserve thinking, make sure to pass previous thinking blocks as well. Then, update the \"save as doc\" process to include the output from the entire chat session rather than the last response. The user can edit out anything they don't want to keep from previous replies." (this phase covers the last two sentences; the history sentences are phase 74)
**Story:** n/a (TODO-derived)
**Context:** `frontend/assets/app.js` (`appendSaveAsDocButton(wrap, markdown)` ~L591, `saveAsDoc(btn, markdown)` ~L615 — today the body is ONLY the clicked bubble's raw answer; `defaultDocTitle()` ~L562; the `conversation` array is the full `bor.chat.v1` record), `app/api/doc_drafts.py` (draft create/edit/push — unchanged by this phase), `frontend/doc-edit.html` + `frontend/assets/doc-edit.js` (the existing edit screen: title / path / free-form editable body — the user's means of editing turns out), phase-59 E2E `tests/e2e/test_response_to_docs.py` (bare-repo fixture + admin flow + the single-turn body expectation that must move to the transcript shape), the mock's default composed answer (embeds the question's first 80 chars → different questions give byte-distinct deterministic answers).
## Objective
"Save as doc" drafts a document from the ENTIRE chat session — every question and answer up to the click — instead of only the last response; the existing doc-edit screen's free-form body editing is how the user edits out anything they don't want to keep from previous replies (no new UI surface).
## Dependencies
- `74_llm_chat_history` (todo) — the chat turn path (including its E2E pins) is settled first; this phase only touches the save-as-doc side of the same page.
## Tasks
1. `01_session_transcript.md` — `buildSessionTranscript()` in `app.js`; `saveAsDoc` posts the transcript as the draft body; the phase-59 single-turn E2E expectation moves to the transcript shape.
2. `02_e2e_full_session_save.md` — multi-turn session → save → all turns present in order → edit a turn out in doc-edit → push → bare-repo git verification; regressions + commit.
## Testing & Quality
- Unit/integration: frontend-only change — no `app/` code (the `doc_drafts` API is untouched; the >90% `app/` floor holds by the full suite).
- E2E: new story suite `tests/e2e/test_save_doc_session.py` (run in isolation) + the updated expectation in the existing `tests/e2e/test_response_to_docs.py`, both on the bare-repo fixture (git as source of truth).
## Completion Criteria
- [ ] The draft body for an N-turn session contains every turn in order (`## N. <question>` + the answer's raw markdown); the doc-edit screen shows the full transcript prefilled; editing a section out and pushing lands exactly the edited body in `BOR_DOCS_REPO` (verified with git, not UI text).
- [ ] The single-turn behavior of phase 59 is preserved in transcript shape (updated E2E green).
- [ ] `uv run pytest` green; coverage >90%; ruff + pyright clean.
- [ ] One atomic `--no-gpg-sign` commit (e.g. `feat(docs): save the whole chat session as a doc`); phase dir moved to `.agents/phases/complete/`.
@@ -1,29 +0,0 @@
# Task 02 — E2E: multi-turn session → save → all turns → edit one out → push (git-verified) + regressions + commit
**Phase:** `75_save_doc_full_session` · **Source:** `TODO.md:4` — "Then, update the \"save as doc\" process to include the output from the entire chat session rather than the last response. The user can edit out anything they don't want to keep from previous replies."
**Story:** n/a (TODO-derived)
## Objective
Prove the whole loop in the browser against the bare repo (git as source of truth, the phase-59 convention): the saved draft carries the ENTIRE session, and the user can edit an unwanted previous reply out before the document lands in the docs repo.
## Work
1. `tests/e2e/test_save_doc_session.py` (Playwright; reuse the bare-repo fixture pattern from `tests/e2e/test_response_to_docs.py`: `git init --bare` + seeded work clone on `main`, app env `BOR_DOCS_REPO`/`BOR_DOCS_BRANCH=bor-docs`/`BOR_DOCS_BASE_BRANCH=main`/`BOR_DOCS_WORK_DIR`, admin login via `tests/e2e/auth_helpers.py`; KB fixture from `tests/e2e/test_chat_rag.py`):
- `test_full_session_save_and_edit_out` —
a. Three DISTINCT on-topic questions in one session (the mock's default composed answer embeds each question's first 80 chars, so the three answers are byte-distinct and assertable); wait for `done` after each.
b. Click "Save as doc" on the LAST brain bubble → `/doc-edit.html?draft=<uuid>`; assert the prefilled body contains `## 1.`, `## 2.`, `## 3.` IN ORDER, each followed by that turn's answer text (byte-exact against the mock), and that the title is the last question (whitespace-collapsed ≤120 chars — the unchanged `defaultDocTitle()`); assert NO thinking scratchpad text leaked into the body (turn 1 asked with `think out loud`, so the record carries a `thinking` block the transcript must exclude).
c. Edit out a previous reply: delete the entire section-2 block (heading + answer) from the body textarea; Push.
d. Assert the status live region shows branch + sha, then GIT-VERIFY: `git -C <bare> show bor-docs:<path>` equals the EDITED body exactly — sections 1 and 3 present, section 2 (both question and answer) gone; the UI's sha prefix equals `git rev-parse bor-docs` (7 chars).
- `test_earlier_bubble_button_saves_whole_session` — click "Save as doc" on the FIRST brain bubble after the third turn has completed → the draft body still contains all three sections (ASSUMPTION A6: the transcript is the whole session at click time, regardless of which bubble's button was used); cancel out without pushing (no git change: `bor-docs` still has the previous commit).
2. Regression runs (isolation, AGENTS.md rule 9): `test_response_to_docs.py` (after task 01's expectation update — the phase-59 contract survives in transcript shape), `test_chat_rag.py` (the meta-row button on every bubble), `test_cache_busting.py` (page list unchanged — no new page in this phase).
3. `uv run pytest` green; `uv run pytest --cov=app --cov-report=term-missing` >90%; `uv run ruff check . && uv run pyright` clean.
4. Commit (Conventional Commits, `--no-gpg-sign`) — e.g. `feat(docs): save the whole chat session as a doc`; move `.agents/phases/todo/75_save_doc_full_session/` → `.agents/phases/complete/`.
## Testing & Quality
- E2E: `uv run pytest tests/e2e/test_save_doc_session.py -v --no-cov` green in isolation (DB up, `git` on PATH — the phase-59 `pytest.skip` guard on absence).
- Coverage: **>90%** on `app/` (no `app/` changes in this phase).
## Completion Criteria
- [ ] The full-session → edit-out → push loop passes with the bare repo's file content as the assertion (section 2 provably absent from the pushed file; sections 1 and 3 byte-exact).
- [ ] The earlier-bubble button saves the whole session (A6 pinned); the thinking block never reaches the document.
- [ ] Regression suites green in isolation; full suite + coverage >90% + ruff + pyright clean.
- [ ] One atomic `--no-gpg-sign` commit; phase dir moved to `.agents/phases/complete/`.