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
+17 -10
View File
@@ -541,10 +541,13 @@ def test_chat_grounds_on_the_pdf_and_chips_the_embedded_image(
PDF (phase 119 A1): the echo carries the read result VERBATIM —
the document's FULL extracted text (LOCKED A4 — the page markers
and both page texts reach the model and land in the answer), and
the sources block carries the PDF's citation chip (the READ doc)
PLUS the embedded image's AUTO-cited chip (phase 124 — the
suggested-tier image doc) with its compact inline figure (the
bytes route, caption/alt settling to the description)."""
the sources block renders the mixed citation as phase-130
clusters (task 01): the chips row carries the PDF's citation chip
(the READ doc) and NO chip for the image doc (its figure IS the
citation), while the embedded image's AUTO-cite (phase 124 — the
suggested-tier image doc) sits in the images row as its compact
figure (the bytes route, caption/alt settling to the
description)."""
login(page, app_url, next="/")
expect(page.locator("#kb-banner")).to_be_hidden()
@@ -563,17 +566,21 @@ def test_chat_grounds_on_the_pdf_and_chips_the_embedded_image(
expect(page.locator("#send-btn")).to_be_enabled(timeout=30_000)
expect(page.locator("#send-label")).to_have_text("Send")
# The citation chips: the READ PDF first (read order), then the
# suggested image doc's auto-cite (phase 124).
# Phase 130 (task 01) — the mixed citation: the chips row carries
# the READ PDF ONLY (no chip for the image doc — the figure IS
# the citation), and the image doc's auto-cite (phase 124) is the
# figure in its own images row.
chip = page.locator(".msg.brain .source-chip")
expect(chip).to_have_count(2)
expect(chip).to_have_count(1)
expect(chip.nth(0)).to_have_text(f"{SOURCE_NAME}/{DOC_PATH}")
expect(chip.nth(1)).to_have_text(f"{SOURCE_NAME}/{IMAGE_REL}")
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 on the image chip: image + caption, both
# The inline figure in the images row: image + caption, both
# settling to the description (the content fetch resolves the
# summary 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)