# 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 `