From 0a1a6b57816e498e2565add895d0e07bed5658d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20=22decko=22=20de=20Brito?= Date: Tue, 26 May 2026 20:57:12 -0300 Subject: [PATCH 1/3] =?UTF-8?q?test(325):=20add=20failing=20tests=20for=20?= =?UTF-8?q?verdict=C3=97status=20dot-color=20combinations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests to TestPhaseTimelineDotColoring covering all verdict×status combinations described in the plan: - verify PASS + completed → green dot - verify FAIL + completed → red dot (currently fails — bug) - verify PASS + failed/skipped/superseded → status wins - review approve/pass/pass-with-follow-ups → green dot - review REWORK + completed → yellow dot (currently fails — bug) - review FAIL + completed → red dot (currently fails — bug) - review REWORK + failed → status wins (red) - non-verify/review phase gen>1 preserves rework logic Also extend _make_report_with_phases helper to accept output_structured per phase entry. --- tests/test_report_html.py | 252 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) diff --git a/tests/test_report_html.py b/tests/test_report_html.py index a7df144..fb7d90e 100644 --- a/tests/test_report_html.py +++ b/tests/test_report_html.py @@ -2934,6 +2934,7 @@ def _make_report_with_phases(self, phases: list): generation=ph["generation"], status=ph["status"], output="done", + output_structured=ph.get("output_structured"), ) for ph in phases ] @@ -3025,6 +3026,257 @@ def test_superseded_phase_css_rule_defined(self, tmp_path: Path) -> None: # The CSS selector must be defined so the dot is actually styled. assert ".phase-status-superseded" in content + # --- Ticket #325: dot color should reflect verdict, not execution status --- + + def test_verify_pass_verdict_completed_gives_green_dot(self, tmp_path: Path) -> None: + """verify phase with verdict=PASS and status=completed should get green dot.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "verify", + "generation": 1, + "status": "completed", + "output_structured": {"verdict": "PASS"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-completed"' in content + assert 'class="phase-status phase-status-failed"' not in content + assert 'class="phase-status phase-status-rework"' not in content + + def test_verify_fail_verdict_completed_gives_red_dot(self, tmp_path: Path) -> None: + """verify phase with verdict=FAIL and status=completed should get red dot.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "verify", + "generation": 1, + "status": "completed", + "output_structured": {"verdict": "FAIL"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-failed"' in content + assert 'class="phase-status phase-status-completed"' not in content + assert 'class="phase-status phase-status-rework"' not in content + + def test_verify_pass_verdict_but_failed_status_gives_red_dot(self, tmp_path: Path) -> None: + """failed status takes priority over PASS verdict for verify phase.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "verify", + "generation": 1, + "status": "failed", + "output_structured": {"verdict": "PASS"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-failed"' in content + assert 'class="phase-status phase-status-completed"' not in content + + def test_verify_pass_verdict_but_skipped_status_gives_skipped_dot(self, tmp_path: Path) -> None: + """skipped status takes priority over PASS verdict for verify phase.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "verify", + "generation": 1, + "status": "skipped", + "output_structured": {"verdict": "PASS"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-skipped"' in content + assert 'class="phase-status phase-status-completed"' not in content + + def test_verify_pass_verdict_but_superseded_status_gives_superseded_dot( + self, tmp_path: Path + ) -> None: + """superseded status takes priority over PASS verdict for verify phase.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "verify", + "generation": 1, + "status": "superseded", + "output_structured": {"verdict": "PASS"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-superseded"' in content + assert 'class="phase-status phase-status-completed"' not in content + + def test_review_approve_verdict_gives_green_dot(self, tmp_path: Path) -> None: + """review phase with verdict=approve and status=completed should get green dot.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "review", + "generation": 1, + "status": "completed", + "output_structured": {"verdict": "approve"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-completed"' in content + assert 'class="phase-status phase-status-failed"' not in content + assert 'class="phase-status phase-status-rework"' not in content + + def test_review_pass_verdict_gives_green_dot(self, tmp_path: Path) -> None: + """review phase with verdict=pass and status=completed should get green dot.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "review", + "generation": 1, + "status": "completed", + "output_structured": {"verdict": "pass"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-completed"' in content + assert 'class="phase-status phase-status-failed"' not in content + assert 'class="phase-status phase-status-rework"' not in content + + def test_review_pass_with_follow_ups_verdict_gives_green_dot(self, tmp_path: Path) -> None: + """review phase with verdict=pass-with-follow-ups should get green dot.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "review", + "generation": 1, + "status": "completed", + "output_structured": {"verdict": "pass-with-follow-ups"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-completed"' in content + assert 'class="phase-status phase-status-failed"' not in content + assert 'class="phase-status phase-status-rework"' not in content + + def test_review_rework_verdict_gives_yellow_dot(self, tmp_path: Path) -> None: + """review phase with verdict=REWORK and status=completed should get yellow rework dot.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "review", + "generation": 1, + "status": "completed", + "output_structured": {"verdict": "REWORK"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-rework"' in content + assert 'class="phase-status phase-status-completed"' not in content + assert 'class="phase-status phase-status-failed"' not in content + + def test_review_fail_verdict_gives_red_dot(self, tmp_path: Path) -> None: + """review phase with verdict=FAIL and status=completed should get red dot.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "review", + "generation": 1, + "status": "completed", + "output_structured": {"verdict": "FAIL"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-failed"' in content + assert 'class="phase-status phase-status-completed"' not in content + assert 'class="phase-status phase-status-rework"' not in content + + def test_review_rework_verdict_but_failed_status_gives_red_dot(self, tmp_path: Path) -> None: + """failed status takes priority over REWORK verdict for review phase.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "review", + "generation": 1, + "status": "failed", + "output_structured": {"verdict": "REWORK"}, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-failed"' in content + assert 'class="phase-status phase-status-rework"' not in content + + def test_non_verify_review_phase_uses_generation_logic(self, tmp_path: Path) -> None: + """implement phase with gen=1 should still use existing green/rework logic.""" + from raki.report.html_report import write_html_report + + report = self._make_report_with_phases( + [ + { + "name": "implement", + "generation": 2, + "status": "completed", + "output_structured": None, + } + ] + ) + output = tmp_path / "report.html" + write_html_report(report, output, include_sessions=True) + content = output.read_text() + assert 'class="phase-status phase-status-rework"' in content + assert 'class="phase-status phase-status-completed"' not in content + # --- Ticket #250: Structured drill-down sections --- From dc774bdd600c14f829962cc05b41bcf08db1f11c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20=22decko=22=20de=20Brito?= Date: Tue, 26 May 2026 20:57:41 -0300 Subject: [PATCH 2/3] fix(325): phase status dot color reflects verdict for verify/review phases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the single-line dot_class expression (line 1384) with a multi-branch block that checks verdict from output_structured before falling back to the existing generation-based logic. Priority rules: - failed/skipped/superseded status always wins over any verdict - verify phases: PASS verdict → green (completed), else → red (failed) - review phases: approve/pass/pass-with-follow-ups → green, REWORK → yellow (rework), else → red (failed) - all other phases: gen>1 completed → yellow (rework), else status All CSS classes (phase-status-completed, phase-status-rework, phase-status-failed, phase-status-skipped, phase-status-superseded) already existed — no style changes required. --- src/raki/report/templates/report.html.j2 | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/raki/report/templates/report.html.j2 b/src/raki/report/templates/report.html.j2 index 59f4a73..100284d 100644 --- a/src/raki/report/templates/report.html.j2 +++ b/src/raki/report/templates/report.html.j2 @@ -1381,7 +1381,29 @@ {% if sorted_phases %}
{% for phase in sorted_phases %} - {% set dot_class = 'rework' if phase.status == 'completed' and phase.generation > 1 else phase.status %} + {# Compute dot_class: failed/skipped/superseded status always wins; for verify/review + phases with structured output, use verdict to pick the color; otherwise fall back to + the existing gen>1 → rework logic. #} + {% if phase.status in ('failed', 'skipped', 'superseded') %} + {% set dot_class = phase.status %} + {% elif phase.output_structured and phase.output_structured is mapping + and phase.output_structured.verdict is defined + and phase.name == 'verify' %} + {% set dot_class = 'completed' if phase.output_structured.verdict | lower == 'pass' else 'failed' %} + {% elif phase.output_structured and phase.output_structured is mapping + and phase.output_structured.verdict is defined + and phase.name == 'review' %} + {% set verdict_lc = phase.output_structured.verdict | lower %} + {% if verdict_lc in ('approve', 'pass', 'pass-with-follow-ups') %} + {% set dot_class = 'completed' %} + {% elif verdict_lc == 'rework' %} + {% set dot_class = 'rework' %} + {% else %} + {% set dot_class = 'failed' %} + {% endif %} + {% else %} + {% set dot_class = 'rework' if phase.status == 'completed' and phase.generation > 1 else phase.status %} + {% endif %}
{{ phase.name }} (gen {{ phase.generation }}) From 6ebcaadf65300fe5459d580e1172be478df76e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20=22decko=22=20de=20Brito?= Date: Tue, 26 May 2026 20:57:56 -0300 Subject: [PATCH 3/3] chore(325): add towncrier fragment for phase dot color verdict fix --- changes/325.fix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/325.fix diff --git a/changes/325.fix b/changes/325.fix new file mode 100644 index 0000000..cec83f8 --- /dev/null +++ b/changes/325.fix @@ -0,0 +1 @@ +Phase status dots in the HTML report now reflect the structured verdict for verify and review phases. A verify phase with verdict ``FAIL`` shows a red dot even when its execution status is ``completed``; a review phase with verdict ``REWORK`` shows a yellow dot; and ``approve``/``pass``/``pass-with-follow-ups`` show a green dot. Hard execution failures (``failed``, ``skipped``, ``superseded``) still take priority over any verdict.