Files
ducoterra 2683128876
Build and Push Containers / build-and-push-app (push) Successful in 2m24s
Build and Push Containers / build-and-push-db (push) Successful in 14s
phase: 111_chat_banner_retry
## Phase 111 Completion Report

**Implemented/Verified:**
- `#kb-banner` contains a `<button type="button" class="banner-retry" id="banner-retry">` (hidden by default, Retry label + SVG)
- `showErrorBanner(detail, opts)` reveals the button only when `opts.retryable` is true AND `lastBrainWrap` exists
- Turn-error path passes `{ retryable: true }`; all non-turn callers (share, save-doc, stale) remain text-only
- `clearErrorBanner()` re-hides the button
- `ERROR_HINT` changed from "Try again — …" to "If this persists, check the LLM is reachable."
- `.banner-retry` CSS styled as a pill (matching `.stale-regenerate` family)
- 12 source-assertion unit tests in `tests/unit/test_frontend_banner_retry.py`

**Test / Lint / Coverage:**
- `uv run pytest tests/unit/test_frontend_banner_retry.py -v --no-cov` → 12 passed
- `uv run pytest --cov=app --cov-report=term-missing` → 2362 passed, 99% coverage
- `uv run ruff check .` → All checks passed
- `uv run pyright` → 0 errors
- `tests/e2e/test_llm_retry.py` → 4 passed (in isolation)
- `tests/e2e/test_smoke.py` → 3 passed (in isolation)

**Completion Criteria:**
- ✅ Retry button visible after failed chat turn, re-runs last question
- ✅ Non-turn callers show text-only banner (no button)
- ✅ pytest green, coverage >90%, ruff + pyright clean
- ✅ Phase dir to be moved by pipeline gate

**Next pending phase:** `112_honesty_gate_weak_hits`
2026-09-14 23:07:56 -04:00

5.1 KiB
Raw Permalink Blame History

Phase 111 — Chat error banner: a real Retry button (TODO L1)

Source: TODO.md L3–21 — "L1 — Chat error banner: 'Try again' is plain text, not a button (2026-09-15, brain-of-reese interactive test)" Story: n/a (interactive-test follow-up fix; extends the phase-06 loading-feedback and phase-49/53 retry assets). Context: frontend/index.html:112 renders #kb-banner (chat page only — the Sources/document pages do not render it) with #kb-banner-text; frontend/assets/app.js — showErrorBanner(detail) (L2084) writes ${detail} ${ERROR_HINT} as plain text; ERROR_HINT (L362) begins "Try again — …" so "Try again" reads as a clickable action but is not. retryLastTurn(wrap) (L2131) re-asks the last question in place (phase 49); #stale-regenerate (index.html:134, handler at app.js ~L1898) is the existing banner-button → retryLastTurn pattern.

Objective

Give the chat-view error banner a real Retry control after a failed/dropped turn: the banner shows a visible Retry button that re-runs the last question without re-typing (reusing the phase-49 redo-in-place and the stale-banner button pattern). The banner text stops mimicking a button, and every existing showErrorBanner caller (share, save-doc, stale chat) keeps working text-only — the Retry button appears only on failed chat turns.

Dependencies

  • 110_fix_sse_db_pool_exhaustion (complete) — pipeline predecessor (execution order) only; no code dependency (this phase touches frontend/index.html, frontend/assets/app.js, frontend/assets/styles.css, and frontend unit tests).

Design (shared by all tasks — the executor reads this, not the chat)

  • Banner button (task 01): add <button type="button" class="banner-retry" id="banner-retry" hidden> inside #kb-banner (after #kb-banner-text), mirroring the #stale-regenerate markup (same refresh SVG + visible "Retry" label). Hidden by default; showErrorBanner(detail, opts) gains an optional second arg — when the caller flags the error as a failed chat turn (the UI state-machine path at app.js:1281 if (state === UI_STATE.error) showErrorBanner(errorDetail)), the button is revealed and wired to retryLastTurn(lastBrainWrap) — the same last-brain-bubble targeting the #stale-regenerate handler uses. No retryable brain bubble → no button.
  • Copy: ERROR_HINT becomes "If this persists, check the LLM is reachable." — the "Try again —" prefix moves to the button (the text must no longer read as a fake control).
  • Non-turn callers (share failures L1857/L1870/L1892, save-doc L735/L742, stale L1620/L1950/L1962, …) pass no opts → text-only banner, no button — no behavior change for them.
  • CSS: .banner-retry in styles.css reuses the .stale-regenerate pill look (same component family); the banner keeps role="alert".
  • NOT touched: the per-answer Retry pill (phase 49), the stale banner, retryLastTurn itself, the server, and the RAG/document views (no #kb-banner there — no split needed, ASSUMPTION in task 01).

Tasks

  1. 01_banner_retry_button.md — banner Retry button markup + handler + hint-copy fix.
  2. 02_banner_retry_tests.md — frontend unit tests for the button's presence/handler + stale hint-copy assertions updated.

Testing & Quality

  • Unit: tests/unit/test_frontend_banner_retry.py (new, task 02) — house-style source assertions: #banner-retry exists in the #kb-banner markup (hidden by default, type="button"); showErrorBanner wires the click → retryLastTurn; the button is revealed only on the turn-error path; clearErrorBanner re-hides it; ERROR_HINT no longer starts with "Try again".
  • E2E: no new file — the turn-error path is exercised by the existing tests/e2e/test_llm_retry.py and tests/e2e/test_smoke.py suites, which must stay green (no banner behavior change for non-turn callers).
  • Coverage: >90% on app/ (validate.sh gate; the frontend JS is pinned by the source-assertion unit tests — no app/ code changes in this phase).

Completion Criteria

  • After a dropped/failed chat turn, #kb-banner shows a visible Retry button; clicking it re-runs the last question without re-typing.
  • Share/save-doc/stale-chat errors show a text-only banner (no button) — unchanged.
  • uv run pytest green; uv run pytest --cov=app --cov-report=term-missing TOTAL >90%; uv run ruff check . && uv run pyright clean.
  • One --no-gpg-sign commit; phase dir moved to .agents/phases/complete/ by the pipeline gate.

Locked decisions

  • A1 — Retry = re-ask the last question in place via the existing retryLastTurn (owner-confirmed 2026-09-14, roadmap confirmation). No new retry mechanism; the phase-49 redo-in-place is reused.
  • A2 — the button is offered only on the UI state-machine's turn-error path (a dropped/failed chat turn); all other banner callers stay text-only (owner-confirmed 2026-09-14). Matches L1's acceptance ("after a dropped/failed turn …").

Commit

git add frontend/ tests/ .agents/phases/ && git commit --no-gpg-sign -m "fix(ui): give the chat error banner a real Retry button that re-asks the last question"