phase: 166_hide_empty_answer_bubble
Build and Push Containers / build-and-push-app (push) Successful in 1m41s
Build and Push Containers / build-and-push-db (push) Successful in 11s

**Phase 166 — final verification pass: green.** All 3 task files already in `complete/`, so this was verification + defect/doc fixing.

- **Verified:** `.msg .bubble:empty { display: none; }` exists once in the Messages block with provenance; `frontend/` diff is styles.css only (**no app.js change**); `tests/unit/test_no_empty_answer_bubble.py` 26 pins pass; phase-158/161 anchor pins (thinking_timeline + tool_states + edit_message) **75 passed**.
- **Phase E2E in isolation:** `uv run pytest tests/e2e/test_no_empty_box.py --no-cov` → **5 passed**. Mutation check (rule removed) → 2 failures, so the suite genuinely pins the defect.
- **Regression set, isolated:** thinking_timeline 6 · turn_progress_loader 3 · chat_history 5 · share_chat 4 · save_share_ux 5 · failed_turn_retry 4 · chat_answer_clusters 4 · chat_error_honesty 4 → all pass.
- **Full gates:** `uv run pytest --cov=app --cov-report=term-missing` → **3880 passed, app/ 99%** (gate >90%); `uv run ruff check .` → clean; `uv run pyright` → **0 errors**.
- **Whole E2E tree swept file-by-file (150 files, own pytest process each).** 4 suites were broken by *earlier* phases; fixed and re-run green:
  - `test_kb_tree`, `test_sync_summary_visibility`, `test_folder_summary_voice` — `td:nth-child(4) span` became a strict-mode violation when phase 156 wrapped the Edit label in `.cell-clip` → scoped to `span.kb-desc-text`.
  - `test_search_tool_scope` — pinned the pre-`690fd98` per-run `<summary>`/`open` shape → re-pinned to the ONE `details.reasoning` container + `.tool-calls-summary`.
  - `test_header_consistency` — `.doc-meta` pixel bound (`<34px`) was broken by phase 153's 44px touch-target floor at ≤640px → replaced with a shape-first single-line check that still fails a real wrap.
- **Completion criteria:** mid-turn box hidden ✅ · bubble appears at first delta, phase-130 rows below unchanged ✅ · restored/shared empty answer paints no box ✅ · no app.js change + anchors + full gates ✅ · commit/`complete/` move = harness's job (I ran no git index/history commands).
- **Docs:** README — added the phase-166 "no empty answer box mid-turn" note; fixed a **stale** Thinking lead ("collapsible Thinking blocks", pre-dated the 2026-10-06 one-disclosure refinement); QA section now states E2E isolation is mandatory (shared Postgres + fixed ports 8123/8901) with the per-file sweep loop. AGENTS.md — rule 5 UI checklist now names the phase-166 suppression rule.
- **Notable:** running all of `tests/e2e/` in one pytest session is not supported (fixture/port interleaving causes ~126 spurious failures) — documented in README. Restored the tracked `.agents/screenshots/doc_modal_scrollbar_1280.png` that `test_document_viewer.py` rewrites on every run.
- **Next pending phase:** `167_api_payload_diet`.
This commit is contained in:
2026-10-08 04:27:03 -04:00
parent b0c7aed613
commit ab9391d019
36 changed files with 2348 additions and 93 deletions
+13 -6
View File
@@ -651,12 +651,19 @@ def test_scoped_scope_survives_save_restore(
page.goto(app_url + f"/?chat={row['id']}")
_assert_scoped_line(_tool_line(page))
# Phase 117 (D3): a restored turn renders FOLDED — one compact
# "Tool call(s) (N)" line (the disclosure shape is unchanged by
# this phase: the `open` attribute is ABSENT).
disclosure = page.locator(".msg.brain .tool-calls-disclosure").last
expect(disclosure).not_to_have_attribute("open", timeout=30_000)
expect(disclosure.locator("summary")).to_have_text(
# Phase 117 (D3), re-pinned for the phase-161 D5 refinement (the
# owner's 2026-10-06 shape, shipped in 690fd98): a restored turn
# renders FOLDED inside the ONE details.reasoning container — the
# container is the ONLY disclosure (zero nested <details>), the
# run itself is a plain div whose label is the
# .tool-calls-summary SPAN. The compact "Tool call (1)" line the
# phase-117 pin named is unchanged; only the element carrying it
# moved (this phase's scope — the scoped line — is asserted above).
container = page.locator(".msg.brain details.reasoning").last
expect(container).not_to_have_attribute("open", timeout=30_000)
expect(container.locator("details")).to_have_count(0)
run = page.locator(".msg.brain .tool-calls-disclosure").last
expect(run.locator(".tool-calls-summary")).to_have_text(
"Tool call (1)", timeout=30_000
)