# Phase 57 — Edit + Re-embed Document Summaries **Source:** `TODO.md` L4 — "Be able to edit the summaries for documents in the RAG. Click an edit button in summary box and change the summary that the AI created. re-embed that document after changing the summary." **Story:** n/a (TODO-derived — owner roadmap confirmation 2026-08-31) **Context:** The summary box is the `.doc-summary` section rendered by the shared core in `frontend/assets/document.js` (page + modal surfaces) — it renders only when `doc.summary` is non-empty, as a labeled `h2` + text-node `
` (XSS contract: `textContent` only). Phase 30 stores a non-markdown document's `lite` digest on `documents.summary` **and** indexes it as one extra embedded chunk (`is_summary=True`, position −1 — `app/rag/importer.py:313–336`). The document viewer is deliberately PUBLIC (phase 16 owner decision — only the catalog is admin-gated), so the new edit affordance and endpoint must be admin-gated (`require_admin`, `app/core/auth.py`); `GET /api/whoami` (`app/api/auth.py:56`) already drives admin reveals on static pages. Async endpoints exist in `app/api/` (`app/api/chat.py:223`, `app/api/git_sources.py:231`), so the re-embed (`await llm.embed([...])`) fits a standard `async def` handler. ## Objective An admin can edit (or clear) the AI-generated summary directly in the summary box; on save the stored summary and its `is_summary` chunk are updated and the chunk is **re-embedded** — anonymous visitors see the unchanged public viewer with no edit affordance. ## Dependencies - `56_import_extensions_env` (todo, preceding) ## Tasks 1. `01_update_summary_api.md` — `PATCH /api/documents/summary` (admin): update/clear `documents.summary`, replace the `is_summary` chunk, re-embed it (one `embed` call, DB untouched if the LLM fails). 2. `02_summary_edit_ui.md` — Edit button in the `.doc-summary` panel (admin-only) → inline textarea + Save/Cancel → live-region status on both viewer surfaces. 3. `03_e2e_edit_summaries.md` — story Playwright suite + regressions + the atomic commit. ## Testing & Quality - Integration: the existing document-content API tests (phase 10) gain the PATCH cases — update + re-embed (chunk content + non-NULL vector + unchanged chunk count), clear (summary NULL + chunk deleted), markdown doc with no prior `is_summary` chunk (one created), 404 unknown pair, 403 anonymous, LLM-failure error mapping (502/503, DB unchanged). - Frontend source pins (house style, `tests/unit/test_save_chat_ui.py` pattern): `document.js` (whoami gate, editor wiring, PATCH path, `textContent` render contract), `styles.css` (new `.doc-summary-*` classes). - E2E (mandatory, A16): `tests/e2e/test_edit_summaries.py`, run in isolation (mock `SUMMARY_MODE` fixture KB). - Coverage: **>90%** on `app/` (validate.sh gate). ## Completion Criteria - [ ] Admin: `PATCH /api/documents/summary` with new text updates `documents.summary`, replaces the `is_summary` chunk's content, and stores a fresh embedding (content chunks untouched — count unchanged). - [ ] Admin: PATCH with an empty/whitespace summary clears `documents.summary` (NULL) and deletes the `is_summary` chunk. - [ ] Anonymous: 403 on PATCH; no Edit button in the viewer; the viewer otherwise renders byte-for-byte as today. - [ ] The Edit button appears in the summary box (document page **and** modal), opens a textarea prefilled with the current summary, and Save reflects the change in the panel with a live-region confirmation; Cancel restores. - [ ] `uv run pytest` green; coverage TOTAL >90%. - [ ] `uv run pytest tests/e2e/test_edit_summaries.py -v --no-cov` green in isolation (DB up). - [ ] Regression E2E suites green in isolation: `test_document_summaries.py`, `test_document_viewer.py`, `test_cache_busting.py`. - [ ] `uv run ruff check . && uv run pyright` clean. - [ ] One `--no-gpg-sign` commit; phase dir moved to `.agent/phases/complete/` (`.agent/` stays untracked — owner instruction, commit 281f355). ## Locked decisions - **Owner-locked (2026-08-31, roadmap confirmation, D4):** 1. Summary editing is **admin-only** (endpoint + UI affordance). The document viewer stays public. 2. "Re-embed that document" = replace the `is_summary` chunk with a fresh embedding (one `embed` call). The document's content chunks are **not** re-embedded — the summary is the only text that changed. 3. Clearing the summary (empty save) sets `documents.summary = NULL` and deletes the `is_summary` chunk. - **Fail-before-write:** the embedding happens before any DB mutation; an LLM failure returns an error (502/503, `app/api/git_sources.py` `ModelUnavailableError`-style mapping) and leaves the row and chunk untouched. ## Commit ```bash git add app/ frontend/ tests/ && git commit --no-gpg-sign -m "feat(kb): edit + re-embed document summaries from the viewer (admin)" ```