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:
@@ -339,6 +339,8 @@ def test_minimal_message_still_validates() -> None:
|
||||
msg.thinking,
|
||||
msg.tools,
|
||||
msg.stopped,
|
||||
msg.failed,
|
||||
msg.error,
|
||||
) == (
|
||||
None,
|
||||
None,
|
||||
@@ -347,6 +349,8 @@ def test_minimal_message_still_validates() -> None:
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
@@ -373,6 +377,8 @@ def test_realistic_bor_chat_v1_payload_round_trips() -> None:
|
||||
"thinking": None,
|
||||
"tools": None,
|
||||
"stopped": None,
|
||||
"failed": None,
|
||||
"error": None,
|
||||
},
|
||||
{
|
||||
"who": "brain",
|
||||
@@ -411,6 +417,8 @@ def test_realistic_bor_chat_v1_payload_round_trips() -> None:
|
||||
},
|
||||
],
|
||||
"stopped": None,
|
||||
"failed": None,
|
||||
"error": None,
|
||||
},
|
||||
{
|
||||
"who": "user",
|
||||
@@ -422,6 +430,8 @@ def test_realistic_bor_chat_v1_payload_round_trips() -> None:
|
||||
"thinking": None,
|
||||
"tools": None,
|
||||
"stopped": None,
|
||||
"failed": None,
|
||||
"error": None,
|
||||
},
|
||||
{
|
||||
"who": "brain",
|
||||
@@ -433,6 +443,8 @@ def test_realistic_bor_chat_v1_payload_round_trips() -> None:
|
||||
"thinking": None,
|
||||
"tools": None,
|
||||
"stopped": True, # the owner stopped the generation mid-answer
|
||||
"failed": None,
|
||||
"error": None,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -459,6 +471,8 @@ def test_realistic_payload_round_trips_through_update_model() -> None:
|
||||
"thinking": "scratchpad",
|
||||
"tools": [_tool_call()],
|
||||
"stopped": None,
|
||||
"failed": None,
|
||||
"error": None,
|
||||
}
|
||||
payload = SavedChatUpdate.model_validate({"messages": [msg]})
|
||||
assert payload.model_dump()["messages"] == [msg]
|
||||
|
||||
Reference in New Issue
Block a user