From a78c1e971b2ccbed562039ad58c75d11e14336cc Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Fri, 23 Oct 2026 21:44:12 +0200 Subject: [PATCH] relay: a forced step 2 is not a try (attempts 4 -> 3) phase.step2_attempts counted every step-2 grade with grade "fail", and a force repeats the last try's grade, so three named-a-tool fails plus "Continue anyway" showed attempts: 4 in the state. Measured against the live relay through the app's desktop path; the contract (and the snapshot's _coach_grades) already exclude forces. Co-Authored-By: Claude Opus 5.5 --- leetcode_coach/leetcode_coach/phase.py | 9 +++++++-- leetcode_coach/leetcode_coach/tests/test_app_events.py | 10 ++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/leetcode_coach/leetcode_coach/phase.py b/leetcode_coach/leetcode_coach/phase.py index 3a196fb..627d590 100644 --- a/leetcode_coach/leetcode_coach/phase.py +++ b/leetcode_coach/leetcode_coach/phase.py @@ -102,9 +102,14 @@ def step2_attempts(events: list[dict[str, Any]]) -> int: A ``fail`` (the list read as code, or named an operation) never reaches the checker, so it has no ``req_check``; a ``partial`` does, and is - already counted there. CLI and app count one file alike. + already counted there. CLI and app count one file alike. A force + repeats the last try's grade but is not a try, so it is not counted. """ - fails = sum(1 for e in _step2_grades(events) if e.get("grade") == "fail") + fails = sum( + 1 + for e in _step2_grades(events) + if e.get("grade") == "fail" and e.get("forced") is not True + ) return req_state(events).tries + fails diff --git a/leetcode_coach/leetcode_coach/tests/test_app_events.py b/leetcode_coach/leetcode_coach/tests/test_app_events.py index 47b75fa..a547f3d 100644 --- a/leetcode_coach/leetcode_coach/tests/test_app_events.py +++ b/leetcode_coach/leetcode_coach/tests/test_app_events.py @@ -84,6 +84,16 @@ def test_step_two_attempts_count_incomplete_lists_and_fail_grades() -> None: assert phase.step2_attempts(events) == 3 +def test_a_force_is_not_a_step_two_try() -> None: + """A force repeats the last try's grade (`fail`); it is no try itself. + + Measured against the live relay 2026-10-23: three named-a-tool fails and + a force showed ``attempts: 4`` in the state. + """ + fails = [_grade(2, "fail"), _grade(2, "fail"), _grade(2, "fail")] + assert phase.step2_attempts([*fails, _grade(2, "fail", forced=True)]) == 3 + + def _gate(log: Path, answers: list[bool] | None = None) -> phase.PhaseGate: asked: list[str] = []