phase: 130_chat_answer_clusters
Build and Push Containers / build-and-push-app (push) Successful in 2m27s
Build and Push Containers / build-and-push-db (push) Successful in 12s

**Phase 130 — final verification pass: 100% green, complete.**

- **Verified:** chips-only row (no chip for image docs) → images-only row (`.msg-images`) → related row → `.msg-actions` last; `getActionsRow` used by all 3 buttons + stopped/failed notes; `markLastRetryable` save-re-append hack gone; in-place chip fallback on image-load failure; CSS right-align with per-button auto margins removed — implementation matches the design in every detail.
- **Completion criteria:** (1) mixed citation renders chips→images→actions-last, DOM-asserted ✅ (2) all 3 buttons in one right-aligned `.msg-actions`; non-admin sees Retry-only; restore path identical ✅ (3) 404-bytes figure → plain chip, unit-pinned ✅ (4) E2E isolated ✅ (5) no behavior change (modal affordances, related tier, ARIA names) ✅
- **Tests/lint/coverage:** `uv run pytest tests/e2e/test_chat_answer_clusters.py -v --no-cov` → 4 passed; `uv run pytest --cov=app --cov-report=term-missing` → 3061 passed, **99%** app coverage; the 4 E2E files this phase modified (test_answer_images, test_image_documents, test_pdf_support, test_responsive_polish) → 22 passed in isolation; `uv run ruff check .` clean, `uv run pyright` → 0 errors.
- **Docs:** README.md — verified consistent in full (page table vs routes, env-var coverage vs `.env.example`, phase-130 cluster wording already in tree); no changes needed. AGENTS.md — fixed one pre-existing inconsistency: rule 5 still cited the retired "centered 46rem chat column"; now states the 72rem container contract (phase 100).
- **Decisions:** PLAN.md §7.1 still says 46rem, but it's owner-managed (phase 100 records the supersession pending owner sign-off) and out of my editable scope — AGENTS.md now cross-references the supersession.
- **Next pending phase:** `131_read_line_ranges` (todo/).
This commit is contained in:
2026-09-28 07:49:37 -04:00
parent 2d0f608246
commit c88c65641a
46 changed files with 3266 additions and 351 deletions
+20 -13
View File
@@ -74,8 +74,9 @@ client-side router. Direct URLs deep-link to the matching view.
### Chat (`/`)
Ask questions; answers stream in with **source chips** that cite the exact
documents used. Clicking a chip opens the document in an **almost-fullscreen
modal** on the same page (no new tab). The onboarding suggestion chips
documents used (a cited image document gets no chip — its compact
figure is the citation, phase 130). Clicking a chip or figure opens the
document in an **almost-fullscreen modal** on the same page (no new tab). The onboarding suggestion chips
follow the opening questions of the 3 most recent saved chats (each
chat's first question — a follow-up never chips, phase 103) — on a
fresh deployment they seed from `BOR_SUGGESTIONS`.
@@ -451,9 +452,13 @@ first-class document:
with a small lazy-loaded thumbnail (progressive enhancement — a fetch
failure falls back to the doc icon), the document viewer renders the
image with the description below it, and a cited image doc renders
**inline in the chat's sources block** (alt text = the description).
The agent's `read` tool returns the description with a marker telling
the model it is reading an image document.
**in the chat's sources images row** (phase 130: its own
`.msg-meta.msg-images` cluster below the chips row — no chip for an
image doc, the figure is the citation; alt text = the description;
a failed image load replaces the figure in place with the plain chip,
so the citation survives a lost byte copy). The agent's `read` tool
returns the description with a marker telling the model it is reading
an image document.
- **Prune guard.** While `BOR_IMAGES` is **off**, syncs never prune
existing image documents — an image doc is invisible to the walk, not
a deleted file. With the toggle on, a deleted image file prunes its
@@ -507,14 +512,16 @@ of its answer:
(enforced server-side); the text answer is always given in full — the
image supplements, never replaces.
- **Rendered as an answer figure.** A shown image appears as a
first-class figure below the answer text and above the source chips —
on the live turn, after a page reload, and on the shared chat page; a
failed load collapses the figure (never a broken-image icon).
- **Image chips on zero-read turns.** A retrieved image document in the
suggested tier is auto-cited: its source chip (with figure) appears
even when the model never `read`s it — the model's seed block already
carries the image's full description, so a real model has no reason to
read it (this applies with `BOR_IMAGES=1`, any `BOR_ANSWER_IMAGES`).
first-class figure below the answer text and above the source
clusters (the chips row, then the images row — phase 130) — on the
live turn, after a page reload, and on the shared chat page; a failed
load collapses the figure (never a broken-image icon).
- **Image citations on zero-read turns.** A retrieved image document in
the suggested tier is auto-cited: its figure (in the images row — no
chip, phase 130) appears even when the model never `read`s it — the
model's seed block already carries the image's full description, so a
real model has no reason to read it (this applies with `BOR_IMAGES=1`,
any `BOR_ANSWER_IMAGES`).
- **Off = byte-identical.** No fourth tool offered, no figure, no
`answer_image` key in any SSE frame or saved record.