phase: 120_failed_turn_retry
All verification complete. Final report: **Phase 120 — Failed-turn retry: verification pass (all 3 tasks were done; final verification + 1 regression fix)** **Verified:** `ChatMessage.failed`/`error` (≤500, `extra="forbid"` intact); `finalizeFailedTurn` funnel on the 3 failure paths (catch-else, stream-drop guard, zero-frame fallback) with `failed: true` + capped detail + `markLastRetryable`; `appendFailedNote` restore branch (Save-as-doc/Tune excluded); `showErrorBanner`/`retryLastTurn` byte-pinned untouched; only the three paths persist `failed: true` (grep + unit pin); no test asserts the old broken behavior. **Defect found & fixed (rule 7):** a real navigate-away mid-turn let the browser's teardown fetch rejection (TypeError, not AbortError) leak into the failed funnel, persisting a phantom failed brain record — `test_sources_midstream_bug.py::test_no_orphan_brain_message_when_navigated_before_first_token` failed (2 `.msg` after reload) and violated the phase-20 navigate-away convention. Fixed: turn-scoped `leftThePage` flag (set unconditionally on `pagehide`, reset in `runTurn`) skips the funnel in the catch-else branch; pinned by new unit test `test_navigate_away_is_not_a_failed_turn`. No phase-overview/PLAN/todo/complete files touched; no commits made. **Gates (exact):** - `uv run pytest` → 2577 passed - `uv run pytest --cov=app --cov-report=term-missing` → TOTAL 4271 stmts, 99% (>90%) - `uv run pytest tests/e2e/test_failed_turn_retry.py -v --no-cov` → 4 passed (isolated) - `uv run ruff check . && uv run pyright` → clean (0 errors) - Regression E2E, isolated: `test_sources_midstream_bug.py` 6/6 (was 5/6); `test_llm_retry`/`test_tool_scaffolding_guardrails`/`test_stop_generation`/`test_navbar_refresh` 17/17 **Completion criteria:** (1) network error → banner + in-bubble Retry, re-ask without re-typing ✅ (E2E A); (2) refresh restores failed bubble + working Retry, no "new chat" ✅ (E2E C); (3) stopped/successful turns byte-identical ✅ (negative E2E, stop suite, byte-identity units); (4) pytest/coverage/lint/types ✅; (5) commit + phase move — left to the harness per pass rules. **Notable:** deviation = the regression fix above (a navigation is not a failed turn; phase-20 partial-persist convention restored). Next pending phase: `121_git_source_tokens`.
This commit is contained in:
@@ -898,6 +898,39 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
|
||||
}
|
||||
.stopped-note svg { width: 10px; height: 10px; display: block; fill: currentColor; }
|
||||
|
||||
/* Phase 120 (TODO.md L3–4): the "Failed" note in a failed brain
|
||||
bubble's meta row — the .stopped-note family in the theme's error
|
||||
color (text + color, never color alone — B5: the "Failed" label
|
||||
carries the meaning, the triangle glyph is aria-hidden decoration in
|
||||
the JS). --err-ink on the --surface bubble ≈9.0:1 (the same value as
|
||||
--brand-ink, computed 9.0:1 on --surface above) — well past 4.5:1
|
||||
(WCAG 2.1 AA); the monochrome theme grays it automatically (token,
|
||||
not literal — phase-92 zero-literal). Non-interactive — no hover, no
|
||||
focus (pointer-events: none), the stopped note's way. The detail
|
||||
span WRAPS (the stopped note's nowrap fits a one-word label; a 500-
|
||||
char error detail must not blow out the 46rem chat column) —
|
||||
overflow-wrap: anywhere keeps long unbroken details inside the row. */
|
||||
.failed-note {
|
||||
display: inline-flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.3rem;
|
||||
max-width: 100%;
|
||||
color: var(--err-ink);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
pointer-events: none;
|
||||
}
|
||||
.failed-note svg {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-top: 0.15rem;
|
||||
display: block;
|
||||
flex: none;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
}
|
||||
.failed-detail { font-weight: 400; overflow-wrap: anywhere; }
|
||||
|
||||
/* Inline tuning form under the bubble: labeled textarea + Save/Cancel
|
||||
(both ≥44px). Save = brand button (dark ink 5.2:1), Cancel = ghost. */
|
||||
.tune-form {
|
||||
|
||||
Reference in New Issue
Block a user