diff --git a/runner/harness/card.py b/runner/harness/card.py index e57d517..21418dc 100644 --- a/runner/harness/card.py +++ b/runner/harness/card.py @@ -141,6 +141,43 @@ def _stage_rows(stages: list[dict]) -> str: return "\n".join(rows) or 'no stages' +# Friendly rung labels for the skip rows. +RUNG_LABEL = { + "install": "install", + "upgrade": "upgrade", + "backup_restore": "backup/restore", + "functional": "functional", + "integration": "integration", + "recipe_local": "recipe-local", +} +SKIP_GREEN = "#57ab5a" # muted green — an intentional skip reads like a pass (but labelled, never inflating) + + +def _skip_rows(skips: dict) -> str: + """Render SKIPPED rungs as stage-like rows. An intentional (declared) skip looks like a pass row + but its status says 'INTENTIONAL SKIP' (muted green) with the declared reason on the line below; + an unintentional skip is amber 'UNINTENTIONAL SKIP' with a prompt to add a test or declare it.""" + rows = [] + for rung, reason in (skips.get("intentional") or {}).items(): + rows.append( + f'' + f'{html.escape(RUNG_LABEL.get(rung, rung))}' + f'intentional skip' + ) + rows.append(f'{html.escape(reason)}') + for rung in skips.get("unintentional") or []: + rows.append( + f'' + f'{html.escape(RUNG_LABEL.get(rung, rung))}' + f'unintentional skip' + ) + rows.append( + 'not declared in EXPECTED_NA — add the ' + "missing test/label, or declare the skip with a reason" + ) + return "\n".join(rows) + + def render_card_html(data: dict, screenshot_rel: str | None = "screenshot.png") -> str: """Build the summary-card HTML from a results.json dict. `screenshot_rel` is the relative path to the screenshot PNG (same dir as the card) — omitted from the card if None / absent. @@ -151,15 +188,8 @@ def render_card_html(data: dict, screenshot_rel: str | None = "screenshot.png") version = html.escape(str(data.get("version") or data.get("ref") or "")) level = int(data.get("level", 0)) cap_reason = str(data.get("level_cap_reason") or "") - # Annotate the cap line by whether the capping rung was an intentional skip (declared, with its - # reason) or an unintentional one (skipped but not declared). - capped = data.get("level_cap_rung") - sk = data.get("skips", {}) or {} - if capped and capped in (sk.get("intentional") or {}): - cap_reason += f" · intentional: {sk['intentional'][capped]}" - elif capped and capped in (sk.get("unintentional") or []): - cap_reason += " · unintentional skip (no EXPECTED_NA — add a test or declare it)" cap = html.escape(cap_reason) + sk = data.get("skips", {}) or {} color = level_color(level) flags = data.get("flags", {}) or {} flag_bits = [] @@ -175,7 +205,7 @@ def render_card_html(data: dict, screenshot_rel: str | None = "screenshot.png") if show_shot else '
no screenshot
' ) - rows = _stage_rows(data.get("stages", [])) + rows = _stage_rows(data.get("stages", [])) + "\n" + _skip_rows(sk) return f"""