Files
ldm0 c461cb1799 refactor(benchmark): unify public web comparisons
Move public-web seeds into CSV fixtures and share scheduling, command construction, classification, evidence, and artifact handling across top-sites and wild-web suites.

Bind Chromium DCL and response evidence to the current navigation loader, surface navigation errors promptly, rotate target-first order across repeated runs, and normalize HTTP and timeout outcomes so cross-engine results remain comparable.
2026-08-23 04:45:50 +08:00

2270 lines
85 KiB
Python

from __future__ import annotations
import html
import json
from pathlib import Path
from typing import Any
from .artifacts import write_json, write_text
def _cell(value: Any) -> str:
if value is None:
return ""
if isinstance(value, float):
return f"{value:.2f}"
return html.escape(str(value))
def _link(path: str, label: str | None = None) -> str:
return f'<a href="{html.escape(path)}">{html.escape(label or path)}</a>'
def _num(value: Any) -> float | None:
if value is None:
return None
try:
return float(value)
except (TypeError, ValueError):
return None
def _status(ok: bool) -> str:
return '<span class="ok">PASS</span>' if ok else '<span class="fail">FAIL</span>'
def _status_badge(status: str) -> str:
classes = {
"PASS": "badge pass",
"FAIL": "badge fail",
"PARTIAL": "badge warn",
"NOT RUN": "badge neutral",
}
return f'<span class="{classes.get(status, "badge neutral")}">{html.escape(status)}</span>'
def _mib(value: Any) -> str:
if value is None:
return ""
try:
return f"{float(value) / (1024 * 1024):.1f} MiB"
except (TypeError, ValueError):
return _cell(value)
def _size(value: Any) -> str:
if value is None:
return ""
try:
raw = float(value)
except (TypeError, ValueError):
return _cell(value)
if raw >= 1024 * 1024:
return f"{raw / (1024 * 1024):.1f} MiB"
if raw >= 1024:
return f"{raw / 1024:.1f} KiB"
return f"{int(raw)} B"
def _target_names(targets: dict[str, Any]) -> list[str]:
preferred = ["moli", "lightpanda", "chrome", "obscura"]
names = [name for name in preferred if name in targets]
names.extend(name for name in targets if name not in names)
return names
def _summary_by_suite(summaries: list[dict[str, Any]]) -> dict[str, dict[str, Any]]:
return {str(summary.get("suite")): summary for summary in summaries if summary.get("suite")}
def _suite_failures(summary: dict[str, Any] | None) -> int | None:
if not summary:
return None
value = summary.get("gate_failures", summary.get("total_failures"))
try:
return int(value or 0)
except (TypeError, ValueError):
return None
def _suite_status(summary: dict[str, Any] | None) -> str:
failures = _suite_failures(summary)
if failures is None:
return "NOT RUN"
return "PASS" if failures == 0 else "FAIL"
def _synthetic_status(by_suite: dict[str, dict[str, Any]]) -> tuple[str, dict[str, Any] | None]:
matrix = by_suite.get("synthetic-matrix")
compare = by_suite.get("synthetic-compare")
synthetic = matrix or compare
failures = _suite_failures(synthetic)
if failures is None:
return "NOT RUN", None
if failures > 0:
return "FAIL", synthetic
if matrix and matrix.get("profile") == "formal":
return "PASS", matrix
return "PARTIAL", synthetic
def _amiibo_status(summary: dict[str, Any] | None) -> str:
failures = _suite_failures(summary)
if failures is None:
return "NOT RUN"
if failures > 0:
return "FAIL"
return "PASS" if summary and summary.get("profile") == "formal" else "PARTIAL"
def _p0_rows(summaries: list[dict[str, Any]]) -> list[tuple[str, str, dict[str, Any] | None, str]]:
by_suite = _summary_by_suite(summaries)
synthetic_status, synthetic_summary = _synthetic_status(by_suite)
return [
(synthetic_status, "Synthetic", synthetic_summary, "Formal local fixture matrix, correctness markers, stability drift."),
(_suite_status(by_suite.get("startup")), "Startup / Size", by_suite.get("startup"), "Serve ready, binary size, optional CDP first page and idle footprint."),
(_suite_status(by_suite.get("cdp-smoke") or by_suite.get("cdp-session")), "CDP", by_suite.get("cdp-smoke") or by_suite.get("cdp-session"), "Raw CDP session and client smoke coverage."),
(_suite_status(by_suite.get("wpt")), "WPT", by_suite.get("wpt"), "Local WPT compat report archival and gate result."),
(_amiibo_status(by_suite.get("amiibo-crawler")), "Amiibo Crawler", by_suite.get("amiibo-crawler"), "Lightpanda demo crawler workload."),
(_suite_status(by_suite.get("wild-web")), "Wild Web", by_suite.get("wild-web"), "Real-site seed classification and extraction readiness."),
]
def _overall_p0_status(summaries: list[dict[str, Any]]) -> str:
if not summaries:
return "NOT RUN"
statuses = [status for status, _, _, _ in _p0_rows(summaries)]
if any(status == "FAIL" for status in statuses):
return "FAIL"
if all(status == "PASS" for status in statuses):
return "PASS"
return "PARTIAL"
def _target_count(versions: dict[str, Any]) -> int:
targets = versions.get("targets") or {}
return sum(1 for target in targets.values() if isinstance(target, dict) and target.get("available"))
def _kpi_cards(versions: dict[str, Any], summaries: list[dict[str, Any]]) -> str:
suite_count = len(summaries)
total_gate_failures = sum(_suite_failures(summary) or 0 for summary in summaries)
target_count = _target_count(versions)
overall = _overall_p0_status(summaries)
cards = [
("P0 Gate", _status_badge(overall), "Overall scorecard status across required benchmark areas."),
("Suites Run", str(suite_count), "Benchmark suites with summary data."),
("Gate Failures", str(total_gate_failures), "Selected gate target failures plus profile failures."),
("Targets Available", str(target_count), "Moli / Lightpanda / Chrome / Obscura binaries detected."),
]
return "".join(
"<section class=\"kpi\">"
f"<div class=\"kpi-label\">{html.escape(label)}</div>"
f"<div class=\"kpi-value\">{value}</div>"
f"<div class=\"kpi-note\">{html.escape(note)}</div>"
"</section>"
for label, value, note in cards
)
def _p0_scorecard(summaries: list[dict[str, Any]]) -> str:
body = []
for status, area, summary, requirement in _p0_rows(summaries):
failures = _suite_failures(summary)
suite_name = summary.get("suite") if summary else ""
body.append(
"<tr>"
f"<th>{html.escape(area)}</th>"
f"<td>{_status_badge(status)}</td>"
f"<td>{_cell(suite_name)}</td>"
f"<td>{_cell(failures)}</td>"
f"<td>{html.escape(requirement)}</td>"
"</tr>"
)
return (
"<table class=\"scorecard\">"
"<thead><tr><th>area</th><th>status</th><th>source suite</th><th>failures</th><th>P0 requirement</th></tr></thead>"
f"<tbody>{''.join(body)}</tbody>"
"</table>"
)
def _target_versions(versions: dict[str, Any]) -> str:
targets = versions.get("targets") or {}
rows = []
for name in ("moli", "lightpanda", "chrome", "obscura"):
target = targets.get(name, {})
rows.append(
"<tr>"
f"<th>{html.escape(name)}</th>"
f"<td>{_cell(target.get('path'))}</td>"
f"<td>{_cell(target.get('version'))}</td>"
f"<td>{_cell(target.get('size_bytes'))}</td>"
f"<td><code>{_cell(target.get('sha256'))}</code></td>"
"</tr>"
)
return "\n".join(rows)
def _int_value(value: Any) -> int:
try:
return int(value or 0)
except (TypeError, ValueError):
return 0
def _cdp_session_trace_events(summary: dict[str, Any] | None) -> int:
if not isinstance(summary, dict):
return 0
explicit = _int_value(summary.get("total_trace_events"))
if explicit:
return explicit
total = 0
targets = summary.get("targets")
if not isinstance(targets, dict):
return total
for target in targets.values():
if not isinstance(target, dict):
continue
for key in ("console_errors", "js_exceptions", "network_failures"):
total += _int_value(target.get(key))
return total
def _artifact_paths_by_suite(summaries: list[dict[str, Any]]) -> dict[str, tuple[str, ...]]:
by_suite = {str(summary.get("suite")): summary for summary in summaries if summary.get("suite") is not None}
known = {
"startup": (
"startup/summary.json",
"startup/gate-rows.json",
"startup/runs.csv",
"startup/runs.json",
"startup/time/",
"startup/cgroup/",
"startup/image-size/",
),
"synthetic": ("synthetic/summary.json", "synthetic/runs.csv"),
"synthetic-matrix": ("synthetic-matrix/summary.json", "synthetic-matrix/matrix.csv"),
"synthetic-compare": ("synthetic-compare/summary.json", "synthetic-compare/runs.csv"),
"cdp-session": ("cdp-session/summary.json", "cdp-session/runs.csv"),
"crawler": ("crawler/summary.json", "crawler/raw-runs.csv"),
"amiibo-crawler": ("amiibo-crawler/summary.json", "amiibo-crawler/raw-runs.csv"),
"wild-web": ("wild-web/summary.json", "wild-web/raw-runs.csv"),
"top-sites": (
"top-sites/summary.json",
"top-sites/raw-runs.csv",
"top-sites/site-outcomes.json",
),
"render-compare": (
"render-compare/summary.json",
"render-compare/raw-runs.csv",
"render-compare/runs.json",
"render-compare/baseline-sites.csv",
"render-compare/baseline-sites.json",
"render-compare/baseline-runs.json",
),
"wpt": ("wpt/summary.json", "wpt/moli-wpt-compat-report.json", "wpt/raw-runs.csv", "wpt/by-tag.csv"),
"cdp-smoke": ("cdp-smoke/summary.json", "cdp-smoke/moli-cdp-smoke.json"),
}
startup = by_suite.get("startup")
if isinstance(startup, dict) and startup.get("cache_artifacts"):
known["startup"] = (*known["startup"], "startup/cache/")
cdp_session = by_suite.get("cdp-session")
if isinstance(cdp_session, dict) and (
_int_value(cdp_session.get("total_failures")) or _cdp_session_trace_events(cdp_session)
):
known["cdp-session"] = (*known["cdp-session"], "cdp-session/traces/")
wild_web = by_suite.get("wild-web")
if isinstance(wild_web, dict):
if _int_value(wild_web.get("total_failures")):
known["wild-web"] = (*known["wild-web"], "wild-web/failures/")
if _int_value(wild_web.get("replay_artifacts")):
known["wild-web"] = (*known["wild-web"], "wild-web/replay/manifest.json")
top_sites = by_suite.get("top-sites")
if isinstance(top_sites, dict) and _int_value(top_sites.get("total_failures")):
known["top-sites"] = (*known["top-sites"], "top-sites/failures/")
render_compare = by_suite.get("render-compare")
if isinstance(render_compare, dict) and _int_value(render_compare.get("total_failures")):
known["render-compare"] = (*known["render-compare"], "render-compare/failures/")
wpt = by_suite.get("wpt")
if isinstance(wpt, dict) and wpt.get("diff") is not None:
known["wpt"] = (*known["wpt"], "wpt/diff.json", "wpt/diff.csv")
return known
def _artifact_index(summaries: list[dict[str, Any]]) -> str:
known = _artifact_paths_by_suite(summaries)
rows = []
for summary in summaries:
suite = str(summary.get("suite"))
paths = known.get(suite, ())
rows.append(
"<tr>"
f"<th>{html.escape(suite)}</th>"
f"<td>{' · '.join(_link(path) for path in paths)}</td>"
"</tr>"
)
return (
"<table>"
"<thead><tr><th>suite</th><th>artifacts</th></tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _horizontal_comparisons(summaries: list[dict[str, Any]]) -> list[dict[str, Any]]:
by_suite = _summary_by_suite(summaries)
comparisons: list[dict[str, Any]] = []
synthetic_compare = by_suite.get("synthetic-compare")
cdp_session = by_suite.get("cdp-session")
if synthetic_compare and cdp_session:
synthetic_cases = [str(case) for case in synthetic_compare.get("cases", [])]
cdp_cases = {str(case) for case in cdp_session.get("cases", [])}
cases = [case for case in synthetic_cases if case in cdp_cases]
targets: dict[str, Any] = {}
for source in (synthetic_compare, cdp_session):
source_targets = source.get("targets", {})
if not isinstance(source_targets, dict):
continue
for target, target_summary in source_targets.items():
if not isinstance(target_summary, dict):
continue
source_cases = target_summary.get("cases", {})
if not isinstance(source_cases, dict):
continue
selected_cases = {case: source_cases[case] for case in cases if case in source_cases}
if selected_cases:
targets[str(target)] = {
**target_summary,
"cases": selected_cases,
}
if cases and targets:
comparisons.append(
{
"suite": "web-scraping-variants",
"sources": ["synthetic-compare", "cdp-session"],
"gate_target": synthetic_compare.get("gate_target", "moli"),
"gate_failures": synthetic_compare.get("gate_failures", 0),
"total_failures": sum(int(target.get("failures", 0) or 0) for target in targets.values()),
"cases": cases,
"targets": targets,
"note": "Derived comparison that joins fetch-style and CDP session variants for shared synthetic cases.",
}
)
for summary in summaries:
if isinstance(summary.get("targets"), dict) and summary.get("cases"):
comparisons.append(summary)
return comparisons
def _read_json_artifact(path: Path) -> Any:
try:
return json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return None
def _render_compare_payload(output_dir: Path) -> dict[str, Any] | None:
suite_dir = output_dir / "render-compare"
runs = _read_json_artifact(suite_dir / "runs.json")
baseline_sites = _read_json_artifact(suite_dir / "baseline-sites.json")
if not isinstance(runs, list) and not isinstance(baseline_sites, list):
return None
run_fields = (
"target",
"engine",
"driver",
"label",
"rank",
"domain",
"url",
"baseline_target",
"baseline_category",
"target_fetch_category",
"category",
"ok",
"excluded",
"elapsed_ms",
"baseline_elapsed_ms",
"peak_pss_bytes",
"peak_rss_bytes",
"render_quality_score",
"raw_content_score",
"ngram_containment",
"raw_ngram_containment",
"key_phrase_hit_rate",
"raw_key_phrase_hit_rate",
"visible_text_ratio",
"baseline_visible_text_length",
"target_visible_text_length",
"failure_artifact",
)
baseline_fields = (
"rank",
"domain",
"url",
"baseline_target",
"baseline_fetch_category",
"category",
"usable",
"baseline_title",
"baseline_visible_text_length",
"baseline_elapsed_ms",
"baseline_peak_pss_bytes",
"baseline_peak_rss_bytes",
)
slim_runs = [
{field: row.get(field) for field in run_fields if field in row}
for row in runs
if isinstance(row, dict)
] if isinstance(runs, list) else []
slim_baselines = [
{field: row.get(field) for field in baseline_fields if field in row}
for row in baseline_sites
if isinstance(row, dict)
] if isinstance(baseline_sites, list) else []
return {
"runs": slim_runs,
"baseline_sites": slim_baselines,
"run_count": len(slim_runs),
"baseline_site_count": len(slim_baselines),
}
def _report_payload(
*,
output_dir: Path,
versions: dict[str, Any],
summaries: list[dict[str, Any]],
publish_readiness: dict[str, Any] | None,
report_diff: dict[str, Any] | None,
) -> dict[str, Any]:
all_artifact_paths = _artifact_paths_by_suite(summaries)
present_suites = [str(summary.get("suite")) for summary in summaries if summary.get("suite") is not None]
return {
"schema_version": 1,
"output_dir": str(output_dir),
"versions": versions,
"summaries": summaries,
"horizontal_comparisons": _horizontal_comparisons(summaries),
"render_compare": _render_compare_payload(output_dir),
"publish_readiness": publish_readiness,
"report_diff": report_diff,
"artifact_paths_by_suite": {suite: all_artifact_paths.get(suite, ()) for suite in present_suites},
}
def _json_script(payload: dict[str, Any]) -> str:
return json.dumps(payload, ensure_ascii=False, sort_keys=True).replace("</", "<\\/")
def _chartjs_document(payload: dict[str, Any]) -> str:
data = _json_script(payload)
document = """<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Moli Benchmark Report</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.9/dist/chart.umd.min.js"></script>
<style>
:root {
color-scheme: light;
--page: #f6f7f9;
--panel: #fff;
--ink: #111827;
--muted: #64748b;
--soft: #f8fafc;
--line: #d8dee8;
--line-strong: #aeb8c6;
--blue: #1f5aa6;
--teal: #287c74;
--amber: #a16618;
--violet: #6d4fc2;
--green: #177245;
--red: #b42318;
--yellow: #935a00;
--green-bg: #eaf7ef;
--red-bg: #fdecec;
--yellow-bg: #fff4d8;
--neutral-bg: #eef2f7;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
background: var(--page);
color: var(--ink);
font: 14px/1.45 Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
overflow-x: hidden;
}
.topbar {
position: sticky;
top: 0;
z-index: 10;
border-bottom: 1px solid var(--line);
background: rgba(255, 255, 255, 0.94);
backdrop-filter: blur(10px);
}
.topbar-inner {
max-width: 1220px;
margin: 0 auto;
padding: 12px 24px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
}
.brand {
display: flex;
flex-direction: column;
gap: 2px;
font-weight: 760;
}
.brand small {
color: var(--muted);
font-weight: 520;
}
.nav {
display: flex;
flex-wrap: wrap;
gap: 14px;
}
.nav a {
color: var(--muted);
text-decoration: none;
font-size: 13px;
}
.nav a:hover {
color: var(--ink);
}
main {
max-width: 1220px;
margin: 0 auto;
padding: 28px 24px 56px;
min-width: 0;
}
h1, h2, h3 {
margin: 0;
letter-spacing: 0;
}
h1 {
font-size: 31px;
line-height: 1.12;
}
h2 {
font-size: 18px;
margin-bottom: 12px;
}
h3 {
font-size: 15px;
}
.muted {
color: var(--muted);
}
.hero {
display: grid;
grid-template-columns: minmax(0, 1fr) max-content;
gap: 18px;
align-items: start;
margin: 6px 0 18px;
}
.eyebrow {
color: var(--muted);
font-size: 12px;
font-weight: 700;
letter-spacing: .08em;
text-transform: uppercase;
}
.hero p {
max-width: 820px;
margin: 8px 0 0;
}
.badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 68px;
border-radius: 999px;
padding: 4px 9px;
font-size: 12px;
font-weight: 750;
}
.badge.pass {
color: var(--green);
background: var(--green-bg);
}
.badge.fail {
color: var(--red);
background: var(--red-bg);
}
.badge.warn {
color: var(--yellow);
background: var(--yellow-bg);
}
.badge.neutral {
color: var(--muted);
background: var(--neutral-bg);
}
.status-tooltip {
position: relative;
display: inline-flex;
}
.status-tooltip[data-tooltip]:hover::after,
.status-tooltip[data-tooltip]:focus::after {
content: attr(data-tooltip);
position: absolute;
left: 0;
top: calc(100% + 8px);
z-index: 30;
width: max-content;
max-width: min(520px, 70vw);
padding: 10px 12px;
border: 1px solid #cbd5e1;
border-radius: 8px;
background: #0f172a;
color: #f8fafc;
box-shadow: 0 18px 44px rgba(15, 23, 42, .22);
font-size: 12px;
font-weight: 500;
line-height: 1.45;
white-space: normal;
overflow-wrap: anywhere;
text-transform: none;
pointer-events: none;
}
.grid {
display: grid;
gap: 12px;
}
.kpis {
grid-template-columns: repeat(4, minmax(0, 1fr));
margin: 16px 0;
}
.two {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.three {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.panel {
margin: 12px 0;
padding: 16px;
border: 1px solid var(--line);
border-radius: 8px;
background: var(--panel);
min-width: 0;
}
.panel.compact {
padding: 14px;
}
.panel.wide {
grid-column: 1 / -1;
}
.takeaway-grid {
display: grid;
grid-template-columns: 1.2fr .8fr;
gap: 12px;
}
.stat {
padding: 14px;
border: 1px solid var(--line);
border-radius: 8px;
background: var(--panel);
}
.stat-label {
color: var(--muted);
font-size: 12px;
text-transform: uppercase;
}
.stat-value {
margin-top: 6px;
font-size: 24px;
font-weight: 780;
}
.stat-note {
margin-top: 3px;
color: var(--muted);
font-size: 12px;
}
.target-card {
border: 1px solid var(--line);
border-radius: 8px;
padding: 13px;
background: var(--soft);
}
.target-card h3 {
display: flex;
justify-content: space-between;
gap: 10px;
margin-bottom: 8px;
}
.target-meta {
display: grid;
gap: 6px;
color: var(--muted);
font-size: 12px;
}
.chart-box {
height: 320px;
}
.failure-box {
height: 260px;
}
.page-chart-box {
height: 420px;
}
canvas {
width: 100%;
max-width: 100%;
height: 250px;
}
.table-wrap {
width: 100%;
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
th, td {
padding: 9px 10px;
border-bottom: 1px solid var(--line);
text-align: left;
vertical-align: top;
overflow-wrap: anywhere;
}
thead th {
color: var(--muted);
background: var(--soft);
font-size: 12px;
text-transform: uppercase;
}
tbody tr:hover td, tbody tr:hover th {
background: #fafcff;
}
code {
font-size: 12px;
}
a {
color: var(--blue);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.pill-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.pill {
display: inline-flex;
align-items: center;
border-radius: 999px;
padding: 5px 9px;
background: var(--neutral-bg);
color: var(--muted);
font-size: 12px;
}
.notice {
border: 1px solid var(--line);
border-radius: 8px;
padding: 10px 12px;
background: var(--soft);
}
.finding-list {
display: grid;
gap: 9px;
}
.finding {
padding: 10px 12px;
border: 1px solid var(--line);
border-left: 4px solid var(--blue);
border-radius: 6px;
background: #fff;
}
.finding.warn {
border-left-color: var(--yellow);
}
.finding.fail {
border-left-color: var(--red);
}
.score-cell {
min-width: 112px;
}
.score-value {
display: flex;
justify-content: space-between;
gap: 10px;
font-variant-numeric: tabular-nums;
}
.score-track {
margin-top: 5px;
height: 7px;
border-radius: 999px;
background: #e6ebf1;
overflow: hidden;
}
.score-fill {
height: 100%;
border-radius: 999px;
background: var(--green);
}
.score-fill.warn {
background: var(--yellow);
}
.score-fill.fail {
background: var(--red);
}
.url-cell {
min-width: 260px;
}
.metric-tight {
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
#webPageTable table {
min-width: 1520px;
table-layout: auto;
}
@media (max-width: 900px) {
.topbar-inner {
align-items: flex-start;
flex-direction: column;
}
main {
padding: 20px 12px 40px;
}
.hero, .two, .three, .kpis, .takeaway-grid {
grid-template-columns: 1fr;
}
}
</style>
<body>
<header class="topbar">
<div class="topbar-inner">
<div class="brand">Moli Benchmark <small>rendered from report-data.json</small></div>
<nav class="nav">
<a href="#overview">Overview</a>
<a href="#findings">Findings</a>
<a href="#comparison">Comparison</a>
<a href="#web-pages">Web Pages</a>
<a href="#targets">Targets</a>
<a href="#readiness">Readiness</a>
<a href="#artifacts">Artifacts</a>
</nav>
</div>
</header>
<main>
<section id="overview" class="hero">
<div>
<div class="eyebrow">Benchmark report</div>
<h1>Benchmark Comparison Report</h1>
<p class="muted">Lower latency and memory are better. Correctness gates remain the primary publish condition.</p>
<p class="muted">Output: <code id="outputDir"></code></p>
</div>
<div id="publishBadge"></div>
</section>
<section class="grid kpis" id="kpis"></section>
<section id="findings" class="takeaway-grid">
<div class="panel compact">
<h2>Executive Summary</h2>
<div id="executiveSummary"></div>
</div>
<div class="panel compact">
<h2>Run Notes</h2>
<div id="runNotes"></div>
</div>
</section>
<section id="comparison" class="grid two">
<div class="panel">
<h2>Latency P50 by Case</h2>
<div class="chart-box"><canvas id="latencyChart"></canvas></div>
</div>
<div class="panel">
<h2>Memory PSS P50 by Case</h2>
<div class="chart-box"><canvas id="memoryChart"></canvas></div>
</div>
<div class="panel wide">
<h2>Memory RSS P50 by Case</h2>
<div class="chart-box"><canvas id="rssChart"></canvas></div>
</div>
</section>
<section class="panel">
<h2>Target Failure Counts</h2>
<div class="failure-box"><canvas id="failureChart"></canvas></div>
</section>
<section class="panel">
<h2>Horizontal Details</h2>
<div class="table-wrap" id="comparisonTable"></div>
</section>
<section id="web-pages" class="panel">
<h2>Web Page Request Scores</h2>
<div class="grid kpis" id="webPageKpis"></div>
<div class="grid two">
<div>
<h3>Lowest Render Quality Scores</h3>
<div class="page-chart-box"><canvas id="pageScoreChart"></canvas></div>
</div>
<div>
<h3>Slowest Page Requests</h3>
<div class="page-chart-box"><canvas id="pageLatencyChart"></canvas></div>
</div>
</div>
<div class="table-wrap" id="webPageTable"></div>
</section>
<section id="targets" class="panel">
<h2>Targets</h2>
<div class="grid three" id="targetCards"></div>
</section>
<section id="readiness" class="panel">
<h2>Publication Readiness</h2>
<div class="table-wrap" id="readinessTable"></div>
</section>
<section class="panel">
<h2>Suites</h2>
<div class="table-wrap" id="suiteTable"></div>
</section>
<section id="artifacts" class="panel">
<h2>Artifacts</h2>
<div class="table-wrap" id="artifactTable"></div>
</section>
<section class="panel">
<h2>Previous Report Diff</h2>
<div id="reportDiff"></div>
</section>
</main>
<script id="report-data" type="application/json">__REPORT_DATA__</script>
<script>
const report = JSON.parse(document.getElementById('report-data').textContent);
const targetOrder = ['moli', 'moli-cdp', 'moli-full', 'moli-full-cdp', 'lightpanda', 'lightpanda-cdp', 'chrome', 'chrome-cdp', 'obscura', 'obscura-cdp'];
const colors = {
moli: '#2356a5',
lightpanda: '#257a73',
chrome: '#9a6419',
obscura: '#6d4cc2',
};
function htmlEscape(value) {
return String(value ?? '').replace(/[&<>"']/g, ch => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[ch]));
}
function number(value, digits = 2) {
const n = Number(value);
return Number.isFinite(n) ? n.toFixed(digits) : '';
}
function mib(value) {
const n = Number(value);
return Number.isFinite(n) ? `${(n / 1024 / 1024).toFixed(1)} MiB` : '';
}
function size(value) {
const n = Number(value);
if (!Number.isFinite(n)) return '';
if (n >= 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MiB`;
if (n >= 1024) return `${(n / 1024).toFixed(1)} KiB`;
return `${Math.round(n)} B`;
}
function badge(status) {
const s = String(status || 'NOT RUN').toUpperCase();
const cls = s === 'PASS' || s === 'PUBLISHABLE' ? 'pass' : s === 'FAIL' ? 'fail' : s === 'INVESTIGATION' || s === 'PARTIAL' ? 'warn' : 'neutral';
return `<span class="badge ${cls}">${htmlEscape(s)}</span>`;
}
function statusBadge(status, tooltip) {
const rendered = badge(status);
if (!tooltip) return rendered;
return `<span class="status-tooltip" tabindex="0" data-tooltip="${htmlEscape(tooltip)}">${rendered}</span>`;
}
function suiteMap() {
return new Map((report.summaries || []).map(summary => [summary.suite, summary]));
}
function targetNames(targets) {
const names = Object.keys(targets || {});
return [...targetOrder.filter(name => names.includes(name)), ...names.filter(name => !targetOrder.includes(name))];
}
function targetMeta(targets, target) {
const meta = targets?.[target] || {};
return typeof meta === 'object' && meta ? meta : {};
}
function targetEngine(targets, target) {
return targetMeta(targets, target).engine || String(target).replace(/-cdp$/, '');
}
function targetLabel(targets, target) {
return targetMeta(targets, target).label || target;
}
function targetColor(targets, target) {
return colors[targetEngine(targets, target)] || '#64748b';
}
function horizontalSummaries() {
const comparisons = report.horizontal_comparisons || [];
const summaries = report.summaries || [];
const result = [...comparisons];
const seen = new Set(result.map(summary => summary.suite));
for (const summary of summaries) {
if (!summary?.targets || seen.has(summary.suite)) continue;
result.push(summary);
seen.add(summary.suite);
}
return result;
}
function firstHorizontalSummary() {
const summaries = horizontalSummaries();
for (const suite of ['web-scraping-variants', 'synthetic-compare', 'top-sites', 'wild-web', 'cdp-session']) {
const summary = summaries.find(candidate => candidate.suite === suite);
if (summary) return summary;
}
return summaries[0];
}
function summaryCases(summary) {
const cases = summary?.cases || [];
return cases.length ? cases : ['aggregate'];
}
function isAggregateSummary(summary) {
return summary?.targets && !(summary?.cases || []).length;
}
function resultFor(summary, target, caseName) {
if (isAggregateSummary(summary)) return summary?.targets?.[target] || {};
return summary?.targets?.[target]?.cases?.[caseName] || {};
}
function performanceResultFor(summary, target, caseName) {
if (!isAggregateSummary(summary)) return resultFor(summary, target, caseName);
const common = summary?.common_success?.targets?.[target];
if (common && Number(common?.elapsed_ms?.count || 0) > 0) return common;
const result = resultFor(summary, target, caseName);
return {
...result,
elapsed_ms: result?.successful_elapsed_ms || result?.elapsed_ms,
peak_pss_bytes: result?.successful_peak_pss_bytes || result?.peak_pss_bytes,
peak_rss_bytes: result?.successful_peak_rss_bytes || result?.peak_rss_bytes,
};
}
function metricFor(summary, target, caseName, metric) {
const result = performanceResultFor(summary, target, caseName);
return result?.[metric]?.p50 ?? null;
}
function targetFailures(summary, target) {
const targetSummary = summary?.targets?.[target] || {};
if (Number.isFinite(Number(targetSummary.failures))) return Number(targetSummary.failures);
return Object.values(targetSummary.cases || {}).reduce((total, result) => total + Number(result.failures || 0), 0);
}
function resultPasses(result) {
return Number(result?.passes ?? result?.successes ?? result?.categories?.success ?? 0);
}
function resultFailures(result) {
return Number(result?.failures ?? result?.total_failures ?? 0);
}
function horizontalCells(summary) {
const targets = targetNames(summary?.targets || {});
const cases = summaryCases(summary);
return cases.flatMap(caseName => targets.map(target => {
const result = performanceResultFor(summary, target, caseName);
return {caseName, target, result};
}));
}
function fastestCell(summary) {
return horizontalCells(summary)
.filter(item => Number.isFinite(Number(item.result.elapsed_ms?.p50)))
.sort((a, b) => Number(a.result.elapsed_ms.p50) - Number(b.result.elapsed_ms.p50))[0];
}
function lowestMemoryCell(summary) {
return horizontalCells(summary)
.filter(item => Number.isFinite(Number(item.result.peak_pss_bytes?.p50)))
.sort((a, b) => Number(a.result.peak_pss_bytes.p50) - Number(b.result.peak_pss_bytes.p50))[0];
}
function passRate(result) {
const passes = resultPasses(result);
const failures = resultFailures(result);
const attempts = passes + failures;
const denominator = attempts || Number(result.pages ?? result.seeds ?? result.sites ?? result.runs ?? 0);
if (!denominator) return '';
return `${passes}/${denominator} (${(passes / denominator * 100).toFixed(1)}%)`;
}
function rawPassRate(result) {
const passes = Number(result?.raw_passes);
const attempts = Number(result?.raw_comparable_attempts);
if (!Number.isFinite(passes) || !Number.isFinite(attempts) || attempts <= 0) return '';
return `${passes}/${attempts} (${(passes / attempts * 100).toFixed(1)}%)`;
}
function siteStability(result) {
const sites = Number(result?.unique_sites);
if (!Number.isFinite(sites) || sites <= 0) return '';
const runs = Number(result?.runs || 0);
if (!result?.repeat_validated) return `${sites} sites; ${runs || 1} run(s), not repeat-validated`;
return `${Number(result?.all_pass_sites || 0)} all-pass / ${Number(result?.flaky_sites || 0)} flaky / ${Number(result?.all_fail_sites || 0)} all-fail`;
}
function statusCoverage(result) {
const observed = Number(result?.status_observed_attempts);
const attempts = Number(result?.observed_sites ?? result?.attempts);
const denominator = Number.isFinite(attempts) && attempts > 0 ? attempts : Number(result?.seeds);
if (!Number.isFinite(observed) || !Number.isFinite(denominator) || denominator <= 0) return '';
const sources = Object.entries(result?.response_status_sources || {})
.map(([source, count]) => `${source}: ${count}`)
.join(', ');
const coverage = `${observed}/${denominator} (${(observed / denominator * 100).toFixed(1)}%)`;
return sources ? `${coverage}; ${sources}` : coverage;
}
function breakdown(items) {
if (!items || typeof items !== 'object') return '';
return Object.entries(items).map(([key, value]) => `${htmlEscape(key)}: ${htmlEscape(value)}`).join(', ');
}
function shortUrl(value) {
const text = String(value || '');
if (text.length <= 96) return text;
return `${text.slice(0, 56)}...${text.slice(-34)}`;
}
function scoreClass(value) {
const n = Number(value);
if (!Number.isFinite(n)) return 'fail';
if (n >= 80) return '';
if (n >= 50) return 'warn';
return 'fail';
}
function scoreBar(value) {
const n = Number(value);
if (!Number.isFinite(n)) return '';
const width = Math.max(0, Math.min(100, n));
const cls = scoreClass(n);
return `<div class="score-cell"><div class="score-value"><strong>${number(n)}</strong><span class="muted">/100</span></div><div class="score-track"><div class="score-fill ${cls}" style="width:${width}%"></div></div></div>`;
}
function renderCategory(category, ok) {
if (ok) return badge('PASS');
const text = String(category || 'FAIL');
const cls = text === 'render-partial' ? 'warn' : 'fail';
return `<span class="badge ${cls}">${htmlEscape(text)}</span>`;
}
function renderKpis() {
const readiness = report.publish_readiness || {};
const targets = report.versions?.targets || {};
const availableTargets = Object.values(targets).filter(t => t && t.available).length;
const gateFailures = (report.summaries || []).reduce((total, summary) => total + Number(summary.gate_failures ?? summary.total_failures ?? 0), 0);
const horizontal = firstHorizontalSummary();
const cards = [
['Publication', badge(readiness.status || 'investigation'), 'Machine-readable publish-readiness gate.'],
['Suites Run', String((report.summaries || []).length), 'Benchmark suites present in this artifact.'],
['Gate Failures', String(gateFailures), 'Selected target gates, not competitor-only failures.'],
['Targets Available', String(availableTargets), 'Detected target binaries in versions.json.'],
];
document.getElementById('kpis').innerHTML = cards.map(([label, value, note]) => `
<div class="stat"><div class="stat-label">${label}</div><div class="stat-value">${value}</div><div class="stat-note">${note}</div></div>
`).join('');
document.getElementById('publishBadge').innerHTML = badge(readiness.status || 'investigation');
document.getElementById('outputDir').textContent = report.output_dir || '';
}
function renderExecutive() {
const readiness = report.publish_readiness || {};
const horizontal = firstHorizontalSummary();
if (horizontal) {
const fastest = fastestCell(horizontal);
const memory = lowestMemoryCell(horizontal);
const commonAttempts = Number(horizontal?.common_success?.attempts || 0);
const cohortNote = commonAttempts > 0 ? ` over ${commonAttempts} common-success attempt(s)` : '';
const failureTargets = targetNames(horizontal.targets || {}).filter(target => targetFailures(horizontal, target) > 0);
document.getElementById('executiveSummary').innerHTML = `
<div class="finding-list">
<div class="finding">Horizontal source: <strong>${htmlEscape(horizontal.suite)}</strong>. Gate target: <strong>${htmlEscape(horizontal.gate_target || 'moli')}</strong>.</div>
<div class="finding">${fastest ? `Fastest comparable-success p50: <strong>${htmlEscape(targetLabel(horizontal.targets, fastest.target))}</strong> / ${htmlEscape(fastest.caseName)} at ${number(fastest.result.elapsed_ms.p50)} ms${cohortNote}.` : 'No comparable successful latency cell is available.'}</div>
<div class="finding">${memory ? `Lowest comparable-success PSS: <strong>${htmlEscape(targetLabel(horizontal.targets, memory.target))}</strong> / ${htmlEscape(memory.caseName)} at ${mib(memory.result.peak_pss_bytes.p50)}${cohortNote}.` : 'No comparable successful memory cell is available.'}</div>
<div class="finding ${failureTargets.length ? 'warn' : ''}">${failureTargets.length ? `Targets with comparison failures: <strong>${htmlEscape(failureTargets.map(target => targetLabel(horizontal.targets, target)).join(', '))}</strong>.` : 'No comparison target failures in this suite.'}</div>
</div>
`;
document.getElementById('runNotes').innerHTML = `
<div class="finding-list">
<div class="finding">Publication status: ${badge(readiness.status || 'investigation')}</div>
<div class="finding">Total target failures: <strong>${htmlEscape(horizontal.total_failures ?? 0)}</strong>. Gate failures: <strong>${htmlEscape(horizontal.gate_failures ?? 0)}</strong>.</div>
<div class="finding">Open <a href="report-data.json">report-data.json</a> for renderer-independent data.</div>
</div>
`;
} else {
document.getElementById('executiveSummary').innerHTML = '<p class="notice">No horizontal comparison suite is present in this report.</p>';
document.getElementById('runNotes').innerHTML = '<p class="notice">No chartable horizontal data is present.</p>';
}
}
function renderTargets() {
const targets = report.versions?.targets || {};
document.getElementById('targetCards').innerHTML = targetNames(targets).map(name => {
const target = targets[name] || {};
return `<article class="target-card">
<h3><span>${htmlEscape(name)}</span>${badge(target.available ? 'PASS' : 'NOT RUN')}</h3>
<div class="target-meta">
<div><strong>Version</strong> ${htmlEscape(target.version || '')}</div>
<div><strong>Size</strong> ${size(target.size_bytes)}</div>
<div><strong>Path</strong> <code>${htmlEscape(target.path || '')}</code></div>
<div><strong>SHA256</strong> <code>${htmlEscape(target.sha256 || '')}</code></div>
</div>
</article>`;
}).join('');
}
function table(headers, rows) {
return `<table><thead><tr>${headers.map(h => `<th>${htmlEscape(h)}</th>`).join('')}</tr></thead><tbody>${rows.join('')}</tbody></table>`;
}
function renderReadiness() {
const checks = report.publish_readiness?.checks || [];
const rows = checks.map(check => `<tr>
<th>${htmlEscape(check.name)}</th>
<td>${badge(check.ok ? 'PASS' : 'FAIL')}</td>
<td>${htmlEscape(JSON.stringify(check.actual ?? ''))}</td>
<td>${htmlEscape(JSON.stringify(check.required ?? ''))}</td>
<td>${htmlEscape(check.detail || '')}</td>
</tr>`);
document.getElementById('readinessTable').innerHTML = table(['check', 'gate', 'actual', 'required', 'detail'], rows);
}
function renderSuites() {
const rows = (report.summaries || []).map(summary => `<tr>
<th>${htmlEscape(summary.suite)}</th>
<td>${htmlEscape(summary.profile || '')}</td>
<td>${htmlEscape(summary.gate_target || '')}</td>
<td>${htmlEscape(summary.gate_failures ?? '')}</td>
<td>${htmlEscape(summary.total_failures ?? '')}</td>
<td>${htmlEscape(summary.runs ?? '')}</td>
<td>${htmlEscape(summary.timeout_seconds ?? '')}</td>
</tr>`);
document.getElementById('suiteTable').innerHTML = table(['suite', 'profile', 'gate target', 'gate failures', 'all failures', 'runs', 'timeout'], rows);
}
function comparisonSummaryHtml(summary) {
const targets = targetNames(summary.targets);
if (isAggregateSummary(summary)) {
const rows = targets.map(target => {
const result = summary.targets[target] || {};
const successfulElapsed = result.successful_elapsed_ms || result.elapsed_ms || {};
const successfulPss = result.successful_peak_pss_bytes || result.peak_pss_bytes || {};
const common = summary?.common_success?.targets?.[target] || {};
const commonAttempts = Number(summary?.common_success?.attempts || 0);
const exclusions = [
Number(result.excluded_runs || 0) ? `excluded: ${result.excluded_runs}` : '',
Number(result.non_comparable_attempts || 0) ? `neutral: ${result.non_comparable_attempts}` : '',
].filter(Boolean).join('; ');
return `<tr>
<th>${htmlEscape(targetLabel(summary.targets, target))}</th>
<td>${passRate(result)}</td>
<td>${rawPassRate(result)}</td>
<td>${htmlEscape(exclusions)}</td>
<td>${htmlEscape(siteStability(result))}</td>
<td>${htmlEscape(statusCoverage(result))}</td>
<td>${htmlEscape(result.successes ?? result.categories?.success ?? result.passes ?? '')}</td>
<td>${htmlEscape(result.failures ?? '')}</td>
<td>${number(successfulElapsed.p50)} / ${number(successfulElapsed.p90)} / ${number(successfulElapsed.p95)}</td>
<td>${mib(successfulPss.p50)} / ${mib(successfulPss.p95)}</td>
<td>${commonAttempts || ''}</td>
<td>${number(common.elapsed_ms?.p50)} / ${number(common.elapsed_ms?.p90)} / ${number(common.elapsed_ms?.p95)}</td>
<td>${mib(common.peak_pss_bytes?.p50)} / ${mib(common.peak_pss_bytes?.p95)}</td>
<td>${breakdown(result.failure_kinds)}</td>
</tr>`;
});
const pairwise = Array.isArray(summary.pairwise) ? summary.pairwise : [];
const pairRows = pairwise.map(item => `<tr>
<th>${htmlEscape(targetLabel(summary.targets, item.left))} vs ${htmlEscape(targetLabel(summary.targets, item.right))}</th>
<td>${badge(item.repeat_validated ? 'REPEAT VALIDATED' : 'SINGLE SAMPLE')}</td>
<td>${htmlEscape(item.both_all_pass ?? '')}</td>
<td>${htmlEscape(item.left_all_pass_right_all_fail ?? '')}</td>
<td>${htmlEscape(item.right_all_pass_left_all_fail ?? '')}</td>
<td>${htmlEscape(item.both_all_fail ?? '')}</td>
<td>${htmlEscape(item.flaky_or_incomplete ?? '')}</td>
<td>${htmlEscape(item.not_comparable ?? '')}</td>
<td>${htmlEscape(item.common_success?.attempts ?? '')}</td>
</tr>`);
const pairTable = pairRows.length
? `<h4>Pairwise site outcomes</h4>${table(['targets', 'validation', 'both pass', 'left only', 'right only', 'both fail', 'flaky / incomplete', 'not comparable', 'common-success attempts'], pairRows)}`
: '';
const contract = summary.snapshot_contract
? `<p class="muted">Snapshot contract: ${htmlEscape(summary.snapshot_contract)}</p>`
: '';
return contract + table(
['target', 'counted pass rate', 'raw pass rate', 'excluded / neutral', 'site stability', 'HTTP status observed', 'success', 'failures', 'successful p50 / p90 / p95 ms', 'successful PSS p50 / p95', 'common n', 'common p50 / p90 / p95 ms', 'common PSS p50 / p95', 'failure kinds'],
rows,
) + pairTable;
}
const cases = summaryCases(summary);
const rows = cases.map(caseName => {
const cells = targets.map(target => {
const result = resultFor(summary, target, caseName);
const failures = Number(result.failures || 0);
const count = Number(result.elapsed_ms?.count || 0);
const status = failures === 0 && count > 0 ? 'PASS' : 'FAIL';
const sample = (result.failure_samples || [])[0];
return `<td>${statusBadge(status, sample)}<br><span class="muted">p50</span> ${number(result.elapsed_ms?.p50)} ms<br><span class="muted">PSS</span> ${mib(result.peak_pss_bytes?.p50)}<br><span class="muted">RSS</span> ${mib(result.peak_rss_bytes?.p50)}<br><span class="muted">failures</span> ${failures}</td>`;
}).join('');
return `<tr><th>${htmlEscape(caseName)}</th>${cells}</tr>`;
});
return table(['case', ...targets.map(target => targetLabel(summary.targets, target))], rows);
}
function renderComparisonTable() {
const summaries = horizontalSummaries();
if (!summaries.length) {
document.getElementById('comparisonTable').innerHTML = '<p class="muted">No horizontal comparison data.</p>';
return;
}
document.getElementById('comparisonTable').innerHTML = summaries.map(summary => `
<section class="comparison-summary">
<h3>${htmlEscape(summary.suite || 'comparison')}</h3>
${comparisonSummaryHtml(summary)}
</section>
`).join('');
}
function renderWebPages() {
const section = document.getElementById('web-pages');
const data = report.render_compare || {};
const runs = Array.isArray(data.runs) ? data.runs : [];
const baselineSites = Array.isArray(data.baseline_sites) ? data.baseline_sites : [];
if (!runs.length && !baselineSites.length) {
section.style.display = 'none';
return;
}
const evaluatedSites = new Set(runs.map(row => `${row.rank}|${row.domain}`)).size;
const targetRequests = runs.length;
const failures = runs.filter(row => !row.ok && !row.excluded).length;
const worst = runs
.filter(row => Number.isFinite(Number(row.render_quality_score)))
.sort((a, b) => Number(a.render_quality_score) - Number(b.render_quality_score))[0];
const cards = [
['Baseline URLs', String(baselineSites.length || evaluatedSites), 'Chrome baseline requests used to build the evaluated set.'],
['Evaluated Pages', String(evaluatedSites), 'Pages that reached target scoring after baseline filtering.'],
['Target Requests', String(targetRequests), 'Browser/page request rows shown below.'],
['Worst Score', worst ? `${number(worst.render_quality_score)} ${worst.target}` : '', worst ? shortUrl(worst.url || worst.domain) : 'No scored target rows.'],
];
document.getElementById('webPageKpis').innerHTML = cards.map(([label, value, note]) => `
<div class="stat"><div class="stat-label">${htmlEscape(label)}</div><div class="stat-value">${htmlEscape(value)}</div><div class="stat-note">${htmlEscape(note)}</div></div>
`).join('');
const baselineByKey = new Map(baselineSites.map(row => [`${row.rank}|${row.domain}`, row]));
const rows = runs
.slice()
.sort((a, b) => Number(a.rank) - Number(b.rank) || String(a.target).localeCompare(String(b.target)))
.map(row => {
const baseline = baselineByKey.get(`${row.rank}|${row.domain}`) || {};
const url = row.url || row.domain;
const artifact = row.failure_artifact ? `<br><a href="render-compare/${htmlEscape(row.failure_artifact)}">failure artifact</a>` : '';
return `<tr>
<th class="metric-tight">${htmlEscape(row.rank)}</th>
<td class="url-cell"><a href="${htmlEscape(url)}">${htmlEscape(shortUrl(url))}</a><br><span class="muted">${htmlEscape(baseline.baseline_title || '')}</span>${artifact}</td>
<td>${htmlEscape(row.label || row.target)}</td>
<td>${renderCategory(row.category, row.ok)}</td>
<td>${scoreBar(row.render_quality_score)}</td>
<td>${scoreBar(row.raw_content_score)}</td>
<td class="metric-tight">${number(row.elapsed_ms)} ms</td>
<td class="metric-tight">${mib(row.peak_rss_bytes)}</td>
<td class="metric-tight">${mib(row.peak_pss_bytes)}</td>
<td class="metric-tight">${number(row.baseline_elapsed_ms)} ms</td>
<td>${htmlEscape(row.target_fetch_category || '')}<br><span class="muted">${htmlEscape(row.baseline_category || '')}</span></td>
<td class="metric-tight">${htmlEscape(row.target_visible_text_length ?? '')} / ${htmlEscape(row.baseline_visible_text_length ?? '')}</td>
<td class="metric-tight">${number(row.ngram_containment)} / ${number(row.key_phrase_hit_rate)}</td>
</tr>`;
});
document.getElementById('webPageTable').innerHTML = table(
['rank', 'web page', 'target', 'result', 'render score', 'raw score', 'elapsed', 'RSS', 'PSS', 'Chrome elapsed', 'fetch categories', 'visible chars', 'ngram / key hits'],
rows,
);
}
function renderArtifacts() {
const artifacts = report.artifact_paths_by_suite || {};
const rows = Object.entries(artifacts).map(([suite, paths]) => `<tr>
<th>${htmlEscape(suite)}</th>
<td>${(paths || []).map(path => `<a href="${htmlEscape(path)}">${htmlEscape(path)}</a>`).join(' · ')}</td>
</tr>`);
const top = ['report-data.json', 'summary.json', 'summary.md', 'versions.json', 'publish-readiness.json', 'index.html'];
rows.unshift(`<tr><th>top-level</th><td>${top.map(path => `<a href="${path}">${path}</a>`).join(' · ')}</td></tr>`);
document.getElementById('artifactTable').innerHTML = table(['suite', 'artifacts'], rows);
}
function renderDiff() {
const diff = report.report_diff;
if (!diff) {
document.getElementById('reportDiff').innerHTML = '<p class="muted">No baseline report was provided.</p>';
return;
}
const summary = diff.summary || {};
document.getElementById('reportDiff').innerHTML = table(['metric', 'value'], [
`<tr><th>baseline</th><td><code>${htmlEscape(diff.baseline || '')}</code></td></tr>`,
`<tr><th>added suites</th><td>${htmlEscape(summary.added ?? '')}</td></tr>`,
`<tr><th>removed suites</th><td>${htmlEscape(summary.removed ?? '')}</td></tr>`,
`<tr><th>changed suites</th><td>${htmlEscape(summary.changed ?? '')}</td></tr>`,
`<tr><th>gate failures delta</th><td>${htmlEscape(summary.gate_failures_delta ?? '')}</td></tr>`,
`<tr><th>total failures delta</th><td>${htmlEscape(summary.total_failures_delta ?? '')}</td></tr>`,
]);
}
function renderCharts() {
const summary = firstHorizontalSummary();
if (!summary || !window.Chart) return;
const targets = targetNames(summary.targets || {});
const cases = summaryCases(summary);
const comparisonChartHeight = Math.max(420, cases.length * Math.max(34, targets.length * 7));
document.getElementById('latencyChart').parentElement.style.height = `${comparisonChartHeight}px`;
document.getElementById('memoryChart').parentElement.style.height = `${comparisonChartHeight}px`;
document.getElementById('rssChart').parentElement.style.height = `${comparisonChartHeight}px`;
const datasets = targets.map(target => ({
label: targetLabel(summary.targets, target),
backgroundColor: targetColor(summary.targets, target),
borderColor: targetColor(summary.targets, target),
data: cases.map(caseName => metricFor(summary, target, caseName, 'elapsed_ms')),
}));
const memoryDatasets = targets.map(target => ({
label: targetLabel(summary.targets, target),
backgroundColor: targetColor(summary.targets, target),
borderColor: targetColor(summary.targets, target),
data: cases.map(caseName => {
const value = metricFor(summary, target, caseName, 'peak_pss_bytes');
return value == null ? null : value / 1024 / 1024;
}),
}));
const rssDatasets = targets.map(target => ({
label: targetLabel(summary.targets, target),
backgroundColor: targetColor(summary.targets, target),
borderColor: targetColor(summary.targets, target),
data: cases.map(caseName => {
const value = metricFor(summary, target, caseName, 'peak_rss_bytes');
return value == null ? null : value / 1024 / 1024;
}),
}));
const chartOptions = (label) => ({
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
align: 'end',
labels: {
boxWidth: 10,
boxHeight: 10,
padding: 14,
usePointStyle: true,
pointStyle: 'rectRounded',
},
},
tooltip: { callbacks: { label: ctx => `${ctx.dataset.label}: ${number(ctx.parsed.x)} ${label}` } },
},
scales: {
x: { beginAtZero: true, grid: { color: '#e6ebf1' }, title: { display: true, text: label } },
y: { grid: { display: false }, ticks: { autoSkip: false } },
},
});
new Chart(document.getElementById('latencyChart'), {
type: 'bar',
data: { labels: cases, datasets },
options: chartOptions('ms'),
});
new Chart(document.getElementById('memoryChart'), {
type: 'bar',
data: { labels: cases, datasets: memoryDatasets },
options: chartOptions('MiB'),
});
new Chart(document.getElementById('rssChart'), {
type: 'bar',
data: { labels: cases, datasets: rssDatasets },
options: chartOptions('MiB'),
});
new Chart(document.getElementById('failureChart'), {
type: 'bar',
data: {
labels: targets.map(target => targetLabel(summary.targets, target)),
datasets: [{
label: 'failures',
backgroundColor: targets.map(target => targetColor(summary.targets, target)),
data: targets.map(target => targetFailures(summary, target)),
}],
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: { y: { beginAtZero: true, precision: 0 }, x: { grid: { display: false }, ticks: { display: false } } },
},
});
}
function renderWebPageCharts() {
const data = report.render_compare || {};
const runs = Array.isArray(data.runs) ? data.runs : [];
if (!runs.length || !window.Chart) return;
const chartRows = runs
.filter(row => Number.isFinite(Number(row.render_quality_score)))
.slice()
.sort((a, b) => Number(a.render_quality_score) - Number(b.render_quality_score))
.slice(0, 32);
const labels = chartRows.map(row => `#${row.rank} ${row.target} ${shortUrl(row.domain || row.url)}`);
const scoreColors = chartRows.map(row => targetColor({[row.target]: row}, row.target));
new Chart(document.getElementById('pageScoreChart'), {
type: 'bar',
data: {
labels,
datasets: [{
label: 'render quality score',
data: chartRows.map(row => Number(row.render_quality_score)),
backgroundColor: scoreColors,
}],
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
x: { min: 0, max: 100, grid: { color: '#e6ebf1' }, title: { display: true, text: 'score' } },
y: { grid: { display: false }, ticks: { autoSkip: false } },
},
},
});
const latencyRows = runs
.filter(row => Number.isFinite(Number(row.elapsed_ms)))
.slice()
.sort((a, b) => Number(b.elapsed_ms) - Number(a.elapsed_ms))
.slice(0, 32);
new Chart(document.getElementById('pageLatencyChart'), {
type: 'bar',
data: {
labels: latencyRows.map(row => `#${row.rank} ${row.target} ${shortUrl(row.domain || row.url)}`),
datasets: [{
label: 'elapsed ms',
data: latencyRows.map(row => Number(row.elapsed_ms)),
backgroundColor: latencyRows.map(row => targetColor({[row.target]: row}, row.target)),
}],
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: { callbacks: { afterLabel: ctx => `RSS: ${mib(latencyRows[ctx.dataIndex]?.peak_rss_bytes)}` } },
},
scales: {
x: { beginAtZero: true, grid: { color: '#e6ebf1' }, title: { display: true, text: 'ms' } },
y: { grid: { display: false }, ticks: { autoSkip: false } },
},
},
});
}
renderKpis();
renderExecutive();
renderTargets();
renderReadiness();
renderSuites();
renderComparisonTable();
renderWebPages();
renderArtifacts();
renderDiff();
renderCharts();
renderWebPageCharts();
</script>
</body>
</html>
"""
return document.replace("__REPORT_DATA__", data)
def _publish_readiness(readiness: dict[str, Any] | None) -> str:
if not readiness:
return ""
rows = []
for check in readiness.get("checks", []):
if not isinstance(check, dict):
continue
rows.append(
"<tr>"
f"<th>{_cell(check.get('name'))}</th>"
f"<td>{_status(bool(check.get('ok')))}</td>"
f"<td>{_cell(check.get('actual'))}</td>"
f"<td>{_cell(check.get('required'))}</td>"
f"<td>{_cell(check.get('detail'))}</td>"
"</tr>"
)
status = str(readiness.get("status", "investigation")).upper()
return (
"<h2>Publication Readiness</h2>"
f"<p>Publication status: {_status_badge('PASS' if status == 'PUBLISHABLE' else 'PARTIAL')} "
f"<code>{html.escape(status.lower())}</code></p>"
"<table>"
"<thead><tr><th>check</th><th>gate</th><th>actual</th><th>required</th><th>detail</th></tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _report_diff(report_diff: dict[str, Any] | None) -> str:
if not report_diff:
return ""
summary = report_diff.get("summary", {})
rows = [
("added suites", summary.get("added")),
("removed suites", summary.get("removed")),
("changed suites", summary.get("changed")),
("unchanged suites", summary.get("unchanged")),
("gate failures delta", summary.get("gate_failures_delta")),
("total failures delta", summary.get("total_failures_delta")),
]
body = "".join(f"<tr><th>{html.escape(label)}</th><td>{_cell(value)}</td></tr>" for label, value in rows)
return (
"<h3>Previous Report Diff</h3>"
f"<p>Baseline: <code>{_cell(report_diff.get('baseline'))}</code></p>"
"<table>"
"<thead><tr><th>metric</th><th>value</th></tr></thead>"
f"<tbody>{body}</tbody>"
"</table>"
)
def _bar(width_percent: float, label: str, *, class_name: str = "") -> str:
width = max(0.0, min(100.0, width_percent))
return (
f'<div class="bar-track {html.escape(class_name)}">'
f'<div class="bar-fill" style="width:{width:.2f}%"></div>'
f'<span>{html.escape(label)}</span>'
"</div>"
)
def _synthetic_compare(summary: dict[str, Any]) -> str:
targets = summary.get("targets", {})
cases = summary.get("cases", [])
rows = []
for case in cases:
cells = [f"<th>{html.escape(str(case))}</th>"]
for target_name in _target_names(targets):
result = targets[target_name].get("cases", {}).get(case, {})
elapsed = result.get("elapsed_ms", {})
pss = result.get("peak_pss_bytes", {})
failures = int(result.get("failures", 0) or 0)
count = int(elapsed.get("count", 0) or 0)
ok = failures == 0 and count > 0
cells.append(
"<td>"
f"{_status(ok)}"
f"<div class=\"metric\">p50 {_cell(elapsed.get('p50'))} ms</div>"
f"<div class=\"metric\">p95 {_cell(elapsed.get('p95'))} ms</div>"
f"<div class=\"metric\">PSS p50 {_mib(pss.get('p50'))}</div>"
f"<div class=\"metric\">failures {failures}</div>"
"</td>"
)
rows.append("<tr>" + "".join(cells) + "</tr>")
header = "".join(f"<th>{html.escape(str(name))}</th>" for name in _target_names(targets))
return (
"<table>"
f"<thead><tr><th>case</th>{header}</tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _synthetic_compare_chart(summary: dict[str, Any]) -> str:
targets = summary.get("targets", {})
cases = summary.get("cases", [])
rows = []
for case in cases:
values: list[tuple[str, float]] = []
for target_name in _target_names(targets):
elapsed = targets.get(target_name, {}).get("cases", {}).get(case, {}).get("elapsed_ms", {})
value = _num(elapsed.get("p50"))
if value is not None:
values.append((target_name, value))
if not values:
continue
max_value = max(value for _, value in values) or 1.0
bars = "".join(
"<div class=\"chart-row\">"
f"<span class=\"chart-target\">{html.escape(target_name)}</span>"
f"{_bar((value / max_value) * 100.0, f'{value:.2f} ms', class_name=target_name)}"
"</div>"
for target_name, value in values
)
rows.append(
"<section class=\"chart-group\">"
f"<h3>{html.escape(str(case))}</h3>"
f"{bars}"
"</section>"
)
if not rows:
return ""
return "<div class=\"chart-grid\">" + "".join(rows) + "</div>"
def _synthetic_advantages(summary: dict[str, Any]) -> str:
targets = summary.get("targets", {})
cases = summary.get("cases", [])
wins = {
target: {"speed": 0, "memory": 0, "passes": 0, "total": 0}
for target in targets
}
for case in cases:
speed_values: list[tuple[str, float]] = []
memory_values: list[tuple[str, float]] = []
for target_name, target_summary in targets.items():
result = target_summary.get("cases", {}).get(case, {})
elapsed = result.get("elapsed_ms", {})
pss = result.get("peak_pss_bytes", {})
failures = int(result.get("failures", 0) or 0)
count = int(elapsed.get("count", 0) or 0)
wins[target_name]["total"] += 1
if failures == 0 and count > 0:
wins[target_name]["passes"] += 1
if elapsed.get("p50") is not None:
speed_values.append((target_name, float(elapsed["p50"])))
if pss.get("p50") is not None:
memory_values.append((target_name, float(pss["p50"])))
if speed_values:
fastest = min(speed_values, key=lambda item: item[1])[0]
wins[fastest]["speed"] += 1
if memory_values:
lowest_memory = min(memory_values, key=lambda item: item[1])[0]
wins[lowest_memory]["memory"] += 1
rows = []
for target_name in _target_names(targets):
result = wins[target_name]
rows.append(
"<tr>"
f"<th>{html.escape(str(target_name))}</th>"
f"<td>{result['passes']} / {result['total']}</td>"
f"<td>{result['speed']}</td>"
f"<td>{result['memory']}</td>"
"</tr>"
)
return (
"<table>"
"<thead><tr><th>target</th><th>passes</th><th>fastest cases</th><th>lowest PSS cases</th></tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _target_metric_table(summary: dict[str, Any], *, label: str) -> str:
targets = summary.get("targets", {})
rows = []
for target_name in _target_names(targets):
result = targets[target_name]
elapsed = result.get("elapsed_ms", {})
pss = result.get("peak_pss_bytes", result.get("browser_peak_pss_bytes", {}))
failures = int(result.get("failures", 0) or 0)
passes = result.get("passes")
total = result.get("pages", result.get("seeds", result.get("runs")))
rows.append(
"<tr>"
f"<th>{html.escape(str(target_name))}</th>"
f"<td>{_status(failures == 0 and int(elapsed.get('count', 0) or 0) > 0)}</td>"
f"<td>{_cell(passes)} / {_cell(total)}</td>"
f"<td>{_cell(elapsed.get('p50'))}</td>"
f"<td>{_cell(elapsed.get('p95'))}</td>"
f"<td>{_mib(pss.get('p50'))}</td>"
f"<td>{_mib(pss.get('p95'))}</td>"
f"<td>{failures}</td>"
"</tr>"
)
return (
"<table>"
f"<thead><tr><th>{html.escape(label)}</th><th>gate</th><th>passes</th><th>p50 ms</th><th>p95 ms</th><th>PSS p50</th><th>PSS p95</th><th>failures</th></tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _wpt(summary: dict[str, Any]) -> str:
counts = summary.get("summary", {})
rows = [
("total", counts.get("total")),
("pass", counts.get("pass")),
("known-fail", counts.get("known_fail")),
("unexpected-fail", counts.get("unexpected_fail")),
("unexpected-pass", counts.get("unexpected_pass")),
("skip", counts.get("skip")),
("pass rate", f"{float(counts['pass_rate_percent']):.2f}%" if counts.get("pass_rate_percent") is not None else None),
(
"unexpected-fail rate",
f"{float(counts['unexpected_fail_rate_percent']):.2f}%" if counts.get("unexpected_fail_rate_percent") is not None else None,
),
("skip rate", f"{float(counts['skip_rate_percent']):.2f}%" if counts.get("skip_rate_percent") is not None else None),
]
summary_rows = "".join(
"<tr>"
f"<th>{html.escape(label)}</th>"
f"<td>{_cell(value)}</td>"
"</tr>"
for label, value in rows
)
tag_rows = []
for tag, tag_counts in sorted((counts.get("by_tag") or {}).items()):
pass_rate = tag_counts.get("pass_rate_percent")
tag_rows.append(
"<tr>"
f"<th>{html.escape(str(tag))}</th>"
f"<td>{_cell(tag_counts.get('total'))}</td>"
f"<td>{_cell(tag_counts.get('pass'))}</td>"
f"<td>{_cell(tag_counts.get('unexpected_fail'))}</td>"
f"<td>{_cell(tag_counts.get('skip'))}</td>"
f"<td>{_cell(f'{float(pass_rate):.2f}%' if pass_rate is not None else None)}</td>"
"</tr>"
)
tag_table = (
"<h3>By Tag</h3>"
"<table>"
"<thead><tr><th>tag</th><th>total</th><th>pass</th><th>unexpected fail</th><th>skip</th><th>pass rate</th></tr></thead>"
f"<tbody>{''.join(tag_rows)}</tbody>"
"</table>"
if tag_rows
else ""
)
diff = summary.get("diff")
diff_table = ""
if isinstance(diff, dict):
diff_rows = [
("added", diff.get("added")),
("removed", diff.get("removed")),
("expectation changes", diff.get("expectation_changes")),
("category changes", diff.get("category_changes")),
("total changes", diff.get("total_changes")),
]
diff_body = "".join(
"<tr>"
f"<th>{html.escape(label)}</th>"
f"<td>{_cell(value)}</td>"
"</tr>"
for label, value in diff_rows
)
diff_table = (
"<h3>Baseline Diff</h3>"
f"<p>Baseline: <code>{_cell(summary.get('baseline'))}</code></p>"
"<table>"
"<thead><tr><th>change</th><th>count</th></tr></thead>"
f"<tbody>{diff_body}</tbody>"
"</table>"
)
return (
"<table>"
"<thead><tr><th>metric</th><th>value</th></tr></thead>"
f"<tbody>{summary_rows}</tbody>"
"</table>"
f"{tag_table}"
f"{diff_table}"
)
def _wild_web(summary: dict[str, Any]) -> str:
targets = summary.get("targets", {})
rows = []
replay_note = (
f"<p>Replay capture: <code>{_cell(summary.get('replay_capture'))}</code>. "
f"Replay artifacts: <code>{_cell(summary.get('replay_artifacts'))}</code>.</p>"
)
for target_name in _target_names(targets):
result = targets[target_name]
elapsed = result.get("elapsed_ms", {})
pss = result.get("peak_pss_bytes", {})
failures = int(result.get("failures", 0) or 0)
failure_kinds = result.get("failure_kinds", {})
categories = result.get("categories", {})
rows.append(
"<tr>"
f"<th>{html.escape(str(target_name))}</th>"
f"<td>{_status(failures == 0 and int(result.get('seeds', 0) or 0) > 0)}</td>"
f"<td>{_cell(result.get('passes'))} / {_cell(result.get('seeds'))}</td>"
f"<td>{_cell(elapsed.get('p50'))}</td>"
f"<td>{_mib(pss.get('p50'))}</td>"
f"<td>{_cell(result.get('extraction_failures'))}</td>"
f"<td>{_cell(failure_kinds)}</td>"
f"<td>{_cell(categories)}</td>"
"</tr>"
)
return replay_note + (
"<table>"
"<thead><tr><th>target</th><th>gate</th><th>passes</th><th>p50 ms</th><th>PSS p50</th><th>extraction failures</th><th>failure kinds</th><th>categories</th></tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _metric_cell(result: dict[str, Any]) -> str:
elapsed = result.get("elapsed_ms", {})
pss = result.get("peak_pss_bytes", result.get("browser_peak_pss_bytes", {}))
failures = int(result.get("failures", 0) or 0)
ok = failures == 0 and int(elapsed.get("count", 0) or 0) > 0
pss_metric = f"<div class=\"metric\">PSS p50 {_mib(pss.get('p50'))}</div>" if pss.get("p50") is not None else ""
return (
"<td>"
f"{_status(ok)}"
f"<div class=\"metric\">p50 {_cell(elapsed.get('p50'))} ms</div>"
f"<div class=\"metric\">p95 {_cell(elapsed.get('p95'))} ms</div>"
f"{pss_metric}"
f"<div class=\"metric\">failures {failures}</div>"
"</td>"
)
def _headline_compare(summaries: list[dict[str, Any]]) -> str:
target_names: list[str] = []
for summary in summaries:
for target_name in _target_names(summary.get("targets", {})):
if target_name not in target_names:
target_names.append(target_name)
if not target_names:
return ""
rows = []
for summary in summaries:
suite = summary.get("suite")
targets = summary.get("targets", {})
if not targets:
continue
if suite in {"synthetic-compare", "cdp-session"}:
for case in summary.get("cases", []):
cells = [f"<th>{html.escape(str(suite))}<div class=\"metric\">{html.escape(str(case))}</div></th>"]
for target_name in target_names:
result = targets.get(target_name, {}).get("cases", {}).get(case, {})
cells.append(_metric_cell(result) if result else "<td></td>")
rows.append("<tr>" + "".join(cells) + "</tr>")
else:
cells = [f"<th>{html.escape(str(suite))}</th>"]
for target_name in target_names:
result = targets.get(target_name, {})
cells.append(_metric_cell(result) if result else "<td></td>")
rows.append("<tr>" + "".join(cells) + "</tr>")
if not rows:
return ""
header = "".join(f"<th>{html.escape(str(name))}</th>" for name in target_names)
return (
"<h2>Headline Compare</h2>"
"<table class=\"headline\">"
f"<thead><tr><th>suite / case</th>{header}</tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _headline_charts(summaries: list[dict[str, Any]]) -> str:
sections = []
synthetic_compare = next((summary for summary in summaries if summary.get("suite") == "synthetic-compare"), None)
if synthetic_compare:
chart = _synthetic_compare_chart(synthetic_compare)
if chart:
sections.append("<h2>Headline Charts</h2><p class=\"section-note\">Lower elapsed time is better. Bars are normalized per case.</p>" + chart)
return "".join(sections)
def _cdp_session(summary: dict[str, Any]) -> str:
targets = summary.get("targets", {})
cases = summary.get("cases", [])
rows = []
for case in cases:
cells = [f"<th>{html.escape(str(case))}</th>"]
for target_name in _target_names(targets):
result = targets[target_name].get("cases", {}).get(case, {})
elapsed = result.get("elapsed_ms", {})
failures = int(result.get("failures", 0) or 0)
console_errors = int(result.get("console_errors", 0) or 0)
js_exceptions = int(result.get("js_exceptions", 0) or 0)
network_failures = int(result.get("network_failures", 0) or 0)
cells.append(
"<td>"
f"{_status(failures == 0 and int(elapsed.get('count', 0) or 0) > 0)}"
f"<div class=\"metric\">p50 {_cell(elapsed.get('p50'))} ms</div>"
f"<div class=\"metric\">p95 {_cell(elapsed.get('p95'))} ms</div>"
f"<div class=\"metric\">failures {failures}</div>"
f"<div class=\"metric\">console errors {console_errors}</div>"
f"<div class=\"metric\">JS exceptions {js_exceptions}</div>"
f"<div class=\"metric\">network failures {network_failures}</div>"
"</td>"
)
rows.append("<tr>" + "".join(cells) + "</tr>")
header = "".join(f"<th>{html.escape(str(name))}</th>" for name in _target_names(targets))
return (
"<table>"
f"<thead><tr><th>case</th>{header}</tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _cdp_smoke(summary: dict[str, Any]) -> str:
client_rows = summary.get("client_rows", [])
if not isinstance(client_rows, list):
client_rows = []
rows = []
for row in client_rows:
if not isinstance(row, dict):
continue
rows.append(
"<tr>"
f"<th>{_cell(row.get('client'))}</th>"
f"<td>{_status(bool(row.get('covered')))}</td>"
f"<td>{_status(bool(row.get('gate_ok')))}</td>"
f"<td>{_cell(row.get('required'))}</td>"
f"<td>{_cell(row.get('record_count'))}</td>"
f"<td>{_cell(row.get('failure_kind'))}</td>"
"</tr>"
)
groups = ", ".join(str(group) for group in summary.get("groups", []) if group)
return (
f"<p>Profile: <strong>{_cell(summary.get('profile', 'smoke'))}</strong>. "
f"Records: <strong>{_cell(summary.get('total_records'))}</strong>. "
f"Groups: {html.escape(groups)}.</p>"
"<table>"
"<thead><tr><th>client family</th><th>covered</th><th>formal gate</th><th>required</th><th>records</th><th>failure kind</th></tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _comparison_notice(summaries: list[dict[str, Any]]) -> str:
target_sets = [set((summary.get("targets") or {}).keys()) for summary in summaries if summary.get("targets")]
if not target_sets:
return ""
all_targets = set().union(*target_sets)
expected = {"moli", "lightpanda", "chrome", "obscura"}
if expected.issubset(all_targets):
return '<p class="notice ok-bg">This report includes moli, lightpanda, chrome, and obscura comparison data.</p>'
missing = ", ".join(sorted(expected - all_targets))
present = ", ".join(_target_names({target: {} for target in all_targets}))
return (
'<p class="notice warn-bg">'
f"This report is not a full horizontal comparison. Present targets: {html.escape(present)}. "
f"Missing targets: {html.escape(missing)}."
"</p>"
)
def _synthetic_matrix(summary: dict[str, Any]) -> str:
cases = summary.get("cases", {})
gate_rows = summary.get("formal_gate_rows", [])
gate_table_rows = []
if isinstance(gate_rows, list):
for result in gate_rows:
if not isinstance(result, dict):
continue
gate_table_rows.append(
"<tr>"
f"<th>{_cell(result.get('gate'))}</th>"
f"<td>{_status(bool(result.get('ok')))}</td>"
f"<td>{_cell(result.get('actual'))}</td>"
f"<td>{_cell(result.get('required'))}</td>"
f"<td>{_cell(result.get('failure_kind'))}</td>"
"</tr>"
)
profile = _cell(summary.get("profile", "smoke"))
profile_failures = int(summary.get("profile_failures", 0) or 0)
requirements_table = (
"<h3>Formal Gate Rows</h3>"
"<table>"
"<thead><tr><th>gate</th><th>status</th><th>actual</th><th>required</th><th>failure kind</th></tr></thead>"
f"<tbody>{''.join(gate_table_rows)}</tbody>"
"</table>"
if gate_table_rows
else ""
)
rows = []
for case, by_concurrency in cases.items():
if not isinstance(by_concurrency, dict):
continue
for concurrency, result in by_concurrency.items():
elapsed = result.get("elapsed_p50_ms", {})
failures = int(result.get("failures", 0) or 0)
stable = bool(result.get("stable", True)) and failures == 0
rows.append(
"<tr>"
f"<th>{html.escape(str(case))}</th>"
f"<td>{_cell(concurrency)}</td>"
f"<td>{_status(stable)}</td>"
f"<td>{_cell(elapsed.get('p50'))}</td>"
f"<td>{_cell(elapsed.get('p95'))}</td>"
f"<td>{_cell(result.get('median_drift_percent'))}</td>"
f"<td>{failures}</td>"
"</tr>"
)
return (
f"<p>Profile: <code>{profile}</code>. Profile failures: {profile_failures}.</p>"
f"{requirements_table}"
"<table>"
"<thead><tr><th>case</th><th>concurrency</th><th>gate</th><th>p50 ms</th><th>p95 ms</th><th>median drift %</th><th>failures</th></tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _startup_summary(summary: dict[str, Any]) -> str:
cases = summary.get("cases", {})
profile = str(summary.get("profile", "smoke"))
gate_rows = summary.get("formal_gate_rows", [])
time_note = (
"available"
if summary.get("time_verbose_available")
else "unavailable in this environment; raw unavailability markers archived"
)
artifact_count = len(summary.get("time_verbose_artifacts") or [])
cgroup_note = (
"available"
if summary.get("cgroup_available")
else "unavailable in this environment; proc cgroup marker archived when possible"
)
cgroup_artifact_count = len(summary.get("cgroup_artifacts") or [])
effective_kernel_modes = sorted(
{
mode
for result in cases.values()
for mode in (result.get("kernel_cache_modes") or [])
if mode and mode != "not-applicable"
}
)
effective_cache_note = ", ".join(str(mode) for mode in effective_kernel_modes) or "not recorded"
rows = []
for case, result in cases.items():
elapsed = result.get("elapsed_ms", {})
pss = result.get("pss_bytes", {})
rss = result.get("rss_bytes", {})
threads = result.get("thread_count", {})
fds = result.get("fd_count", {})
time_rss = result.get("time_max_rss_bytes", {})
failures = int(result.get("failures", 0) or 0)
cdp_page_p50 = result.get("cdp_page_elapsed_p50_ms")
cdp_page_p95 = result.get("cdp_page_elapsed_p95_ms")
binary_size = result.get("binary_bytes")
stripped_size = result.get("stripped_binary_bytes")
tar_gz_size = result.get("tar_gz_bytes")
image_uncompressed_size = result.get("image_uncompressed_bytes")
image_compressed_size = result.get("image_compressed_bytes")
deploy_size = ""
if binary_size is not None or stripped_size is not None or tar_gz_size is not None:
deploy_size = (
f"<div>bin {_size(binary_size)}</div>"
f"<div class=\"metric\">stripped {_size(stripped_size)}</div>"
f"<div class=\"metric\">tar.gz {_size(tar_gz_size)}</div>"
)
if image_uncompressed_size is not None or image_compressed_size is not None:
deploy_size += (
f"<div>rootfs {_size(image_uncompressed_size)}</div>"
f"<div class=\"metric\">rootfs.tar.gz {_size(image_compressed_size)}</div>"
)
rows.append(
"<tr>"
f"<th>{html.escape(str(case))}</th>"
f"<td>{_status(failures == 0)}</td>"
f"<td>{_cell(elapsed.get('p50'))}</td>"
f"<td>{_cell(elapsed.get('p95'))}</td>"
f"<td>{_cell(cdp_page_p50)}</td>"
f"<td>{_cell(cdp_page_p95)}</td>"
f"<td>{deploy_size}</td>"
f"<td>{_mib(pss.get('p50'))}</td>"
f"<td>{_mib(rss.get('p50'))}</td>"
f"<td>{_mib(time_rss.get('p50'))}</td>"
f"<td>{_cell(threads.get('p50'))}</td>"
f"<td>{_cell(fds.get('p50'))}</td>"
f"<td>{failures}</td>"
"</tr>"
)
gate_table = ""
if isinstance(gate_rows, list) and gate_rows:
rows_html = []
for row in gate_rows:
if not isinstance(row, dict):
continue
rows_html.append(
"<tr>"
f"<th>{html.escape(str(row.get('gate')))}</th>"
f"<td>{_status(row.get('ok') is True)}</td>"
f"<td>{html.escape(str(row.get('actual')))}</td>"
f"<td>{html.escape(str(row.get('required')))}</td>"
f"<td>{html.escape(str(row.get('failure_kind') or ''))}</td>"
"</tr>"
)
if rows_html:
gate_table = (
"<h3>Startup Formal Gates</h3>"
"<table>"
"<thead><tr><th>gate</th><th>status</th><th>actual</th><th>required</th><th>failure kind</th></tr></thead>"
f"<tbody>{''.join(rows_html)}</tbody>"
"</table>"
)
return (
f"<p class=\"section-note\">Profile: <code>{html.escape(profile)}</code>. "
f"/usr/bin/time -v: {html.escape(time_note)}; artifacts: {artifact_count}. "
f"cgroup raw files: {html.escape(cgroup_note)}; artifacts: {cgroup_artifact_count}. "
f"OS cache mode: {html.escape(effective_cache_note)}"
f"{' (drop-cache requested)' if summary.get('drop_os_cache') else ''}.</p>"
f"{gate_table}"
"<table>"
"<thead><tr><th>case</th><th>gate</th><th>p50 ms</th><th>p95 ms</th><th>CDP page p50 ms</th><th>CDP page p95 ms</th><th>deploy size</th><th>PSS p50</th><th>RSS p50</th><th>/usr/bin/time max RSS p50</th><th>threads p50</th><th>fds p50</th><th>failures</th></tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def _amiibo_crawler(summary: dict[str, Any]) -> str:
targets = summary.get("targets", {})
profile = _cell(summary.get("profile", "smoke"))
profile_failures = int(summary.get("profile_failures", 0) or 0)
requirements = summary.get("formal_requirements", {})
requirement_rows = []
if isinstance(requirements, dict):
for name, result in requirements.items():
if not isinstance(result, dict):
continue
requirement_rows.append(
"<tr>"
f"<th>{html.escape(str(name))}</th>"
f"<td>{_status(bool(result.get('ok')))}</td>"
f"<td>{_cell(result.get('actual'))}</td>"
f"<td>{_cell(result.get('required'))}</td>"
"</tr>"
)
requirements_table = (
"<h3>Formal Profile Requirements</h3>"
"<table>"
"<thead><tr><th>requirement</th><th>gate</th><th>actual</th><th>required</th></tr></thead>"
f"<tbody>{''.join(requirement_rows)}</tbody>"
"</table>"
if requirement_rows
else ""
)
rows = []
for target in _target_names(targets):
target_result = targets[target]
mode_results = target_result.get("modes")
result_items = mode_results.items() if isinstance(mode_results, dict) and mode_results else [("", target_result)]
for mode, result in result_items:
elapsed = result.get("elapsed_ms", {})
browser_pss = result.get("browser_peak_pss_bytes", {})
failures = int(result.get("failures", 0) or 0)
failure_kinds = result.get("failure_kinds", {})
failure_note = ""
if isinstance(failure_kinds, dict) and failure_kinds:
failure_note = ", ".join(f"{html.escape(str(kind))}: {_cell(count)}" for kind, count in sorted(failure_kinds.items()))
rows.append(
"<tr>"
f"<th>{html.escape(str(target))}</th>"
f"<td>{html.escape(str(mode))}</td>"
f"<td>{_status(failures == 0 and int(result.get('runs', 0) or 0) > 0)}</td>"
f"<td>{_cell(result.get('passes'))} / {_cell(result.get('runs'))}</td>"
f"<td>{_cell(elapsed.get('p50'))}</td>"
f"<td>{_cell(elapsed.get('p95'))}</td>"
f"<td>{_mib(browser_pss.get('p50'))}</td>"
f"<td>{_cell(result.get('assertion_failures'))}</td>"
f"<td>{failures}</td>"
f"<td>{failure_note}</td>"
"</tr>"
)
return (
f"<p>Profile: <code>{profile}</code>. Profile failures: {profile_failures}.</p>"
f"{requirements_table}"
"<table>"
"<thead><tr><th>target</th><th>mode</th><th>gate</th><th>passes</th><th>p50 ms</th><th>p95 ms</th><th>browser PSS p50</th><th>assertion failures</th><th>failures</th><th>failure kinds</th></tr></thead>"
f"<tbody>{''.join(rows)}</tbody>"
"</table>"
)
def write_benchmark_html(
*,
output_dir: Path,
versions: dict[str, Any],
summaries: list[dict[str, Any]],
publish_readiness: dict[str, Any] | None = None,
report_diff: dict[str, Any] | None = None,
) -> None:
payload = _report_payload(
output_dir=output_dir,
versions=versions,
summaries=summaries,
publish_readiness=publish_readiness,
report_diff=report_diff,
)
write_json(output_dir / "report-data.json", payload)
write_text(output_dir / "index.html", _chartjs_document(payload))