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
+15 -12
View File
@@ -413,12 +413,13 @@ def test_chat_sources_block_shows_the_inline_image(
turn chips nothing): the mock's echo carries the agent ``read``
result VERBATIM — including the task-05 ``IMAGE_DOC_MARKER`` line
(the model saw the description, not raw bytes) — and the answer's
sources block carries the citation chip (the READ doc clears the
usefulness floor) AND the compact inline figure — the ``<img>``
from the bytes route, the visible caption + ``alt`` settling to
the document's summary (the figure fetches it from the content
endpoint — the frame carries no summary), the "shown in the chat
nicely" contract."""
sources block carries the citation (the READ doc clears the
usefulness floor) as the compact figure in the images row
(phase 130 task 01: NO chip for an image doc — the figure IS the
citation) — the ``<img>`` from the bytes route, the visible
caption + ``alt`` settling to the document's summary (the figure
fetches it from the content endpoint — the frame carries no
summary), the "shown in the chat nicely" contract."""
login(page, app_url, next="/")
expect(page.locator("#kb-banner")).to_be_hidden()
@@ -433,15 +434,17 @@ def test_chat_sources_block_shows_the_inline_image(
expect(page.locator("#send-btn")).to_be_enabled(timeout=30_000)
expect(page.locator("#send-label")).to_have_text("Send")
# The citation chip (the text affordance stays — the figure is
# additive, not a replacement).
chip = page.locator(".msg.brain .source-chip")
expect(chip).to_have_count(1)
expect(chip).to_have_text(f"{SOURCE_NAME}/{DOC_PATH}")
# Phase 130 (task 01, TODO L6): NO chip for the image doc — its
# figure is the citation — and the figure sits in its own images
# cluster (.msg-meta.msg-images), not interleaved with a chip.
expect(page.locator(".msg.brain .source-chip")).to_have_count(0)
row = page.locator(".msg.brain .msg-meta.msg-images")
expect(row).to_have_count(1)
expect(row).to_have_attribute("aria-label", "Source images")
# The inline figure: image + caption, both settling to the summary
# (the content fetch resolves the alt/caption after the title).
fig = page.locator(".msg.brain .source-image")
fig = row.locator(".source-image")
expect(fig).to_have_count(1)
img = fig.locator(".source-image-img")
expect(img).to_be_visible(timeout=15_000)