#!/usr/bin/env python3
"""Durable local run state and self-refreshing HTML for CARBON test passes."""

from __future__ import annotations

import argparse
import contextlib
import datetime as dt
import hashlib
import html
import json
import os
import re
import secrets
import shutil
import sys
import tempfile
import time
from collections.abc import Iterator
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from typing import Any

from carbon_brand import REPORT_FAVICON, schema_provenance_html
from carbon_schemas import schema_origin
from carbon_quality_confidence import assess_quality_confidence
from carbon_persona_journeys import render_persona_journeys
from carbon_promotions import select_promotion
from carbon_report_visuals import (
    REPORT_VISUAL_CSS,
    render_confidence_drivers,
    render_coverage_heatmap,
    render_decision_hero,
    render_evidence_spine,
    render_risk_matrix,
)

SCHEMA_VERSION = 1
EVIDENCE_SCHEMA = "carbon.page-evidence/v1"
EVIDENCE_CHANNELS = [
    "screenshot", "dom", "accessibility", "console", "network", "performance",
    "trace", "data", "storage", "video", "other",
]
RUN_STATUSES = {"planned", "running", "paused", "completed", "canceled"}
TEST_STATUSES = {
    "planned",
    "queued",
    "running",
    "passed",
    "failed",
    "blocked",
    "skipped",
    "canceled",
}
TERMINAL_TEST_STATUSES = {"passed", "failed", "blocked", "skipped", "canceled"}
PRIORITIES = {"P0", "P1", "P2", "P3"}
MODES = {"quick", "medium", "large"}
TEST_STRATEGIES = {"breadth", "depth", "balanced"}
QUALITY_DIMENSIONS = {
    "functional", "security", "accessibility", "performance",
    "reliability", "usability", "data", "operational",
}
OBSERVATION_STATUSES = {"passed", "failed", "blocked", "skipped"}
TEST_TRANSITIONS = {
    "planned": {"queued", "running", "blocked", "skipped", "canceled"},
    "queued": {"running", "blocked", "skipped", "canceled"},
    "running": {"passed", "failed", "blocked", "canceled"},
    "passed": set(),
    "failed": {"running", "canceled"},
    "blocked": {"running", "skipped", "canceled"},
    "skipped": {"running", "canceled"},
    "canceled": set(),
}
SECRET_PATTERNS = [
    re.compile(r"\bsk-[A-Za-z0-9_-]{16,}\b"),
    re.compile(r"\bghp_[A-Za-z0-9]{20,}\b"),
    re.compile(r"\bAKIA[0-9A-Z]{16}\b"),
    re.compile(r"\bBearer\s+[A-Za-z0-9._~+/-]{16,}\b", re.IGNORECASE),
    re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----"),
    re.compile(r"\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b"),
]

REPORT_ICON_PATHS = {
    "confidence": '<circle cx="12" cy="12" r="8"/><path d="m8.5 12 2.2 2.2 4.8-5"/><path d="M12 1v2M12 21v2M1 12h2M21 12h2"/>',
    "risk": '<path d="M12 3 2.8 20h18.4L12 3Z"/><path d="M12 9v5M12 17.5v.1"/>',
    "coverage": '<circle cx="12" cy="12" r="8"/><path d="M12 4v8l5.7 5.7"/><path d="M12 12h8"/>',
    "change": '<path d="M5 18V8a3 3 0 0 1 3-3h8"/><path d="m13 2 3 3-3 3"/><path d="M8 12h8a3 3 0 0 1 3 3v4"/><path d="m16 16 3 3 3-3"/>',
    "position": '<circle cx="12" cy="12" r="7"/><circle cx="12" cy="12" r="2"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3"/>',
    "context": '<path d="m12 2 8 4.5v9L12 20l-8-4.5v-9L12 2Z"/><path d="m4 6.5 8 4.5 8-4.5M12 11v9"/>',
    "product": '<circle cx="5" cy="6" r="2"/><circle cx="19" cy="6" r="2"/><circle cx="12" cy="18" r="2"/><path d="M7 7.2 10.7 16M17 7.2 13.3 16M7 6h10"/>',
    "execution": '<path d="M5 4h14v16H5z"/><path d="m10 8 5 4-5 4V8Z"/>',
    "safety": '<path d="M12 2 5 5v6c0 4.8 2.8 8.2 7 10 4.2-1.8 7-5.2 7-10V5l-7-3Z"/><path d="m8.5 12 2.2 2.2 4.8-5"/>',
    "budget": '<path d="M4 17a8 8 0 1 1 16 0"/><path d="m12 17 4-6"/><path d="M6.5 14H4M20 14h-2.5M12 6V4"/>',
    "evidence": '<path d="M2 12s3.7-6 10-6 10 6 10 6-3.7 6-10 6S2 12 2 12Z"/><circle cx="12" cy="12" r="2.5"/>',
    "tests": '<path d="M7 3h10v18H7z"/><path d="M9 3V1h6v2M10 9l1.5 1.5L15 7M10 15l1.5 1.5L15 13"/>',
    "findings": '<circle cx="10" cy="10" r="6"/><path d="m14.5 14.5 5 5M10 7v3M10 13v.1"/>',
    "cycle": '<path d="M20 8a8 8 0 0 0-14-3L3 8"/><path d="M3 3v5h5M4 16a8 8 0 0 0 14 3l3-3"/><path d="M21 21v-5h-5"/>',
    "quick": '<path d="m13 2-8 12h7l-1 8 8-12h-7l1-8Z"/>',
    "medium": '<path d="M4 6h16M4 12h16M4 18h16"/><circle cx="8" cy="6" r="2"/><circle cx="15" cy="12" r="2"/><circle cx="10" cy="18" r="2"/>',
    "large": '<path d="M20 15.5A8 8 0 0 1 8.5 4 8.5 8.5 0 1 0 20 15.5Z"/><path d="M16 4v3M14.5 5.5h3"/>',
    "functional": '<path d="M4 5h16v14H4z"/><path d="m8 12 2.4 2.4L16 9"/>',
    "security": '<path d="M12 2 5 5v6c0 4.8 2.8 8.2 7 10 4.2-1.8 7-5.2 7-10V5l-7-3Z"/><path d="M9 12h6M12 9v6"/>',
    "accessibility": '<circle cx="12" cy="4" r="2"/><path d="M4 8h16M12 8v13M8 21l4-7 4 7"/>',
    "performance": '<path d="M4 17a8 8 0 1 1 16 0"/><path d="m12 17 5-7"/><path d="M7 14H4M20 14h-3M12 7V4"/>',
    "reliability": '<path d="M20 8a8 8 0 0 0-14-3L3 8"/><path d="M3 3v5h5M4 16a8 8 0 0 0 14 3l3-3"/><path d="M21 21v-5h-5"/>',
    "usability": '<path d="M4 5h16v14H4z"/><path d="M8 9h8M8 13h5M8 17h3"/>',
    "data": '<ellipse cx="12" cy="5" rx="7" ry="3"/><path d="M5 5v6c0 1.7 3.1 3 7 3s7-1.3 7-3V5M5 11v6c0 1.7 3.1 3 7 3s7-1.3 7-3v-6"/>',
    "operational": '<path d="M5 4h14v16H5z"/><path d="M8 8h8M8 12h8M8 16h5"/><circle cx="17" cy="16" r="1"/>',
    "learn": '<path d="M12 3a5 5 0 0 0-5 5c0 2 1 3.3 2 4.5V16h6v-3.5c1-1.2 2-2.5 2-4.5a5 5 0 0 0-5-5Z"/><path d="M9 19h6M10 22h4M12 3V1M4 8H2M22 8h-2"/>',
    "observation": '<path d="M3 5h18v14H3z"/><path d="M7 9h10M7 13h6M7 16h4"/>',
    "hypothesis": '<path d="M9 2h6M10 2v6l-5 10a2 2 0 0 0 1.8 3h10.4a2 2 0 0 0 1.8-3L14 8V2"/><path d="M8 15h8"/>',
    "import": '<path d="M12 3v11M8 10l4 4 4-4"/><path d="M4 17v4h16v-4"/>',
    "artifact": '<path d="M6 3h9l4 4v14H6z"/><path d="M15 3v5h4M9 12h6M9 16h6"/>',
    "timeline": '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
    "unknown": '<circle cx="12" cy="12" r="9"/><path d="M9.7 9a2.5 2.5 0 1 1 3.8 2.1c-1 .6-1.5 1.1-1.5 2.4M12 17.5v.1"/>',
}


def report_icon(name: str, label: str = "") -> str:
    paths = REPORT_ICON_PATHS.get(name, REPORT_ICON_PATHS["artifact"])
    accessibility = f'role="img" aria-label="{html.escape(label)}"' if label else 'aria-hidden="true"'
    return f'<svg class="section-icon icon-{html.escape(name)}" viewBox="0 0 24 24" {accessibility} fill="none" stroke="currentColor" stroke-width="1.65" stroke-linecap="round" stroke-linejoin="round">{paths}</svg>'


def report_heading(title: str, icon: str, level: int = 2, heading_id: str = "") -> str:
    identifier = f' id="{html.escape(heading_id)}"' if heading_id else ""
    return f'<h{level}{identifier} class="icon-heading">{report_icon(icon)}<span>{html.escape(title)}</span></h{level}>'

REPORT_THEME_CSS = """
:root {
  color-scheme: dark;
  --bg: #000000;
  --panel: #070707;
  --panel-raised: #0c0c0c;
  --panel-soft: #0a0a0a;
  --text: #ffffff;
  --muted: #9b9b9b;
  --muted-strong: #d6d6d6;
  --line: #242424;
  --line-bright: #4a4a4a;
  --accent: #ffffff;
  --accent-bright: #ffffff;
  --accent-soft: rgba(255, 255, 255, .07);
  --accent-line: rgba(255, 255, 255, .28);
  --good: #72e6b1;
  --good-soft: rgba(114, 230, 177, .10);
  --bad: #ff7d7d;
  --bad-soft: rgba(255, 125, 125, .10);
  --warn: #f2cd73;
  --warn-soft: rgba(242, 205, 115, .10);
  --risk: #ff9f43;
  --risk-soft: rgba(255, 159, 67, .11);
  --risk-line: rgba(255, 159, 67, .48);
  --info: #ffffff;
  --info-soft: rgba(255, 255, 255, .07);
  --shadow: 0 24px 70px rgba(0, 0, 0, .72);
}
* { box-sizing: border-box; }
html {
  overflow-x: hidden;
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
body {
  min-height: 100vh;
  min-height: 100svh;
  margin: 0;
  background: #000000;
  color: var(--text);
  font: 15px/1.55 Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
}
img, video, canvas { max-width: 100%; height: auto; }
svg { max-width: 100%; }
button, input, select, textarea, summary, a { touch-action: manipulation; }
button, input, select, textarea { font: inherit; }
pre, code { max-width: 100%; overflow-wrap: anywhere; }
body::before {
  position: fixed;
  inset: 0;
  z-index: -1;
  content: "";
  pointer-events: none;
  background-image: linear-gradient(rgba(255, 255, 255, .025) 1px, transparent 1px);
  background-size: 100% 72px;
  mask-image: linear-gradient(to bottom, black, transparent 78%);
}
main {
  width: min(1180px, calc(100% - 32px));
  margin: 0 auto;
  padding: max(24px, env(safe-area-inset-top)) env(safe-area-inset-right) calc(88px + env(safe-area-inset-bottom)) env(safe-area-inset-left);
}
a { color: var(--accent-bright); text-underline-offset: 3px; }
a:hover { color: #d8d8d8; }
a:focus-visible, summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 6px;
}
.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  min-height: 64px;
  margin-bottom: 24px;
  padding: 10px 14px 10px 10px;
  border: 1px solid rgba(255, 255, 255, .08);
  border-radius: 4px;
  background: #000000;
}
.brand-cluster, .brand-lockup, .masthead-meta, .progress-copy, .head, .test-head, .meta {
  display: flex;
  align-items: center;
}
.brand-cluster { gap: 14px; min-width: 0; }
.brand-lockup { gap: 12px; color: var(--text); text-decoration: none; }
.brand-mark { width: 42px; height: 42px; flex: 0 0 auto; }
.brand-copy { display: grid; gap: 3px; }
.brand-wordmark { font-size: 17px; font-weight: 860; letter-spacing: .24em; line-height: 1; }
.brand-tagline { color: var(--muted); font-size: 8px; font-weight: 760; letter-spacing: .24em; line-height: 1.2; white-space: nowrap; }
.product-divider { width: 1px; height: 32px; overflow: hidden; background: #4a4a4a; color: transparent; }
.product-name { color: var(--muted-strong); font-size: 12px; font-weight: 720; letter-spacing: .12em; text-transform: uppercase; white-space: nowrap; }
.masthead-meta { justify-content: flex-end; gap: 10px; }
.updated { color: var(--muted); font-size: 12px; white-space: nowrap; }
.status-pill, .badge, .priority, .head span, .meta span {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 3px 10px;
  background: rgba(255, 255, 255, .025);
  color: var(--muted-strong);
  font-size: 11px;
  font-weight: 750;
  letter-spacing: .055em;
  text-transform: uppercase;
}
.status-running, .status-exploring { border-color: rgba(255, 255, 255, .38); background: var(--info-soft); color: #ffffff; }
.status-completed, .status-passed, .status-supported { border-color: rgba(55, 214, 189, .38); background: var(--good-soft); color: #70ead5; }
.status-failed, .status-canceled, .status-rejected { border-color: rgba(251, 113, 133, .38); background: var(--bad-soft); color: #fda4af; }
.status-blocked, .status-paused, .status-unknown { border-color: rgba(251, 191, 36, .38); background: var(--warn-soft); color: #fcd34d; }
.hero {
  position: relative;
  overflow: hidden;
  margin-bottom: 18px;
  border: 1px solid var(--accent-line);
  border-radius: 4px;
  padding: clamp(26px, 5vw, 54px);
  background: linear-gradient(135deg, rgba(255, 255, 255, .06), transparent 44%), var(--panel);
  box-shadow: var(--shadow);
}
.hero::after {
  position: absolute;
  top: -70px;
  right: -55px;
  width: 220px;
  height: 220px;
  border: 34px solid rgba(255, 255, 255, .035);
  border-radius: 34px;
  content: "";
  transform: rotate(18deg);
}
.hero > * { position: relative; z-index: 1; }
.eyebrow {
  margin: 0 0 10px;
  color: var(--accent-bright);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
}
h1 {
  max-width: 920px;
  margin: 0;
  font-size: clamp(36px, 6vw, 68px);
  line-height: .98;
  letter-spacing: -.055em;
  text-wrap: balance;
}
h2 { margin: 44px 0 14px; font-size: clamp(22px, 3vw, 30px); letter-spacing: -.035em; }
h3 { letter-spacing: -.02em; }
h3, h4, p { margin-top: .55em; }
.icon-heading { display: flex; align-items: center; gap: 11px; }
.section-icon { width: 21px; height: 21px; flex: 0 0 auto; color: #ffffff; }
.icon-heading .section-icon { width: 25px; height: 25px; }
.risk-panel .section-icon, .risk-panel .icon-heading, .risk-text { color: var(--risk); }
.section-kicker { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 10px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; }
.section-kicker .section-icon { width: 17px; height: 17px; }
.panel > h2:first-child { margin-top: 0; }
.target-line {
  max-width: 850px;
  margin: 18px 0 0;
  color: var(--muted-strong);
  font-size: 15px;
  overflow-wrap: anywhere;
}
.progress-shell {
  margin-top: 28px;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px;
  background: rgba(0, 0, 0, .28);
}
.progress {
  display: block;
  width: 100%;
  height: 10px;
  overflow: hidden;
  border: 0;
  border-radius: 999px;
  appearance: none;
}
.progress::-webkit-progress-bar { border-radius: 999px; background: #242733; }
.progress::-webkit-progress-value { border-radius: 999px; background: #ffffff; }
.progress::-moz-progress-bar { border-radius: 999px; background: #ffffff; }
.progress-copy { justify-content: space-between; gap: 14px; margin-top: 10px; color: var(--muted); font-size: 12px; }
.progress-copy strong { color: var(--text); font-size: 13px; }
.metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 10px;
  margin: 18px 0;
}
.metric {
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 15px 16px;
  background: linear-gradient(180deg, rgba(255,255,255,.025), transparent), var(--panel-soft);
}
.metric-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.metric .section-icon { width: 18px; height: 18px; color: #777; }
.metric span, .metric strong { display: block; }
.metric span { color: var(--muted); font-size: 11px; font-weight: 750; letter-spacing: .07em; text-transform: uppercase; }
.metric strong { margin-top: 7px; font-size: 30px; line-height: 1; letter-spacing: -.045em; }
.metric-passed strong { color: var(--good); }
.metric-failed strong { color: var(--bad); }
.metric-blocked strong { color: var(--warn); }
.metric-running strong { color: var(--info); }
.panel, .test, .finding, .card {
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 20px;
  background: linear-gradient(180deg, rgba(255,255,255,.018), transparent 35%), var(--panel);
  box-shadow: 0 14px 38px rgba(0, 0, 0, .14);
  overflow-wrap: anywhere;
}
.panel, .test, .finding, .card { margin: 12px 0; }
.spotlight { border-color: var(--accent-line); background: linear-gradient(135deg, var(--accent-soft), transparent 48%), var(--panel); }
.muted { color: var(--muted); }
.context-ledger { display: grid; border-top: 1px solid var(--line); }
.context-band { display: grid; grid-template-columns: minmax(170px,.42fr) minmax(0,1.58fr); gap: clamp(20px,4vw,54px); margin: 0; padding: 25px 0; border-bottom: 1px solid var(--line); }
.context-band, .context-band-body, .context-item, .context-item p { min-width: 0; overflow-wrap: anywhere; }
.context-band-title { display: flex; align-items: flex-start; gap: 12px; }
.context-band-title .section-icon { width: 25px; height: 25px; }
.context-band-title h3 { margin: 0; font-size: 13px; letter-spacing: .08em; text-transform: uppercase; }
.context-band-title p { margin: 6px 0 0; color: var(--muted); font-size: 12px; }
.context-band-body { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 18px 26px; }
.context-item { min-width: 0; }
.context-item h4 { margin: 0 0 7px; color: var(--muted); font-size: 10px; letter-spacing: .09em; text-transform: uppercase; }
.context-item p:last-child { margin-bottom: 0; }
.cycle { display: grid; grid-template-columns: repeat(3,1fr); border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
.cycle-step { position: relative; min-height: 168px; margin: 0; padding: 24px; border-right: 1px solid var(--line); background: var(--panel); }
.cycle-step:last-child { border-right: 0; }
.cycle-step .section-icon { width: 28px; height: 28px; }
.cycle-step h3 { margin: 28px 0 8px; font-size: 20px; }
.cycle-step p { color: var(--muted); }
.cycle-index { position: absolute; top: 18px; right: 20px; color: #444; font: 700 34px/1 ui-monospace,monospace; }
code {
  border: 1px solid rgba(255,255,255,.055);
  border-radius: 7px;
  padding: 2px 6px;
  background: #090a0f;
  color: #ffffff;
  overflow-wrap: anywhere;
}
ul, ol { padding-left: 22px; }
li + li { margin-top: 5px; }
details {
  margin-top: 14px;
  border-top: 1px solid var(--line);
  padding-top: 13px;
}
summary { color: var(--muted-strong); cursor: pointer; font-weight: 750; }
summary::marker { color: var(--accent); }
.table-wrap { max-width: 100%; overflow: auto; overscroll-behavior-inline: contain; -webkit-overflow-scrolling: touch; border: 1px solid var(--line); border-radius: 12px; }
table { width: 100%; border-collapse: collapse; background: var(--panel-soft); }
th, td { padding: 11px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
th { color: var(--muted-strong); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; }
tr:last-child td { border-bottom: 0; }
.footer {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  margin-top: 52px;
  border-top: 1px solid var(--line);
  padding-top: 20px;
  color: var(--muted);
  font-size: 12px;
}
.promotion-offer { max-width: 640px; color: var(--muted-strong); }
.promotion-offer b { display: block; margin-bottom: 4px; color: var(--risk); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; }
@media (max-width: 680px) {
  body { font-size: 16px; }
  main {
    width: min(calc(100% - 20px), 1180px);
    padding: max(10px, env(safe-area-inset-top)) env(safe-area-inset-right) calc(64px + env(safe-area-inset-bottom)) env(safe-area-inset-left);
  }
  .masthead { align-items: stretch; flex-direction: column; gap: 9px; border-radius: 15px; }
  .brand-cluster { flex-wrap: wrap; }
  .product-name { white-space: normal; }
  .masthead-meta { width: 100%; align-items: center; justify-content: space-between; flex-direction: row; gap: 8px; }
  .brand-wordmark { font-size: 13px; }
  .product-name { font-size: 12px; }
  .status-pill, .badge, .priority, .head span, .meta span { min-height: 32px; font-size: 11px; }
  button, summary, [role="button"] { min-height: 44px; }
  .brand-lockup, .footer a { min-height: 44px; }
  .footer a { display: inline-flex; align-items: center; }
  input, select, textarea { min-height: 44px; font-size: 16px; }
  .eyebrow, .section-label, .metric small, th { font-size: 11px; line-height: 1.35; }
  .hero { border-radius: 20px; padding: 28px 20px; }
  .hero::after { opacity: .65; }
  .hero::after { right: -120px; }
  h1 { font-size: clamp(34px, 12vw, 52px); }
  .progress-copy, .footer { align-items: flex-start; flex-direction: column; }
  .context-band { grid-template-columns: 1fr; gap: 16px; }
  .context-band-body { grid-template-columns: 1fr; }
  .cycle { grid-template-columns: 1fr; }
  .cycle-step { border-right: 0; border-bottom: 1px solid var(--line); }
  .cycle-step:last-child { border-bottom: 0; }
}
@media (max-width: 420px) {
  .product-divider, .product-name { display: none; }
  .brand-cluster { flex-wrap: nowrap; }
  .masthead-meta { align-items: flex-start; flex-direction: column; }
  .updated { white-space: normal; overflow-wrap: anywhere; }
  .hero { padding: 24px 18px; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; animation: none !important; }
}
"""


def carbon_lockup(product: str) -> str:
    return f"""
    <div class="brand-cluster">
      <a class="brand-lockup" href="https://testers.ai" aria-label="CARBON — Engineering Confidence">
        <svg class="brand-mark" viewBox="0 0 256 256" role="img" aria-hidden="true">
          <rect width="256" height="256" fill="#000000"/>
          <path fill="#ffffff" d="M56 34h112l48 48-48 48V96l-16-16H94c-15 0-22 7-22 22v52c0 15 7 22 22 22h104v46H56c-21 0-32-11-32-32V66c0-21 11-32 32-32Z"/>
        </svg>
        <span class="brand-copy"><span class="brand-wordmark">CARBON</span><span class="brand-tagline">ENGINEERING CONFIDENCE</span></span>
      </a>
      <span class="product-divider" aria-hidden="true">/</span>
      <span class="product-name">{html.escape(product)}</span>
    </div>
    """


def utc_now() -> str:
    return (
        dt.datetime.now(dt.timezone.utc)
        .replace(microsecond=0)
        .isoformat()
        .replace("+00:00", "Z")
    )


def run_id() -> str:
    stamp = dt.datetime.now(dt.timezone.utc).strftime("%Y%m%dT%H%M%SZ")
    return f"{stamp}-{secrets.token_hex(3)}"


def atomic_json(path: Path, value: Any) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    with tempfile.NamedTemporaryFile(
        "w", encoding="utf-8", dir=path.parent, delete=False
    ) as handle:
        json.dump(value, handle, indent=2, ensure_ascii=False)
        handle.write("\n")
        temporary = Path(handle.name)
    os.replace(temporary, path)


def redact(value: Any) -> Any:
    if isinstance(value, str):
        result = value
        for pattern in SECRET_PATTERNS:
            result = pattern.sub("[REDACTED]", result)
        return result
    if isinstance(value, list):
        return [redact(item) for item in value]
    if isinstance(value, dict):
        return {key: redact(item) for key, item in value.items()}
    return value


def reject_embedded_secrets(value: Any) -> None:
    serialized = json.dumps(value, ensure_ascii=False)
    for pattern in SECRET_PATTERNS:
        if pattern.search(serialized):
            raise ValueError(
                "Plan contains a credential-like value. Use environment-variable or ignored-file names only."
            )


@contextlib.contextmanager
def run_lock(run_dir: Path, timeout: float = 10.0) -> Iterator[None]:
    lock_path = run_dir / ".state.lock"
    deadline = time.monotonic() + timeout
    while True:
        try:
            descriptor = os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)
            os.write(descriptor, f"{os.getpid()} {time.time()}".encode())
            os.close(descriptor)
            break
        except FileExistsError:
            try:
                if time.time() - lock_path.stat().st_mtime > 120:
                    lock_path.unlink()
                    continue
            except FileNotFoundError:
                continue
            if time.monotonic() >= deadline:
                raise TimeoutError(f"Timed out waiting for run lock: {lock_path}")
            time.sleep(0.1)
    try:
        yield
    finally:
        lock_path.unlink(missing_ok=True)


def load_json(path: Path) -> dict[str, Any]:
    try:
        value = json.loads(path.read_text(encoding="utf-8"))
    except FileNotFoundError as error:
        raise ValueError(f"Missing file: {path}") from error
    except json.JSONDecodeError as error:
        raise ValueError(f"Invalid JSON in {path}: {error}") from error
    if not isinstance(value, dict):
        raise ValueError(f"Expected a JSON object in {path}")
    return value


def load_state(run_dir: Path) -> dict[str, Any]:
    return load_json(run_dir / "run.json")


def list_of_strings(value: Any, field: str) -> list[str]:
    if value is None:
        return []
    if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
        raise ValueError(f"{field} must be an array of strings")
    return value


def normalize_open_test_steps(value: Any, field: str) -> tuple[list[str], list[dict[str, str]]]:
    """Accept both CARBON string steps and OpenTest.ai structured testcase steps."""
    if value is None:
        return [], []
    if not isinstance(value, list):
        raise ValueError(f"{field} must be an array")
    strings: list[str] = []
    structured: list[dict[str, str]] = []
    allowed = ("description", "thinking", "action", "target", "value", "expected")
    for index, item in enumerate(value):
        if isinstance(item, str):
            strings.append(item)
            continue
        if not isinstance(item, dict):
            raise ValueError(f"{field}[{index}] must be a string or an OpenTest.ai step object")
        step = {key: str(item.get(key) or "") for key in allowed}
        if not any(step.values()):
            raise ValueError(f"{field}[{index}] is an empty OpenTest.ai step object")
        structured.append(step)
        strings.append(step["description"] or step["action"] or step["expected"])
    return strings, structured


def normalize_test(raw: Any, seen: set[str]) -> dict[str, Any]:
    if not isinstance(raw, dict):
        raise ValueError("Each test must be an object")
    title = str(raw.get("title", "")).strip()
    if not title:
        raise ValueError("OpenTest.ai testcase requires the title field")
    supplied_id = str(raw.get("id", "")).strip()
    generated_id = f"OTAI-{hashlib.sha256((title + json.dumps(raw, sort_keys=True, default=str)).encode()).hexdigest()[:12].upper()}"
    test_id = supplied_id or generated_id
    priority = str(raw.get("priority") or "P2").upper()
    if not re.fullmatch(r"[A-Za-z0-9._:-]+", test_id):
        raise ValueError(f"Invalid test id: {test_id!r}")
    if test_id in seen:
        raise ValueError(f"Duplicate test id: {test_id}")
    if priority not in PRIORITIES:
        raise ValueError(f"Test {test_id} priority must be P0, P1, P2, or P3")
    seen.add(test_id)
    estimated = int(raw.get("estimatedSeconds", 60))
    if estimated < 1:
        raise ValueError(f"Test {test_id} estimatedSeconds must be positive")
    flags = raw.get("flags", {})
    if flags is None:
        flags = {}
    if not isinstance(flags, dict):
        raise ValueError(f"Test {test_id} flags must be an object")
    predicted_outcome = str(raw.get("predictedOutcome", "unknown")).strip().lower()
    if predicted_outcome not in {"pass", "fail", "unknown"}:
        raise ValueError(f"Test {test_id} predictedOutcome must be pass, fail, or unknown")
    step_strings, open_test_steps = normalize_open_test_steps(raw.get("steps"), f"{test_id}.steps")
    normalized = {
        "schemaOrigin": schema_origin("testcase"),
        "id": test_id,
        "title": title,
        "goal": str(raw.get("goal") or raw.get("claim") or title),
        "priority": priority,
        "type": str(raw.get("type", "functional")),
        "dimensions": list_of_strings(raw.get("dimensions"), f"{test_id}.dimensions"),
        "flow": str(raw.get("flow") or raw.get("goal") or ""),
        "item": str(raw.get("item", "")),
        "lifecycle": str(raw.get("lifecycle", "candidate")),
        "surfaceIds": list_of_strings(raw.get("surfaceIds"), f"{test_id}.surfaceIds"),
        "flowIds": list_of_strings(raw.get("flowIds"), f"{test_id}.flowIds"),
        "apiOperation": str(raw.get("apiOperation", "")),
        "claim": str(raw.get("claim", "")),
        "userPromise": str(raw.get("userPromise", "")),
        "evidenceRequirements": list_of_strings(
            raw.get("evidenceRequirements"), f"{test_id}.evidenceRequirements"
        ),
        "reason": str(raw.get("reason", "")),
        "provenance": list_of_strings(raw.get("provenance"), f"{test_id}.provenance"),
        "prerequisites": list_of_strings(
            raw.get("prerequisites"), f"{test_id}.prerequisites"
        ),
        "data": list_of_strings(raw.get("data"), f"{test_id}.data"),
        "steps": step_strings,
        "oracle": str(raw.get("oracle", "")),
        "oracleBasis": str(raw.get("oracleBasis", "")),
        "predictedOutcome": predicted_outcome,
        "predictionBasis": str(raw.get("predictionBasis", "")),
        "estimatedSeconds": estimated,
        "dependencies": list_of_strings(
            raw.get("dependencies"), f"{test_id}.dependencies"
        ),
        "parallelGroup": str(raw.get("parallelGroup", "")),
        "adapter": str(raw.get("adapter", "isolated-browser")),
        "flags": {str(key): bool(value) for key, value in flags.items()},
        "status": "planned",
        "attempts": 0,
        "actual": "",
        "evidence": [],
        "observations": [],
        "error": "",
        "startedAt": None,
        "finishedAt": None,
    }
    if open_test_steps:
        normalized["openTestSteps"] = open_test_steps
    return normalized


def reject_dependency_cycles(tests: list[dict[str, Any]]) -> None:
    dependencies = {test["id"]: test["dependencies"] for test in tests}
    visiting: list[str] = []
    visited: set[str] = set()

    def visit(test_id: str) -> None:
        if test_id in visited:
            return
        if test_id in visiting:
            cycle = visiting[visiting.index(test_id) :] + [test_id]
            raise ValueError(f"Dependency cycle detected: {' → '.join(cycle)}")
        visiting.append(test_id)
        for dependency in dependencies[test_id]:
            visit(dependency)
        visiting.pop()
        visited.add(test_id)

    for test_id in dependencies:
        visit(test_id)


def initialize(args: argparse.Namespace) -> Path:
    target_root = Path(args.root).expanduser().resolve()
    state_root = (
        Path(args.state_root).expanduser().resolve() if args.state_root else target_root
    )
    plan_path = Path(args.plan).expanduser().resolve()
    plan = load_json(plan_path)
    reject_embedded_secrets(plan)
    mode = args.mode.lower()
    if mode not in MODES:
        raise ValueError(f"Unknown mode: {mode}")
    identifier = args.run_id or run_id()
    run_dir = state_root / ".carbon" / "runs" / identifier
    if run_dir.exists():
        raise ValueError(f"Run directory already exists: {run_dir}")
    tests_raw = plan.get("tests")
    if not isinstance(tests_raw, list) or not tests_raw:
        raise ValueError("Plan requires a non-empty tests array")
    seen: set[str] = set()
    tests = [normalize_test(raw, seen) for raw in tests_raw]
    known = {test["id"] for test in tests}
    for test in tests:
        missing = [
            dependency for dependency in test["dependencies"] if dependency not in known
        ]
        if missing:
            raise ValueError(f"Test {test['id']} has unknown dependencies: {missing}")
    reject_dependency_cycles(tests)

    default_strategy = {"quick": "breadth", "medium": "balanced", "large": "balanced"}[mode]
    strategy = plan.get("testStrategy") if isinstance(plan.get("testStrategy"), dict) else {}
    selected_strategy = str(strategy.get("selected") or default_strategy).lower()
    if selected_strategy not in TEST_STRATEGIES:
        raise ValueError("testStrategy.selected must be breadth, depth, or balanced")
    if "personas" not in plan:
        default_personas = [
            {"id": "PERSONA-PRIMARY", "name": "Primary user", "role": "frequent task owner", "goal": "complete the scoped task efficiently"},
            {"id": "PERSONA-FIRST-TIME", "name": "First-time user", "role": "new or infrequent user", "goal": "understand and complete the task without prior knowledge"},
            {"id": "PERSONA-POWER", "name": "Power user", "role": "advanced high-frequency user", "goal": "complete the workflow quickly across realistic state"},
            {"id": "PERSONA-ACCESS", "name": "Access-needs user", "role": "keyboard, zoom, or assistive-technology user", "goal": "complete the same promise without visual-only or mouse-only dependency"},
            {"id": "PERSONA-RECOVERY", "name": "Interrupted user", "role": "user recovering from failure or interruption", "goal": "resume without duplicate work or data loss"},
            {"id": "PERSONA-BOUNDARY", "name": "Boundary-role user", "role": "wrong-role, wrong-owner, or neighboring-tenant user", "goal": "observe only permitted data and actions"},
        ]
        raw_personas = default_personas[: {"quick": 2, "medium": 4, "large": 6}[mode]]
    else:
        raw_personas = plan.get("personas") if isinstance(plan.get("personas"), list) else []
    personas = []
    for index, raw in enumerate(raw_personas):
        if not isinstance(raw, dict):
            raise ValueError("personas must contain objects")
        persona_id = str(raw.get("id") or f"PERSONA-{index + 1:02d}").strip()
        if not persona_id:
            raise ValueError("Each persona requires an id")
        goal = str(raw.get("goal") or "complete the scoped task")
        personas.append({
            "id": persona_id,
            "name": str(raw.get("name") or persona_id),
            "role": str(raw.get("role") or "product user"),
            "goal": goal,
            "intent": str(raw.get("intent") or goal),
            "plan": [str(value) for value in raw.get("plan", []) if str(value).strip()] or [
                "Orient to the product and find the likely starting point.",
                f"Attempt to {goal} using realistic behavior and data.",
                "Verify the visible result and any persisted or downstream effect.",
                "Explore an adjacent or recovery path and record delight, confusion, friction, and surprise.",
            ],
            "requiredData": [str(value) for value in raw.get("requiredData", []) if str(value).strip()],
            "successSignals": [str(value) for value in raw.get("successSignals", []) if str(value).strip()],
            "risks": [str(value) for value in raw.get("risks", []) if str(value).strip()],
            "derivedFrom": [str(value) for value in raw.get("derivedFrom", []) if str(value).strip()],
        })
    if not personas:
        raise ValueError("Every CARBON run requires at least one planned persona journey")
    if "explorationCharters" not in plan:
        raw_charters = [
            {"id": "CHARTER-BREADTH", "strategy": "breadth", "title": "Map the reachable experience", "mission": "Sample changed and adjacent routes, widgets, states, transitions, and linked APIs."},
            {"id": "CHARTER-DEPTH", "strategy": "depth", "title": "Drill into the riskiest journey", "mission": "Follow the highest-risk behavior through boundaries, interruption, retry, recovery, roles, and persistence."},
        ]
        if selected_strategy == "breadth":
            raw_charters = raw_charters[:1]
        elif selected_strategy == "depth":
            raw_charters = raw_charters[1:]
    else:
        raw_charters = plan.get("explorationCharters") if isinstance(plan.get("explorationCharters"), list) else []
    exploration_charters = []
    for index, raw in enumerate(raw_charters):
        if not isinstance(raw, dict):
            raise ValueError("explorationCharters must contain objects")
        charter_strategy = str(raw.get("strategy") or selected_strategy).lower()
        if charter_strategy == "balanced":
            charter_strategy = "breadth" if index == 0 else "depth"
        if charter_strategy not in {"breadth", "depth"}:
            raise ValueError("Each exploration charter strategy must be breadth or depth")
        exploration_charters.append({
            "id": str(raw.get("id") or f"CHARTER-{index + 1:02d}"),
            "strategy": charter_strategy,
            "title": str(raw.get("title") or "Explore the scoped product behavior"),
            "mission": str(raw.get("mission") or "Observe product behavior and adapt from evidence."),
            "stopConditions": [str(value) for value in raw.get("stopConditions", []) if str(value).strip()],
        })

    browser_contract = plan.get("browserContract")
    if browser_contract is not None:
        if not isinstance(browser_contract, dict) or browser_contract.get("protocol") != "carbon-browser/v1":
            raise ValueError("browserContract must be a carbon-browser/v1 object")
        for field in ("requiredOperations", "requiredEvidence", "desiredEvidence", "adapterFamilies"):
            if field in browser_contract:
                list_of_strings(browser_contract.get(field), f"browserContract.{field}")

    now = utc_now()
    state = {
        "schemaVersion": SCHEMA_VERSION,
        "runId": identifier,
        "title": args.title or str(plan.get("title", "CARBON test pass")),
        "mode": mode,
        "target": args.target or str(plan.get("target", "")),
        "targetRoot": str(target_root),
        "stateRoot": str(state_root),
        "environment": str(plan.get("environment", "local isolated test environment")),
        "revision": str(plan.get("revision", "")),
        "scope": list_of_strings(plan.get("scope"), "scope"),
        "exclusions": list_of_strings(plan.get("exclusions"), "exclusions"),
        "assumptions": list_of_strings(plan.get("assumptions"), "assumptions"),
        "detectedFlows": list_of_strings(plan.get("detectedFlows"), "detectedFlows"),
        "detectedItems": list_of_strings(plan.get("detectedItems"), "detectedItems"),
        "availableAdapters": list_of_strings(
            plan.get("availableAdapters"), "availableAdapters"
        ),
        "browserContract": browser_contract,
        "setupDependencies": list_of_strings(
            plan.get("setupDependencies"), "setupDependencies"
        ),
        "credentialSources": list_of_strings(
            plan.get("credentialSources"), "credentialSources"
        ),
        "stopConditions": list_of_strings(plan.get("stopConditions"), "stopConditions"),
        "residualUncertainty": list_of_strings(
            plan.get("residualUncertainty"), "residualUncertainty"
        ),
        "concurrency": max(1, int(plan.get("concurrency", 1))),
        "estimatedTests": len(tests),
        "estimatedMinutes": int(
            plan.get(
                "estimatedMinutes",
                max(1, round(sum(test["estimatedSeconds"] for test in tests) / 60)),
            )
        ),
        "sourcePlan": str(plan_path),
        "status": "planned",
        "createdAt": now,
        "updatedAt": now,
        "startedAt": None,
        "finishedAt": None,
        "pauseReason": "",
        "cutLine": "",
        "currentTestId": None,
        "tests": tests,
        "findings": [],
        "testStrategy": {
            "requested": str(strategy.get("requested") or "auto"),
            "selected": selected_strategy,
            "rationale": str(strategy.get("rationale") or f"{mode.title()} runs default to {selected_strategy} coverage."),
        },
        "personas": personas,
        "explorationCharters": exploration_charters,
        "personaJourneyContract": {
            "required": True,
            "plannedCount": len(personas),
            "emotionalMeasures": ["joy", "happiness", "surprise"],
            "htmlReport": "persona-journeys.html",
            "markdownReport": "persona-journeys.md",
        },
        "evidenceManifest": "evidence/manifest.json",
        "evidenceSummary": {
            "pages": 0, "artifacts": 0, "screenshots": 0,
            "consoleArtifacts": 0, "networkArtifacts": 0,
            "issues": 0, "highSignals": 0, "personaFeedback": 0,
            "personasRepresented": 0, "explorationObservations": 0,
            "breadthObservations": 0, "depthObservations": 0, "channels": {},
        },
        "lastEvent": "Plan created",
    }
    run_dir.mkdir(parents=True, exist_ok=False)
    shutil.copy2(plan_path, run_dir / "plan.json")
    atomic_json(
        run_dir / "evidence" / "manifest.json",
        {
            "schema": EVIDENCE_SCHEMA,
            "capturePolicy": {
                "everyTestedPage": True,
                "channels": EVIDENCE_CHANNELS,
                "textTelemetry": "bounded and redacted before persistence",
                "binaryEvidence": "capture only with synthetic or explicitly safe visible data",
                "missingChannels": "recorded as evidence gaps, never invented",
            },
            "pages": [], "issues": [], "summary": state["evidenceSummary"],
            "strategy": state["testStrategy"],
            "personas": state["personas"],
            "explorationCharters": state["explorationCharters"],
            "personaFeedback": [],
            "explorationObservations": [],
            "updatedAt": now,
        },
    )
    state["qualityConfidence"] = assess_quality_confidence(state, load_evidence_manifest(run_dir))
    atomic_json(run_dir / "run.json", state)
    append_event(run_dir, {"kind": "run-created", "detail": "Plan created"})
    render(run_dir, state)
    return run_dir


def append_event(run_dir: Path, event: dict[str, Any]) -> None:
    payload = redact({"time": utc_now(), **event})
    with (run_dir / "events.jsonl").open("a", encoding="utf-8") as handle:
        handle.write(json.dumps(payload, ensure_ascii=False) + "\n")


def find_test(state: dict[str, Any], test_id: str) -> dict[str, Any]:
    for test in state["tests"]:
        if test["id"] == test_id:
            return test
    raise ValueError(f"Unknown test id: {test_id}")


def save_and_render(
    run_dir: Path, state: dict[str, Any], event: dict[str, Any]
) -> None:
    state["updatedAt"] = utc_now()
    state["lastEvent"] = str(event.get("detail", event.get("kind", "Updated")))
    state["qualityConfidence"] = assess_quality_confidence(state, load_evidence_manifest(run_dir))
    atomic_json(run_dir / "run.json", redact(state))
    append_event(run_dir, event)
    render(run_dir, state)


def start_run(args: argparse.Namespace) -> None:
    run_dir = Path(args.run_dir).expanduser().resolve()
    with run_lock(run_dir):
        state = load_state(run_dir)
        if state["status"] in {"completed", "canceled"}:
            raise ValueError(f"Cannot start a {state['status']} run")
        state["status"] = "running"
        state["startedAt"] = state["startedAt"] or utc_now()
        state["pauseReason"] = ""
        save_and_render(
            run_dir, state, {"kind": "run-started", "detail": "Run started"}
        )


def update_test(args: argparse.Namespace) -> None:
    run_dir = Path(args.run_dir).expanduser().resolve()
    status = args.status.lower()
    if status not in TEST_STATUSES:
        raise ValueError(f"Unknown test status: {status}")
    with run_lock(run_dir):
        state = load_state(run_dir)
        if state["status"] in {"completed", "canceled"}:
            raise ValueError(f"Cannot update a {state['status']} run")
        test = find_test(state, args.test_id)
        current_status = test["status"]
        if (
            status != current_status
            and status not in TEST_TRANSITIONS[current_status]
            and not args.force
        ):
            raise ValueError(
                f"Illegal test transition {test['id']}: {current_status} → {status}; "
                "use --force only with explicit rerun evidence"
            )
        if status == "running":
            unmet = [
                dependency
                for dependency in test["dependencies"]
                if find_test(state, dependency)["status"] != "passed"
            ]
            if unmet and not args.force:
                raise ValueError(
                    f"Test {test['id']} has unmet dependencies {unmet}; use --force only with explicit evidence"
                )
            test["attempts"] += 1
            test["startedAt"] = test["startedAt"] or utc_now()
            state["status"] = "running"
            state["startedAt"] = state["startedAt"] or utc_now()
            state["currentTestId"] = test["id"]
        if status in TERMINAL_TEST_STATUSES:
            test["finishedAt"] = utc_now()
            if state.get("currentTestId") == test["id"]:
                state["currentTestId"] = None
        test["status"] = status
        if args.actual is not None:
            test["actual"] = redact(args.actual)
        if args.evidence:
            test["evidence"].extend(redact(args.evidence))
        if args.error is not None:
            test["error"] = redact(args.error)
        save_and_render(
            run_dir,
            state,
            {
                "kind": "test-updated",
                "testId": test["id"],
                "status": status,
                "detail": f"{test['id']} → {status}",
                "actual": test["actual"],
                "evidence": test["evidence"],
                "error": test["error"],
            },
        )


def record_observation(args: argparse.Namespace) -> None:
    run_dir = Path(args.run_dir).expanduser().resolve()
    with run_lock(run_dir):
        state = load_state(run_dir)
        if state["status"] in {"completed", "canceled"}:
            raise ValueError(f"Cannot update a {state['status']} run")
        test = find_test(state, args.test_id)
        observations = test.setdefault("observations", [])
        dimensions = sorted(set(args.dimension))
        observation = redact({
            "id": f"{test['id']}-OBS-{len(observations) + 1:03d}",
            "title": args.title,
            "status": args.status,
            "actual": args.actual,
            "evidence": args.evidence,
            "dimensions": dimensions,
            "severity": args.severity,
            "recordedAt": utc_now(),
        })
        observations.append(observation)
        save_and_render(
            run_dir,
            state,
            {
                "kind": "test-observation-recorded",
                "testId": test["id"],
                "observationId": observation["id"],
                "status": observation["status"],
                "detail": f"{observation['id']} → {observation['status']}",
            },
        )


def add_finding(args: argparse.Namespace) -> None:
    run_dir = Path(args.run_dir).expanduser().resolve()
    with run_lock(run_dir):
        state = load_state(run_dir)
        finding_id = f"F-{len(state['findings']) + 1:03d}"
        repro = redact(
            {
                "schema": "carbon.repro/v1",
                "schemaOrigin": schema_origin("bug"),
                "bug_title": args.title,
                "findingId": finding_id,
                "runId": state["runId"],
                "revision": state.get("revision", ""),
                "target": state.get("target", ""),
                "environment": args.environment or state.get("environment", ""),
                "testId": args.test_id or "",
                "testIds": [args.test_id] if args.test_id else [],
                "category": args.category or "",
                "dimensions": sorted(set(args.dimension)),
                "startingState": args.starting_state or "",
                "steps": args.steps,
                "expected": args.expected or "",
                "actual": args.actual or "",
                "evidence": args.evidence,
                "codeLocations": args.code_location,
                "regressionTests": args.regression_test,
                "createdAt": utc_now(),
            }
        )
        repro_dir = run_dir / "repro" / finding_id
        repro_dir.mkdir(parents=True, exist_ok=False)
        atomic_json(repro_dir / "repro.json", repro)
        finding = redact(
            {
                "id": finding_id,
                "schemaOrigin": schema_origin("bug"),
                "bug_title": args.title,
                "severity": args.severity.lower(),
                "title": args.title,
                "detail": args.detail,
                "evidence": args.evidence,
                "testId": args.test_id or "",
                "testIds": [args.test_id] if args.test_id else [],
                "category": args.category or "",
                "dimensions": sorted(set(args.dimension)),
                "codeLocations": args.code_location,
                "reproPath": str(repro_dir / "repro.json"),
                "time": utc_now(),
            }
        )
        state["findings"].append(finding)
        save_and_render(
            run_dir,
            state,
            {"kind": "finding-added", "detail": f"{finding['id']}: {finding['title']}"},
        )


def pause_run(args: argparse.Namespace) -> None:
    run_dir = Path(args.run_dir).expanduser().resolve()
    with run_lock(run_dir):
        state = load_state(run_dir)
        if state["status"] not in {"planned", "running", "paused"}:
            raise ValueError(f"Cannot pause a {state['status']} run")
        state["status"] = "paused"
        state["pauseReason"] = args.reason
        save_and_render(
            run_dir,
            state,
            {"kind": "run-paused", "detail": f"Paused: {args.reason}"},
        )


def resume_run(args: argparse.Namespace) -> None:
    run_dir = Path(args.run_dir).expanduser().resolve()
    with run_lock(run_dir):
        state = load_state(run_dir)
        if state["status"] != "paused":
            raise ValueError("Only a paused run can resume")
        state["status"] = "running"
        state["pauseReason"] = ""
        state["startedAt"] = state["startedAt"] or utc_now()
        save_and_render(
            run_dir, state, {"kind": "run-resumed", "detail": "Run resumed"}
        )


def complete_run(args: argparse.Namespace) -> None:
    run_dir = Path(args.run_dir).expanduser().resolve()
    with run_lock(run_dir):
        state = load_state(run_dir)
        unfinished = [
            test["id"]
            for test in state["tests"]
            if test["status"] in {"planned", "queued", "running"}
        ]
        evidence_manifest = load_evidence_manifest(run_dir)
        pages = evidence_manifest.get("pages") if isinstance(evidence_manifest.get("pages"), list) else []
        browser_test_ids = {
            test["id"]
            for test in state["tests"]
            if test["status"] in {"passed", "failed"}
            and any(token in str(test.get("adapter", "")).lower() for token in ("browser", "playwright", "selenium", "cypress", "visual", "accessibility"))
        }
        linked_test_ids = {
            str(test_id)
            for page in pages
            for test_id in (page.get("testIds") if isinstance(page.get("testIds"), list) else [])
        }
        missing_page_evidence = sorted(browser_test_ids - linked_test_ids)
        incomplete_pages: list[str] = []
        for page in pages:
            captured = {str(artifact.get("kind")) for artifact in page.get("artifacts", []) if isinstance(artifact, dict)}
            accounted = captured | {str(value) for value in page.get("unavailable", [])}
            missing_channels = sorted(set(EVIDENCE_CHANNELS) - accounted)
            if missing_channels:
                incomplete_pages.append(f"{page.get('id', 'page')}: {', '.join(missing_channels)}")
            if "screenshot" in captured and not page.get("visualReviews"):
                incomplete_pages.append(f"{page.get('id', 'page')}: screenshot visual review not recorded")
        perspective_gaps: list[str] = []
        latest_persona_feedback = {
            str(value.get("personaId")): value
            for value in evidence_manifest.get("personaFeedback", [])
            if isinstance(value, dict) and value.get("personaId")
        }
        represented_personas = {
                str(value.get("personaId"))
                for value in latest_persona_feedback.values()
                if isinstance(value, dict) and value.get("personaId") and value.get("status") in {"observed", "stopped", "reviewed"}
            }
        planned_personas = {str(value.get("id")) for value in state.get("personas", []) if isinstance(value, dict) and value.get("id")}
        missing_personas = sorted(planned_personas - represented_personas)
        if missing_personas:
            perspective_gaps.append("planned personas without completed journey feedback: " + ", ".join(missing_personas))
        explored_personas = {
            str(value.get("personaId"))
            for value in evidence_manifest.get("explorationObservations", [])
            if isinstance(value, dict) and value.get("personaId") and value.get("status") != "blocked"
        }
        missing_explorers = sorted(planned_personas - explored_personas)
        if missing_explorers:
            perspective_gaps.append("planned personas without exploratory observations: " + ", ".join(missing_explorers))
        observed_charters = {
                str(value.get("charterId"))
                for value in evidence_manifest.get("explorationObservations", [])
                if isinstance(value, dict) and value.get("charterId") and value.get("status") != "blocked"
            }
        planned_charters = {str(value.get("id")) for value in state.get("explorationCharters", []) if isinstance(value, dict) and value.get("id")}
        missing_charters = sorted(planned_charters - observed_charters)
        if missing_charters:
            perspective_gaps.append("exploration charters without observations: " + ", ".join(missing_charters))
        missing_inputs = sorted({
            str(item)
            for value in latest_persona_feedback.values()
            for item in (value.get("missingInputs") if isinstance(value.get("missingInputs"), list) else [])
            if str(item).strip()
        })
        if missing_inputs:
            perspective_gaps.append("persona exploration needs user input: " + "; ".join(missing_inputs))
        if not evidence_manifest.get("strategy", {}).get("selected"):
            perspective_gaps.append("breadth/depth strategy was not recorded")
        evidence_gaps = []
        if missing_page_evidence:
            evidence_gaps.append(f"executed browser tests without linked page evidence: {', '.join(missing_page_evidence)}")
        if incomplete_pages:
            evidence_gaps.append("page channels not captured or explicitly unavailable: " + "; ".join(incomplete_pages))
        evidence_gaps.extend(perspective_gaps)
        if evidence_gaps and not args.cut_line:
            raise ValueError(
                "Evidence capture is incomplete: " + " | ".join(evidence_gaps)
                + ". Capture the artifacts, mark unavailable/privacy-withheld channels, or supply --cut-line with the explicit evidence limitation."
            )
        if unfinished and not args.cut_line:
            raise ValueError(
                f"Unfinished tests remain: {unfinished}. Supply --cut-line to complete with explicit untested scope."
            )
        state["status"] = "completed"
        state["finishedAt"] = utc_now()
        state["currentTestId"] = None
        state["cutLine"] = args.cut_line or ""
        if evidence_gaps:
            state["residualUncertainty"] = list(dict.fromkeys(state.get("residualUncertainty", []) + evidence_gaps))
        save_and_render(
            run_dir,
            state,
            {
                "kind": "run-completed",
                "detail": "Run completed"
                + (" with an explicit cut line" if unfinished else ""),
                "unfinished": unfinished,
            },
        )


def cancel_run(args: argparse.Namespace) -> None:
    run_dir = Path(args.run_dir).expanduser().resolve()
    with run_lock(run_dir):
        state = load_state(run_dir)
        if state["status"] == "completed":
            raise ValueError("Cannot cancel a completed run")
        now = utc_now()
        for test in state["tests"]:
            if test["status"] == "running":
                test["status"] = "canceled"
                test["finishedAt"] = now
                test["error"] = test["error"] or f"Run canceled: {args.reason}"
        state["status"] = "canceled"
        state["finishedAt"] = now
        state["currentTestId"] = None
        save_and_render(
            run_dir,
            state,
            {"kind": "run-canceled", "detail": f"Canceled: {args.reason}"},
        )


def priority_rank(priority: str) -> int:
    return int(priority[1])


def next_tests(state: dict[str, Any]) -> list[dict[str, Any]]:
    candidates = []
    for test in state["tests"]:
        if test["status"] not in {"planned", "queued"}:
            continue
        if all(
            find_test(state, dep)["status"] == "passed" for dep in test["dependencies"]
        ):
            candidates.append(test)
    return sorted(
        candidates,
        key=lambda test: (
            priority_rank(test["priority"]),
            test["estimatedSeconds"],
            test["id"],
        ),
    )


def summary(state: dict[str, Any]) -> dict[str, Any]:
    counts = {status: 0 for status in TEST_STATUSES}
    for test in state["tests"]:
        counts[test["status"]] += 1
    done = sum(counts[status] for status in TERMINAL_TEST_STATUSES)
    confidence = state.get("qualityConfidence") if isinstance(state.get("qualityConfidence"), dict) else assess_quality_confidence(state)
    return {
        "runId": state["runId"],
        "status": state["status"],
        "mode": state["mode"],
        "total": len(state["tests"]),
        "done": done,
        "counts": counts,
        "currentTestId": state.get("currentTestId"),
        "nextTestIds": [test["id"] for test in next_tests(state)[:5]],
        "findings": len(state["findings"]),
        "evidence": state.get("evidenceSummary", {}),
        "qualityConfidence": confidence,
        "updatedAt": state["updatedAt"],
    }


def render_list(items: list[str]) -> str:
    if not items:
        return '<span class="muted">None recorded</span>'
    return (
        "<ul>"
        + "".join(f"<li>{html.escape(str(item))}</li>" for item in items)
        + "</ul>"
    )


def safe_evidence_href(value: Any) -> str | None:
    candidate = Path(str(value or ""))
    if candidate.is_absolute() or ".." in candidate.parts or not candidate.parts:
        return None
    return candidate.as_posix()


def load_evidence_manifest(run_dir: Path) -> dict[str, Any]:
    path = run_dir / "evidence" / "manifest.json"
    try:
        value = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError):
        return {"schema": EVIDENCE_SCHEMA, "pages": [], "issues": [], "summary": {}}
    if not isinstance(value, dict) or value.get("schema") != EVIDENCE_SCHEMA:
        return {"schema": EVIDENCE_SCHEMA, "pages": [], "issues": [], "summary": {}}
    return value


def render_evidence_manifest(run_dir: Path) -> str:
    manifest = load_evidence_manifest(run_dir)
    pages = manifest.get("pages") if isinstance(manifest.get("pages"), list) else []
    summary = manifest.get("summary") if isinstance(manifest.get("summary"), dict) else {}
    evidence_metric_icons = {"pages": "evidence", "artifacts": "artifact", "screenshots": "evidence", "consoleArtifacts": "observation", "networkArtifacts": "product", "issues": "findings", "personaFeedback": "product", "explorationObservations": "position"}
    evidence_metrics = "".join(
        f'<div class="metric"><div class="metric-top"><span>{html.escape(label)}</span>{report_icon(evidence_metric_icons.get(key, "evidence"))}</div><strong>{int(summary.get(key, 0) or 0)}</strong></div>'
        for key, label in (
            ("pages", "Pages captured"), ("artifacts", "Artifacts"),
            ("screenshots", "Screenshots"), ("consoleArtifacts", "Console logs"),
            ("networkArtifacts", "Network logs"), ("issues", "Artifact signals"),
            ("personaFeedback", "Persona feedback"), ("explorationObservations", "Explore observations"),
        )
    )
    strategy = manifest.get("strategy") if isinstance(manifest.get("strategy"), dict) else {}
    selected_strategy = str(strategy.get("selected") or "not selected")
    planned_personas = manifest.get("personas") if isinstance(manifest.get("personas"), list) else []
    charters = manifest.get("explorationCharters") if isinstance(manifest.get("explorationCharters"), list) else []
    persona_plan_html = "".join(
        f'<article class="perspective-plan">{report_icon("product")}<strong>{html.escape(str(persona.get("name") or persona.get("id") or "Persona"))}</strong><span>{html.escape(str(persona.get("role") or "product user"))}</span><p>{html.escape(str(persona.get("goal") or ""))}</p></article>'
        for persona in planned_personas
        if isinstance(persona, dict)
    ) or '<p class="muted">No generated user perspectives were planned for this run.</p>'
    charter_plan_html = "".join(
        f'<article class="charter-plan charter-{html.escape(str(charter.get("strategy") or "breadth"))}">{report_icon("position")}<span>{html.escape(str(charter.get("strategy") or "explore"))}</span><strong>{html.escape(str(charter.get("title") or charter.get("id") or "Exploration charter"))}</strong><p>{html.escape(str(charter.get("mission") or ""))}</p></article>'
        for charter in charters
        if isinstance(charter, dict)
    ) or '<p class="muted">No exploratory charters were planned for this run.</p>'
    strategy_html = f"""
      <div class="strategy-board">
        <article class="strategy-summary"><p class="section-kicker">{report_icon("coverage")} Coverage strategy</p><strong>{html.escape(selected_strategy)}</strong><p>{html.escape(str(strategy.get("rationale") or "No strategy rationale recorded."))}</p><div class="strategy-track"><span class="strategy-node breadth">Breadth</span><i></i><span class="strategy-node depth">Depth</span></div></article>
        <div class="perspective-plans">{persona_plan_html}</div>
      </div>
      <div class="charter-plans">{charter_plan_html}</div>
    """
    if not pages:
        return f"""
        <section class="evidence-section">
          <div class="section-heading"><div><p class="eyebrow">Evidence observatory</p>{report_heading("Every tested page, every available channel", "evidence")}</div><span class="status-pill status-unknown">awaiting capture</span></div>
          <div class="metrics evidence-metrics">{evidence_metrics}</div>
          <article class="panel evidence-empty">{report_heading("No page evidence bundles yet", "evidence", level=3)}<p>Executed browser pages must register screenshots, DOM/accessibility state, console and page errors, network traffic, performance, data, storage-safe metadata, traces, and video when the host can safely provide them. Unsupported or sensitive channels stay visible as gaps.</p></article>
        </section>
        """
    cards: list[str] = []
    for page in pages:
        artifacts = page.get("artifacts") if isinstance(page.get("artifacts"), list) else []
        issues = page.get("issues") if isinstance(page.get("issues"), list) else []
        screenshots = [artifact for artifact in artifacts if artifact.get("kind") == "screenshot" and safe_evidence_href(artifact.get("path"))]
        gallery = "".join(
            f'<a class="shot" href="{html.escape(str(artifact["path"]))}"><img src="{html.escape(str(artifact["path"]))}" alt="Screenshot evidence for {html.escape(str(page.get("title") or page.get("url") or page.get("id")))}"><span>{html.escape(str(artifact.get("label") or "Screenshot"))}</span></a>'
            for artifact in screenshots
        ) or '<div class="shot-empty">No safe screenshot was available for this page state.</div>'
        issue_rows = "".join(
            f'<li class="signal signal-{html.escape(str(item.get("severity", "low")))}"><span>{html.escape(str(item.get("severity", "signal")))}</span><div><strong>{html.escape(str(item.get("title", "Artifact signal")))}</strong><p>{html.escape(str(item.get("detail", "")))}</p><small>{html.escape(str(item.get("category", "general")))} · {html.escape(str(item.get("confidence", "signal")))}</small></div></li>'
            for item in issues
        ) or '<li class="signal signal-clear"><span>clear</span><div><strong>No general issue signals found in the collected artifacts</strong><p>This is not proof that the page is defect-free; review any missing channels and untested behavior.</p></div></li>'
        artifact_rows = "".join(
            f'<a class="artifact-chip" href="{html.escape(href)}"><strong>{html.escape(str(artifact.get("kind", "artifact")))}</strong><span>{html.escape(str(artifact.get("label", "evidence")))}</span><small>{int(artifact.get("bytes", 0) or 0):,} bytes{f" · {int(artifact.get("redactions", 0))} redacted" if artifact.get("redactions") else ""}{" · truncated" if artifact.get("truncated") else ""}</small></a>'
            for artifact in artifacts
            if (href := safe_evidence_href(artifact.get("path")))
        ) or '<span class="muted">Metadata only</span>'
        coverage = page.get("channelCoverage") if isinstance(page.get("channelCoverage"), dict) else {}
        coverage_html = "".join(
            f'<span class="coverage-chip {"present" if coverage.get(channel) else "missing"}">{html.escape(channel)}</span>'
            for channel in EVIDENCE_CHANNELS
        )
        gaps = page.get("evidenceGaps") if isinstance(page.get("evidenceGaps"), list) else page.get("unavailable", [])
        visual_reviews = page.get("visualReviews") if isinstance(page.get("visualReviews"), list) else []
        visual_review_html = render_list([str(value) for value in visual_reviews])
        persona_feedback = page.get("personaFeedback") if isinstance(page.get("personaFeedback"), list) else []
        persona_html = "".join(
            f'''<article class="persona-card persona-{html.escape(str(feedback.get("severity") or "none"))}"><div class="persona-head"><div class="icon-heading">{report_icon("product")}<div><span>{html.escape(str(feedback.get("role") or "product user"))}</span><strong>{html.escape(str(feedback.get("name") or feedback.get("personaId") or "Persona"))}</strong></div></div><small>{html.escape(str(feedback.get("confidence") or "opinion"))}</small></div><p class="persona-goal">Goal: {html.escape(str(feedback.get("goal") or "not recorded"))}</p><div class="rating-grid">{''.join(f'<div><span>{html.escape(str(name))}</span><progress value="{int(score)}" max="5">{int(score)}/5</progress><strong>{int(score)}/5</strong></div>' for name, score in (feedback.get("ratings") or {}).items())}</div><div class="persona-columns"><section><h4>Worked</h4>{render_list([str(value) for value in feedback.get("positives", [])])}</section><section><h4>Friction</h4>{render_list([str(value) for value in feedback.get("frictions", [])])}</section><section><h4>Recommendation</h4>{render_list([str(value) for value in feedback.get("recommendations", [])])}</section></div></article>'''
            for feedback in persona_feedback
            if isinstance(feedback, dict)
        ) or '<div class="perspective-empty">No generated-persona feedback was recorded for this page state.</div>'
        exploration = page.get("explorationObservations") if isinstance(page.get("explorationObservations"), list) else []
        exploration_html = "".join(
            f'''<article class="explore-card explore-{html.escape(str(observation.get("strategy") or "breadth"))}"><div class="icon-heading">{report_icon("position")}<div><span>{html.escape(str(observation.get("strategy") or "explore"))}</span><strong>{html.escape(str(observation.get("title") or "Exploratory observation"))}</strong><small>{html.escape(str(observation.get("charterId") or "charter"))} · {html.escape(str(observation.get("status") or "observed"))}</small></div></div><p>{html.escape(str(observation.get("observation") or ""))}</p><dl><dt>Action</dt><dd>{html.escape(str(observation.get("action") or "not recorded"))}</dd><dt>Next</dt><dd>{html.escape(str(observation.get("nextStep") or "stop or continue based on risk"))}</dd></dl></article>'''
            for observation in exploration
            if isinstance(observation, dict)
        ) or '<div class="perspective-empty">No breadth/depth exploratory observation was recorded for this page state.</div>'
        cards.append(f"""
        <article class="page-evidence">
          <div class="page-head"><div><p class="eyebrow">{html.escape(str(page.get("id", "PAGE")))} · {html.escape(str(page.get("state", "observed")))}</p><h3 class="icon-heading">{report_icon("evidence")}<span>{html.escape(str(page.get("title") or page.get("url") or "Tested page"))}</span></h3><p class="page-url">{html.escape(str(page.get("url", "URL not recorded")))}</p></div><div class="page-facts"><span>{html.escape(str(page.get("viewport", "viewport not recorded")))}</span><span>{len(artifacts)} artifacts</span><span>{len(issues)} signals</span></div></div>
          <div class="evidence-gallery">{gallery}</div>
          <div class="coverage-strip" aria-label="Evidence channel coverage">{coverage_html}</div>
          <details open><summary>Screenshot and visual review</summary>{visual_review_html}<p class="muted">Visual observations support layout and content findings; they do not prove interaction, authorization, persistence, or backend correctness.</p></details>
          <details open><summary>Generated user-perspective feedback</summary><p class="muted">Persona feedback is qualitative product evidence, not an automatic pass/fail oracle. Each perspective stays tied to this page, its screenshot, and linked tests.</p><div class="persona-grid">{persona_html}</div></details>
          <details open><summary>Breadth and depth exploratory testing</summary><p class="muted">Breadth samples surfaces, states, and transitions. Depth follows the highest-risk behavior through boundaries, interruption, recovery, and adjacent state.</p><div class="explore-grid">{exploration_html}</div></details>
          <details open><summary>General issue review across all collected artifacts</summary><ul class="signal-list">{issue_rows}</ul></details>
          <details><summary>Artifact inventory and provenance</summary><div class="artifact-grid">{artifact_rows}</div><p class="muted"><strong>Unavailable or withheld:</strong> {html.escape(", ".join(map(str, gaps)) if gaps else "none explicitly recorded")}</p></details>
        </article>
        """)
    return f"""
    <section class="evidence-section">
      <div class="section-heading"><div><p class="eyebrow">Evidence observatory</p>{report_heading("Every tested page, every available channel", "evidence")}</div><span class="status-pill status-completed">artifact review active</span></div>
      <p class="section-intro">Screenshots are shown in context. Console, network, DOM, accessibility, performance, test data, storage-safe metadata, traces, and recordings are retained when available, redacted where possible, and reviewed together for general issues on each page.</p>
      <div class="metrics evidence-metrics">{evidence_metrics}</div>
      {strategy_html}
      {''.join(cards)}
    </section>
    """


def status_counts(state: dict[str, Any]) -> dict[str, int]:
    counts = {status: 0 for status in TEST_STATUSES}
    for test in state["tests"]:
        counts[test["status"]] += 1
    return counts


def render(run_dir: Path, state: dict[str, Any] | None = None) -> Path:
    if state is None:
        state = load_state(run_dir)
    counts = status_counts(state)
    evidence_manifest = load_evidence_manifest(run_dir)
    quality_confidence = assess_quality_confidence(state, evidence_manifest)
    persona_journeys = render_persona_journeys(run_dir, state, evidence_manifest)
    total = len(state["tests"])
    done = sum(counts[status] for status in TERMINAL_TEST_STATUSES)
    percent = round(done * 100 / total) if total else 0
    upcoming = next_tests(state)
    current = (
        find_test(state, state["currentTestId"]) if state.get("currentTestId") else None
    )
    refresh = state["status"] in {"planned", "running", "paused"}
    promotion_context = list(state.get("promotionContext", [])) if isinstance(state.get("promotionContext"), list) else []
    if total >= 30 and state.get("concurrency", 1) <= 2 and state.get("status") in {"planned", "running", "paused"}:
        promotion_context.append("parallel-execution")
    if counts.get("failed", 0) >= 5:
        promotion_context.append("repeated-major-failures")
    if state.get("status") in {"planned", "paused"} and not state.get("availableAdapters"):
        promotion_context.append("local-execution-blocked")
    promotion = select_promotion(promotion_context, "run-report")
    promotional_footer = "" if not promotion else (
        f'<span class="promotion-offer"><b>{html.escape(promotion["label"])}</b>'
        f'{html.escape(promotion["text"])} <a href="{html.escape(promotion["url"])}">'
        f'{html.escape(promotion["linkLabel"])}</a></span>'
    )
    tests_html = []
    for test in sorted(
        state["tests"],
        key=lambda value: (
            priority_rank(value["priority"]),
            value["estimatedSeconds"],
            value["id"],
        ),
    ):
        flags = [name for name, enabled in test["flags"].items() if enabled]
        observations_html = ""
        if test.get("observations"):
            observations_html = f"""
              <section class="behavior-observations">
                <h4>{report_icon("observation")} Behavior observations</h4>
                <div class="observation-list">
                  {''.join(
                    f'<article class="observation observation-{html.escape(str(observation.get("status") or "observed"))}">'
                    f'<div><strong>{html.escape(str(observation.get("title") or observation.get("id") or "Observed behavior"))}</strong>'
                    f'<span class="status-pill status-{html.escape(str(observation.get("status") or "observed"))}">{html.escape(str(observation.get("status") or "observed"))}</span></div>'
                    f'<p>{html.escape(str(observation.get("actual") or ""))}</p>'
                    f'<p class="muted">Dimensions: {html.escape(", ".join(observation.get("dimensions", [])) or "not classified")} · Evidence: {html.escape("; ".join(observation.get("evidence", [])) or "not attached")}</p>'
                    f'</article>'
                    for observation in test.get("observations", []) if isinstance(observation, dict)
                  )}
                </div>
              </section>
            """
        predicted = str(test.get("predictedOutcome") or "unknown")
        if predicted == "fail":
            if test["status"] == "failed":
                prediction_interpretation = "Suspected defect confirmed: observed behavior contradicted the grounded oracle."
            elif test["status"] == "passed":
                prediction_interpretation = "Suspected defect was not reproduced: observed behavior met the oracle."
            else:
                prediction_interpretation = "Deliberately bug-revealing: a failure would confirm the source-backed suspicion."
        elif predicted == "pass":
            prediction_interpretation = "Pre-execution evidence predicted that the build would meet this oracle."
        else:
            prediction_interpretation = "No supported pre-execution outcome prediction was recorded."
        prediction_html = f'''<section><h4>Pre-execution prediction</h4><p><strong>{html.escape(predicted)}</strong> · {html.escape(prediction_interpretation)}</p><p class="muted">{html.escape(str(test.get("predictionBasis") or "No prediction basis recorded."))}</p></section>'''
        tests_html.append(
            f"""
            <article class="test test-{html.escape(test["status"])}" data-test-card data-status="{html.escape(test['status'])}" data-priority="{html.escape(test['priority'])}" data-search="{html.escape(' '.join(str(test.get(key) or '') for key in ('id', 'title', 'reason', 'type', 'adapter')).lower())}">
              <div class="test-head">
                <div class="test-title">{report_icon("tests")}<span class="priority">{html.escape(test["priority"])}</span>
                <strong>{html.escape(test["id"])}</strong> · {html.escape(test["title"])}</div>
                <span class="status-pill status-{html.escape(test["status"])}">{html.escape(test["status"])}</span>
              </div>
              <p>{html.escape(test["reason"])}</p>
              <div class="meta">
                <span>{html.escape(test["adapter"])}</span>
                <span>{test["estimatedSeconds"]} sec</span>
                <span>{html.escape(test["type"])}</span>
                <span>{html.escape(", ".join(flags) if flags else "safe/default")}</span>
              </div>
              {observations_html}
              <details>
                <summary>Plan, oracle, provenance, and result</summary>
                <div class="details-grid">
                  <section><h4>Provenance</h4>{render_list(test["provenance"])}</section>
                  <section><h4>Prerequisites</h4>{render_list(test["prerequisites"])}</section>
                  <section><h4>Data</h4>{render_list(test["data"])}</section>
                  <section><h4>Steps</h4>{render_list(test["steps"])}</section>
                  <section><h4>Normative oracle</h4><p>{html.escape(test["oracle"])}</p><p class="muted">Basis: {html.escape(str(test.get("oracleBasis") or "Not recorded"))}</p></section>
                  {prediction_html}
                  <section><h4>Dependencies</h4>{render_list(test["dependencies"])}</section>
                  <section><h4>Actual</h4><p>{html.escape(test["actual"]) or '<span class="muted">Not run</span>'}</p></section>
                  <section><h4>Evidence</h4>{render_list(test["evidence"])}</section>
                  <section><h4>Error or blocker</h4><p>{html.escape(test["error"]) or '<span class="muted">None</span>'}</p></section>
                </div>
              </details>
            </article>
            """
        )
    findings_html = (
        "".join(
            f"""<article class="finding finding-{html.escape(item["severity"])}" data-finding-card data-severity="{html.escape(item['severity'])}" data-search="{html.escape(' '.join(str(item.get(key) or '') for key in ('id', 'title', 'detail', 'severity')).lower())}"><div class="finding-title">{report_icon("findings")}<strong>{html.escape(item["severity"].upper())} · {html.escape(item["id"])} · {html.escape(item["title"])}</strong></div>
            <p>{html.escape(item["detail"])}</p><p class="muted">{html.escape(item["evidence"])}</p>
            <p><strong>Portable repro:</strong> <code>{html.escape(item.get("reproPath", "not recorded"))}</code></p></article>"""
            for item in state["findings"]
        )
        or '<p class="muted">No findings recorded yet.</p>'
    )
    status_icon = {"planned": "tests", "running": "cycle", "passed": "confidence", "failed": "findings", "blocked": "risk", "skipped": "change"}
    status_cards = "".join(
        f'<div class="metric metric-{html.escape(status)}"><div class="metric-top"><span>{html.escape(status)}</span>{report_icon(status_icon.get(status, "tests"))}</div><strong>{count}</strong></div>'
        for status, count in counts.items()
        if count or status in {"planned", "running", "passed", "failed", "blocked"}
    )
    confidence_band = quality_confidence["confidenceInQuality"]["band"]
    remaining_risk = quality_confidence["remainingRisk"]["level"]
    coverage_summary = quality_confidence["coverage"]
    dimension_cards = "".join(
        f'''<article class="dimension dimension-{html.escape(str(item['band']))}">
          <div class="dimension-head">{report_icon(identifier)}<div><h3>{html.escape(str(item['label']))}</h3><span>{html.escape(str(item['band']))}</span></div><strong>{html.escape(str(item['score']) if item['score'] is not None else "—")}</strong></div>
          <p>{html.escape(str(item['basis']))}</p>
          <div class="dimension-facts"><span>{item['conclusive']} conclusive</span><span>{item['evidenceBacked']} evidenced</span><span>{item['failed']} failed</span></div>
          <details><summary>Evidence and gaps</summary><p>{item['tests']} mapped test(s) · {item['severeFindings']} severe finding(s)</p>{render_list(item['gaps'])}</details>
        </article>'''
        for identifier, item in quality_confidence.get("dimensions", {}).items()
    )
    dimensions = quality_confidence.get("dimensions", {})
    severe_findings = sum(1 for item in state["findings"] if str(item.get("severity") or "").lower() in {"critical", "high"})
    if counts.get("failed", 0) or severe_findings:
        ship_label, ship_tone = "HOLD", "risk"
        ship_headline = "Resolve the strongest adverse evidence before shipping."
    elif confidence_band == "high" and remaining_risk == "low":
        ship_label, ship_tone = "SHIP", "neutral"
        ship_headline = "The observed evidence supports this scoped release."
    elif confidence_band == "moderate" and remaining_risk not in {"high", "critical"}:
        ship_label, ship_tone = "CANARY", "neutral"
        ship_headline = "Release narrowly while gathering the missing evidence."
    else:
        ship_label, ship_tone = "MORE EVIDENCE", "risk" if remaining_risk in {"high", "critical"} else "neutral"
        ship_headline = "The current run does not support a broad ship decision yet."
    decision_summary = quality_confidence["interpretation"]
    next_evidence = quality_confidence["whatWouldChangeConfidence"][0] if quality_confidence["whatWouldChangeConfidence"] else "No decision-changing evidence item recorded"
    narrative_html = render_evidence_spine([
        {"value": state["title"]},
        {"value": f"{remaining_risk} remaining risk", "tone": "risk" if remaining_risk in {"high", "critical"} else "neutral"},
        {"value": f"{coverage_summary['conclusive']} of {coverage_summary['total']} tests conclusive"},
        {"value": ship_label, "tone": "risk" if ship_tone == "risk" else "neutral"},
        {"value": next_evidence},
    ])
    coverage_heatmap_html = render_coverage_heatmap([
        {
            "label": item.get("label") or identifier,
            "value": item.get("band") or "—",
            "detail": f"{item.get('conclusive', 0)} conclusive · {item.get('failed', 0)} failed",
            "state": "risk" if item.get("failed") or item.get("band") in {"low", "not-assessed"} else "good" if item.get("band") in {"high", "moderate"} else "unknown",
        }
        for identifier, item in dimensions.items()
    ], "Coverage at a glance", "A dash or striped cell means the dimension has not been conclusively assessed.")
    risk_matrix_html = render_risk_matrix([
        {
            "label": item.get("label") or identifier,
            "risk": min(100, 20 + 25 * int(item.get("failed", 0)) + 20 * int(item.get("severeFindings", 0)) + (20 if item.get("gaps") else 0)),
            "evidence": min(100, (100 * int(item.get("evidenceBacked", 0)) / max(1, int(item.get("tests", 0))))),
        }
        for identifier, item in dimensions.items()
    ], note="Position is derived from mapped test evidence and recorded gaps; it is not a statistical probability.")
    positive_drivers = [
        {"label": item.get("label") or identifier, "detail": f"{item.get('conclusive', 0)} conclusive and {item.get('evidenceBacked', 0)} evidence-backed result(s)."}
        for identifier, item in dimensions.items() if item.get("conclusive") and not item.get("failed")
    ]
    negative_drivers = [
        {"label": f"Remaining risk: {reason}", "detail": "Recorded by the evidence-qualified confidence assessment."}
        for reason in quality_confidence["remainingRisk"]["reasons"]
    ] + [
        {"label": f"Coverage gap: {gap}", "detail": "Closing this gap may change the release decision."}
        for gap in quality_confidence["coverageMissing"]
    ]
    confidence_drivers_html = render_confidence_drivers(positive_drivers[:8], negative_drivers[:8])
    next_text = (
        ", ".join(f"{test['id']} {test['title']}" for test in upcoming[:3])
        or "No runnable planned tests"
    )
    current_text = (
        f"{current['id']} {current['title']}"
        if current
        else "No test currently marked running"
    )
    estimated_tests = state.get("estimatedTests", len(state["tests"]))
    test_count_label = (
        f"{estimated_tests} {'test' if estimated_tests == 1 else 'tests'}"
    )
    browser_contract = state.get("browserContract") if isinstance(state.get("browserContract"), dict) else None
    browser_contract_card = ""
    if browser_contract:
        browser_contract_card = f'''<div class="context-item"><h4>CARBON Browser contract</h4><p><strong>{html.escape(str(browser_contract.get("protocol")))}</strong> · negotiation required</p><p class="muted">{html.escape(str(browser_contract.get("degradationPolicy") or "Missing capabilities stay visible."))}</p></div>'''
    refresh_script = """
    <script>
      const key = "carbon-scroll-" + document.body.dataset.run;
      const prior = sessionStorage.getItem(key);
      if (prior !== null) scrollTo(0, Number(prior));
      document.querySelectorAll("details").forEach((details, index) => {
        const detailsKey = key + "-details-" + index;
        details.open = sessionStorage.getItem(detailsKey) === "open";
        details.addEventListener("toggle", () => {
          sessionStorage.setItem(detailsKey, details.open ? "open" : "closed");
        });
      });
      const search = document.getElementById("report-search");
      const status = document.getElementById("status-filter");
      const priority = document.getElementById("priority-filter");
      const resultCount = document.getElementById("result-count");
      const applyReportFilters = () => {
        const query = (search?.value || "").trim().toLowerCase();
        let visible = 0;
        document.querySelectorAll("[data-test-card]").forEach(card => {
          const show = (!query || card.dataset.search.includes(query)) &&
            (!status?.value || card.dataset.status === status.value) &&
            (!priority?.value || card.dataset.priority === priority.value);
          card.hidden = !show;
          if (show) visible += 1;
        });
        document.querySelectorAll("[data-finding-card]").forEach(card => {
          card.hidden = Boolean(query && !card.dataset.search.includes(query));
        });
        if (resultCount) resultCount.textContent = `${visible} test${visible === 1 ? "" : "s"} shown`;
      };
      [search, status, priority].forEach(control => control?.addEventListener("input", applyReportFilters));
      document.getElementById("expand-all")?.addEventListener("click", () => document.querySelectorAll("details").forEach(node => node.open = true));
      document.getElementById("collapse-all")?.addEventListener("click", () => document.querySelectorAll("details").forEach(node => node.open = false));
      applyReportFilters();
      if (__AUTO_REFRESH__) {
        let refreshTimer;
        const scheduleRefresh = () => {
          clearTimeout(refreshTimer);
          refreshTimer = setTimeout(() => {
            if (document.visibilityState !== "visible") return scheduleRefresh();
            sessionStorage.setItem(key, String(scrollY));
            location.reload();
          }, 30000);
        };
        addEventListener("pointerdown", scheduleRefresh);
        addEventListener("keydown", scheduleRefresh);
        scheduleRefresh();
      }
    </script>
    """.replace("__AUTO_REFRESH__", str(refresh).lower())
    document = f"""<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
  <link rel="icon" href="{REPORT_FAVICON}">
  <title>{html.escape(state["title"])} · CARBON by testers.ai</title>
  <style>
    {REPORT_THEME_CSS}
    {REPORT_VISUAL_CSS}
    .test-head {{ justify-content: space-between; gap: 14px; }}
    .test-head > div {{ min-width: 0; }}
    .test-title, .finding-title {{ display: flex; align-items: center; gap: 9px; }}
    .test-title .section-icon {{ color: var(--muted); }}
    .priority {{ margin-right: 7px; border-color: var(--accent-line); background: var(--accent-soft); color: var(--accent-bright); }}
    .meta {{ flex-wrap: wrap; gap: 7px; margin-top: 14px; }}
    .meta span {{ min-height: 25px; color: var(--muted); font-size: 10px; }}
    .test {{ position: relative; overflow: hidden; }}
    .test::before {{ position: absolute; inset: 0 auto 0 0; width: 3px; background: var(--line-bright); content: ""; }}
    .test-passed::before {{ background: var(--good); }}
    .test-failed::before {{ background: var(--bad); }}
    .test-blocked::before {{ background: var(--warn); }}
    .test-running::before {{ background: var(--info); }}
    .behavior-observations {{ margin: 18px 0 6px; border-top: 1px solid var(--line); padding-top: 14px; }}
    .behavior-observations h4 {{ display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }}
    .behavior-observations .section-icon {{ width: 18px; height: 18px; color: var(--muted); }}
    .observation-list {{ display: grid; gap: 8px; }}
    .observation {{ border-left: 3px solid var(--line-bright); padding: 10px 12px; background: rgba(255,255,255,.025); }}
    .observation-passed {{ border-left-color: var(--good); }}
    .observation-failed {{ border-left-color: var(--bad); }}
    .observation-blocked, .observation-skipped {{ border-left-color: var(--warn); }}
    .observation > div {{ display: flex; align-items: center; justify-content: space-between; gap: 12px; }}
    .observation p {{ margin: 7px 0 0; }}
    .details-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; }}
    .details-grid section {{ border: 1px solid var(--line); border-radius: 12px; padding: 13px; background: var(--panel-soft); }}
    .details-grid h4 {{ margin: 0 0 7px; color: var(--muted-strong); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; }}
    .finding {{ border-left: 3px solid var(--info); }}
    .finding-title .section-icon {{ color: var(--risk); }}
    .finding-high, .finding-critical {{ border-left-color: var(--bad); }}
    .finding-medium {{ border-left-color: var(--warn); }}
    .finding-low {{ border-left-color: var(--info); }}
    .confidence-board {{ display: grid; grid-template-columns: minmax(0,1.3fr) minmax(300px,.7fr); grid-template-areas:"confidence risk" "ledger ledger"; gap: 12px; margin: 18px 0 34px; }}
    .confidence-hero {{ grid-area:confidence; min-height:270px; border-color:var(--accent-line); background:linear-gradient(145deg,rgba(255,255,255,.07),transparent 52%),var(--panel); }}
    .risk-panel {{ grid-area:risk; min-height:270px; border-color:var(--risk-line); background:linear-gradient(145deg,var(--risk-soft),transparent 60%),var(--panel); }}
    .confidence-ledger {{ grid-area:ledger; display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); border-top:1px solid var(--line); border-bottom:1px solid var(--line); }}
    .confidence-ledger article {{ min-width:0; padding:21px 24px; border-right:1px solid var(--line); }}
    .confidence-ledger article:last-child {{ border-right:0; }}
    .confidence-ledger h3 {{ margin:0 0 10px; font-size:14px; }}
    .confidence-band {{ margin: 4px 0 8px; font-size: clamp(34px,6vw,58px); line-height: 1; letter-spacing: -.05em; text-transform: capitalize; }}
    .confidence-note {{ color: var(--muted); font-size: 12px; }}
    .dimension-grid {{ display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr)); gap:10px; margin:-18px 0 34px; }}
    .dimension {{ position:relative; overflow:hidden; border:1px solid var(--line); border-radius:16px; padding:16px; background:var(--panel-soft); }}
    .dimension::before {{ position:absolute; inset:0 0 auto; height:3px; background:var(--line-bright); content:""; }}
    .dimension-high::before {{ background:var(--good); }} .dimension-moderate::before {{ background:#d6d6d6; }} .dimension-guarded::before {{ background:var(--warn); }} .dimension-low::before {{ background:var(--bad); }}
    .dimension-head {{ display:grid; grid-template-columns:auto 1fr auto; align-items:start; gap:10px; }}
    .dimension-head .section-icon {{ color:var(--muted-strong); }} .dimension-head h3 {{ margin:0; font-size:14px; }}
    .dimension-head span {{ color:var(--muted); font-size:10px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; }}
    .dimension-head > strong {{ font-size:26px; line-height:1; }} .dimension > p {{ color:var(--muted-strong); font-size:12px; min-height:56px; }}
    .dimension-facts {{ display:flex; flex-wrap:wrap; gap:5px; }} .dimension-facts span {{ border:1px solid var(--line); border-radius:999px; padding:3px 7px; color:var(--muted); font-size:9px; text-transform:uppercase; }}
    .report-toolbar {{ position:sticky; top:10px; z-index:8; display:grid; grid-template-columns:minmax(180px,1fr) auto auto auto; gap:8px; align-items:center; margin:12px 0 18px; border:1px solid var(--line-bright); border-radius:16px; padding:10px; background:rgba(7,7,7,.94); box-shadow:var(--shadow); backdrop-filter:blur(14px); }}
    .report-toolbar input, .report-toolbar select, .report-toolbar button {{ min-height:38px; border:1px solid var(--line); border-radius:10px; padding:8px 10px; background:#000; color:var(--text); font:inherit; font-size:12px; }}
    .report-toolbar input:focus, .report-toolbar select:focus, .report-toolbar button:focus {{ outline:2px solid #fff; outline-offset:2px; }}
    .report-toolbar button {{ cursor:pointer; }} .report-toolbar .result-count {{ color:var(--muted); font-size:11px; white-space:nowrap; }}
    [hidden] {{ display:none !important; }}
    .section-heading, .page-head {{ display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; }}
    .section-heading h2 {{ margin: 0; }}
    .section-intro {{ max-width: 900px; color: var(--muted-strong); }}
    .evidence-metrics {{ margin-bottom: 18px; }}
    .page-evidence {{ margin: 16px 0; border: 1px solid var(--line); border-radius: 4px; padding: 20px; background: linear-gradient(145deg, rgba(255,255,255,.05), transparent 32%), var(--panel); box-shadow: var(--shadow); }}
    .page-head h3 {{ margin: 0; font-size: clamp(20px, 3vw, 28px); }}
    .page-url {{ margin: 5px 0 0; color: var(--muted); overflow-wrap: anywhere; }}
    .page-facts {{ display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 7px; }}
    .page-facts span, .coverage-chip {{ border: 1px solid var(--line); border-radius: 999px; padding: 5px 9px; color: var(--muted-strong); font-size: 10px; font-weight: 750; letter-spacing: .04em; text-transform: uppercase; }}
    .evidence-gallery {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 12px; margin: 18px 0 14px; }}
    .shot {{ display: block; overflow: hidden; border: 1px solid var(--line-bright); border-radius: 16px; background: #050609; color: var(--muted-strong); text-decoration: none; }}
    .shot img {{ display: block; width: 100%; max-height: 520px; object-fit: contain; background: #050609; }}
    .shot span {{ display: block; border-top: 1px solid var(--line); padding: 8px 11px; font-size: 11px; }}
    .shot-empty {{ border: 1px dashed var(--line-bright); border-radius: 16px; padding: 30px; color: var(--muted); text-align: center; }}
    .coverage-strip {{ display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; }}
    .coverage-chip.present {{ border-color: rgba(55,214,189,.35); background: var(--good-soft); color: #70ead5; }}
    .coverage-chip.missing {{ border-style: dashed; color: #707687; }}
    .signal-list {{ display: grid; gap: 8px; margin: 12px 0 0; padding: 0; list-style: none; }}
    .signal {{ display: grid; grid-template-columns: 68px 1fr; gap: 12px; border: 1px solid var(--line); border-radius: 13px; padding: 12px; background: var(--panel-soft); }}
    .signal > span {{ align-self: start; border-radius: 2px; padding: 3px 7px; background: var(--info-soft); color: #ffffff; font-size: 10px; font-weight: 800; text-align: center; text-transform: uppercase; }}
    .signal-high > span, .signal-critical > span {{ background: var(--bad-soft); color: #fda4af; }}
    .signal-medium > span {{ background: var(--warn-soft); color: #fcd34d; }}
    .signal-clear > span {{ background: var(--good-soft); color: #70ead5; }}
    .signal strong, .signal p, .signal small {{ display: block; }}
    .signal p {{ margin: 3px 0; color: var(--muted-strong); }}
    .signal small {{ color: var(--muted); }}
    .artifact-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 8px; margin: 12px 0; }}
    .artifact-chip {{ display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 12px; padding: 11px; background: var(--panel-soft); color: var(--text); text-decoration: none; }}
    .artifact-chip > strong {{ color: var(--accent-bright); font-size: 10px; letter-spacing: .07em; text-transform: uppercase; }}
    .artifact-chip > span {{ margin-top: 3px; overflow-wrap: anywhere; }}
    .artifact-chip > small {{ margin-top: 5px; color: var(--muted); }}
    .strategy-board {{ display: grid; grid-template-columns: minmax(250px,.75fr) minmax(0,1.25fr); gap: 12px; margin: 18px 0 12px; }}
    .strategy-summary, .perspective-plan, .charter-plan, .persona-card, .explore-card {{ border: 1px solid var(--line); border-radius: 16px; background: var(--panel-soft); }}
    .strategy-summary {{ padding: 20px; background: linear-gradient(145deg,rgba(255,255,255,.07),transparent 55%),var(--panel-soft); }}
    .strategy-summary > strong {{ display: block; font-size: clamp(30px,5vw,50px); letter-spacing: -.05em; line-height: 1; text-transform: capitalize; }}
    .strategy-track {{ display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 8px; margin-top: 22px; }}
    .strategy-track i {{ height: 2px; background: linear-gradient(90deg,var(--accent),var(--info)); }}
    .strategy-node {{ border: 1px solid var(--line-bright); border-radius: 999px; padding: 5px 9px; font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; }}
    .strategy-node.breadth {{ color: var(--accent-bright); }} .strategy-node.depth {{ color: #bdbdbd; }}
    .perspective-plans {{ display: grid; grid-template-columns: repeat(auto-fit,minmax(180px,1fr)); gap: 8px; }}
    .perspective-plan {{ padding: 13px; }} .perspective-plan strong, .perspective-plan span {{ display: block; }}
    .perspective-plan span {{ color: var(--accent-bright); font-size: 10px; font-weight: 800; text-transform: uppercase; }}
    .perspective-plan p {{ margin: 6px 0 0; color: var(--muted); font-size: 12px; }}
    .charter-plans, .persona-grid, .explore-grid {{ display: grid; grid-template-columns: repeat(auto-fit,minmax(min(100%,280px),1fr)); gap: 10px; margin: 12px 0; }}
    .charter-plan {{ display: grid; gap: 5px; padding: 15px; border-left: 3px solid var(--accent); }} .charter-depth {{ border-left-color: var(--info); }}
    .charter-plan > span, .explore-card > div > span {{ color: var(--accent-bright); font-size: 10px; font-weight: 850; letter-spacing: .08em; text-transform: uppercase; }}
    .charter-depth > span, .explore-depth > div > span {{ color: #bdbdbd; }} .charter-plan p {{ margin: 0; color: var(--muted); }}
    .persona-card {{ padding: 16px; overflow: hidden; }} .persona-medium, .persona-high, .persona-critical {{ border-color: rgba(251,191,36,.38); }}
    .persona-head {{ display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }} .persona-head strong, .persona-head span {{ display: block; }}
    .persona-head span {{ color: var(--accent-bright); font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; }} .persona-head strong {{ font-size: 20px; }}
    .persona-head small {{ border: 1px solid var(--line); border-radius: 999px; padding: 4px 7px; color: var(--muted); text-transform: uppercase; }}
    .persona-goal {{ color: var(--muted-strong); }} .rating-grid {{ display: grid; gap: 6px; margin: 12px 0; }}
    .rating-grid > div {{ display: grid; grid-template-columns: minmax(72px,.6fr) 1fr auto; align-items: center; gap: 8px; }} .rating-grid span {{ color: var(--muted); font-size: 10px; text-transform: uppercase; }}
    .rating-grid progress {{ width: 100%; height: 7px; accent-color: var(--good); }} .rating-grid strong {{ font-size: 11px; }}
    .persona-columns {{ display: grid; grid-template-columns: repeat(3,1fr); gap: 7px; }} .persona-columns section {{ border-top: 1px solid var(--line); padding-top: 8px; }} .persona-columns h4 {{ margin: 0; color: var(--muted); font-size: 10px; text-transform: uppercase; }} .persona-columns ul {{ margin: 6px 0 0; padding-left: 16px; font-size: 12px; }}
    .explore-card {{ display: grid; gap: 10px; padding: 16px; border-left: 3px solid var(--accent); }} .explore-depth {{ border-left-color: var(--info); }}
    .explore-card > div > strong, .explore-card > div > small {{ display: block; }} .explore-card > div > small {{ margin-top: 4px; color: var(--muted); }} .explore-card > p {{ margin: 0; color: var(--muted-strong); }}
    .explore-card dl {{ display: grid; grid-template-columns: auto 1fr; gap: 4px 8px; margin: 0; font-size: 12px; }} .explore-card dt {{ color: var(--muted); text-transform: uppercase; }} .explore-card dd {{ margin: 0; }}
    .perspective-empty {{ border: 1px dashed var(--line-bright); border-radius: 14px; padding: 24px; color: var(--muted); }}
    @media (max-width: 680px) {{
      .test-head {{ align-items: flex-start; }}
      .test-head .status-pill {{ flex: 0 0 auto; }}
      .details-grid {{ grid-template-columns: 1fr; }}
      .section-heading, .page-head {{ flex-direction: column; }}
      .page-facts {{ justify-content: flex-start; }}
      .page-evidence {{ border-radius: 18px; padding: 15px; }}
      .signal {{ grid-template-columns: 1fr; }}
      .signal > span {{ justify-self: start; }}
      .strategy-board {{ grid-template-columns: 1fr; }}
      .persona-columns {{ grid-template-columns: 1fr; }}
      .rating-grid > div {{ grid-template-columns: 70px 1fr auto; }}
      .confidence-board {{ grid-template-columns: 1fr; }}
      .confidence-board {{ grid-template-areas:"confidence" "risk" "ledger"; }}
      .confidence-ledger {{ grid-template-columns:1fr; }}
      .confidence-ledger article {{ border-right:0; border-bottom:1px solid var(--line); }}
      .confidence-ledger article:last-child {{ border-bottom:0; }}
      .report-toolbar {{ position:static; grid-template-columns:1fr 1fr; }}
      .report-toolbar input {{ grid-column:1/-1; }}
    }}
  </style>
</head>
<body data-run="{html.escape(state["runId"])}">
<main>
  <header class="masthead">
    {carbon_lockup("Run report")}
    <div class="masthead-meta">
      <span class="status-pill status-{html.escape(state["status"])}">{html.escape(state["status"])}</span>
      <span class="updated">Updated {html.escape(state["updatedAt"])}</span>
    </div>
  </header>

  {render_decision_hero(ship_label, ship_headline, decision_summary, quality_confidence['confidenceInQuality']['basis'], ship_tone)}
  {narrative_html}

  <section class="hero">
    <p class="eyebrow">CARBON {html.escape(state["mode"])} · run {html.escape(state["runId"])}</p>
    <h1>{html.escape(state["title"])}</h1>
    <p class="target-line">{html.escape(state["target"]) or "Target not recorded"}</p>
    <div class="progress-shell">
      <progress class="progress" value="{done}" max="{max(total, 1)}" aria-label="CARBON test progress: {done} of {total} terminal">{done}/{total}</progress>
    </div>
    <div class="progress-copy">
      <strong>{percent}% terminal · {done} of {total}</strong>
      <span>Estimate {state["estimatedMinutes"]} minutes · {html.escape(state["lastEvent"])}</span>
    </div>
  </section>

  <div class="metrics">{status_cards}</div>

  <section class="panel" aria-labelledby="persona-journeys-heading">
    <div class="section-heading">
      <div><p class="eyebrow">Intent-led product use</p>{report_heading("Persona journeys and exploratory testing", "product", heading_id="persona-journeys-heading")}</div>
      <span class="status-pill status-{html.escape('completed' if persona_journeys['completedJourneys'] == persona_journeys['plannedPersonas'] and persona_journeys['personasExplored'] == persona_journeys['plannedPersonas'] else 'running')}">{persona_journeys['completedJourneys']} / {persona_journeys['plannedPersonas']} journeys</span>
    </div>
    <p class="section-intro">Every planned persona attempts a realistic task, explores beyond the script, and records what worked, friction, confusion, delight, surprise, and the evidence behind the observation.</p>
    <p><a class="button-link" href="persona-journeys.html">Open the live persona journey report</a> <a class="button-link" href="persona-journeys.md">Open the written journey document</a></p>
  </section>

  <section aria-labelledby="quality-confidence-heading">
    <div class="section-heading"><div><p class="eyebrow">Evidence-qualified result</p>{report_heading("Confidence in quality", "confidence", heading_id="quality-confidence-heading")}</div><span class="status-pill status-{html.escape('completed' if confidence_band in {'high','moderate'} else 'unknown')}">{html.escape(confidence_band)}</span></div>
    <div class="confidence-board">
      <article class="panel confidence-hero"><p class="section-kicker">{report_icon("confidence")} Decision signal</p><div class="confidence-band">{html.escape(confidence_band)}</div><p>{html.escape(quality_confidence['confidenceInQuality']['basis'])}</p><p class="confidence-note">{html.escape(quality_confidence['interpretation'])}</p></article>
      <article class="panel risk-panel"><p class="section-kicker risk-text">{report_icon("risk")} Remaining risk</p><div class="confidence-band risk-text">{html.escape(remaining_risk)}</div>{render_list(quality_confidence['remainingRisk']['reasons'])}</article>
      <div class="confidence-ledger">
        <article>{report_heading("Coverage missing", "coverage", level=3)}{render_list(quality_confidence['coverageMissing'])}<p class="confidence-note">Conclusive: {coverage_summary['conclusive']} of {coverage_summary['total']} · Evidence-backed: {coverage_summary['evidenceBacked']} · Blocked: {coverage_summary['blocked']} · Unfinished: {coverage_summary['unfinished']}</p></article>
        <article>{report_heading("What changes confidence", "change", level=3)}{render_list(quality_confidence['whatWouldChangeConfidence'])}</article>
        <article>{report_heading("Coverage achieved", "confidence", level=3)}<p><strong>{coverage_summary['conclusivePercent']}%</strong> conclusive · <strong>{coverage_summary['terminalPercent']}%</strong> terminal</p><p>{coverage_summary['passed']} passed · {coverage_summary['failed']} failed · {coverage_summary['skipped']} skipped</p></article>
      </div>
    </div>
    {report_heading("Multidimensional confidence", "coverage", level=3)}
    <p class="section-intro">Each dimension is a scoped evidence judgment, not a probability that the product is correct. A dash means the dimension has not been conclusively assessed.</p>
    <div class="dimension-grid">{dimension_cards}</div>
    {coverage_heatmap_html}
    {risk_matrix_html}
    {confidence_drivers_html}
  </section>

  <section class="panel spotlight">
    {report_heading("Current position", "position")}
    <p><strong>Current:</strong> {html.escape(current_text)}</p>
    <p><strong>Next:</strong> {html.escape(next_text)}</p>
    <p><strong>Pause reason:</strong> {html.escape(state["pauseReason"]) or '<span class="muted">Not paused</span>'}</p>
    <p><strong>Resume:</strong> <code>python3 carbon_run.py resume --run-dir {html.escape(str(run_dir))}</code></p>
  </section>

  <section>
    {report_heading("Run context", "context")}
    <div class="context-ledger">
      <article class="context-band"><div class="context-band-title">{report_icon("context")}<div><h3>Environment & scope</h3><p>Where this evidence applies.</p></div></div><div class="context-band-body"><div class="context-item"><h4>Environment</h4><p>{html.escape(state["environment"])}</p><p><strong>Revision:</strong> {html.escape(state["revision"]) or "not recorded"}</p></div><div class="context-item"><h4>Scope</h4>{render_list(state["scope"])}</div><div class="context-item"><h4>Exclusions</h4>{render_list(state["exclusions"])}</div><div class="context-item"><h4>Assumptions</h4>{render_list(state["assumptions"])}</div></div></article>
      <article class="context-band"><div class="context-band-title">{report_icon("product")}<div><h3>Product map</h3><p>What Carbon discovered.</p></div></div><div class="context-band-body"><div class="context-item"><h4>Detected flows</h4>{render_list(state.get("detectedFlows", []))}</div><div class="context-item"><h4>Detected items</h4>{render_list(state.get("detectedItems", []))}</div><div class="context-item"><h4>Execution adapters</h4>{render_list(state.get("availableAdapters", []))}</div>{browser_contract_card}</div></article>
      <article class="context-band"><div class="context-band-title">{report_icon("safety")}<div><h3>Authority & safety</h3><p>What execution may and may not do.</p></div></div><div class="context-band-body"><div class="context-item"><h4>Setup dependencies</h4>{render_list(state.get("setupDependencies", []))}</div><div class="context-item"><h4>Credential sources</h4>{render_list(state["credentialSources"])}<p class="muted">Names or ignored paths only; no secret values.</p></div><div class="context-item"><h4>Stop conditions</h4>{render_list(state.get("stopConditions", []))}</div><div class="context-item"><h4>Residual uncertainty</h4>{render_list(state.get("residualUncertainty", []))}</div></div></article>
      <article class="context-band"><div class="context-band-title">{report_icon("budget")}<div><h3>Limits & cut line</h3><p>How far this run goes.</p></div></div><div class="context-band-body"><div class="context-item"><h4>Execution budget</h4><p>{test_count_label} · concurrency {state.get("concurrency", 1)}</p><p><strong>Target root:</strong> {html.escape(state.get("targetRoot", "not recorded"))}</p><p><strong>State root:</strong> {html.escape(state.get("stateRoot", str(run_dir.parents[2]) if len(run_dir.parents) > 2 else "not recorded"))}</p></div><div class="context-item"><h4>Cut line</h4><p>{html.escape(state["cutLine"]) or '<span class="muted">No cut line recorded.</span>'}</p></div></div></article>
    </div>
  </section>

  {render_evidence_manifest(run_dir)}

  <section>
    {report_heading("Prioritized test plan and results", "tests")}
    <div class="report-toolbar" aria-label="Report controls">
      <input id="report-search" type="search" placeholder="Search tests and findings" aria-label="Search tests and findings">
      <select id="status-filter" aria-label="Filter by status"><option value="">All statuses</option>{''.join(f'<option value="{value}">{value}</option>' for value in sorted(TEST_STATUSES))}</select>
      <select id="priority-filter" aria-label="Filter by priority"><option value="">All priorities</option>{''.join(f'<option value="{value}">{value}</option>' for value in sorted(PRIORITIES))}</select>
      <span id="result-count" class="result-count"></span>
      <button id="expand-all" type="button">Expand details</button>
      <button id="collapse-all" type="button">Collapse details</button>
    </div>
    {"".join(tests_html)}
  </section>

  <section>
    {report_heading("Findings so far", "findings")}
    {findings_html}
  </section>

  <section>
    {report_heading("Choose the next cycle", "cycle")}
    <div class="cycle">
      <article class="cycle-step">{report_icon("quick")}<span class="cycle-index">01</span><h3>CARBON Quick</h3><p>Changed, failed, and adjacent scope. Usually 5–15 minutes.</p></article>
      <article class="cycle-step">{report_icon("medium")}<span class="cycle-index">02</span><h3>CARBON Medium</h3><p>Primary non-destructive flows plus representative edges. Usually 30–90 minutes.</p></article>
      <article class="cycle-step">{report_icon("large")}<span class="cycle-index">03</span><h3>CARBON Large</h3><p>Broad authorized overnight run with durable pause/resume.</p></article>
    </div>
  </section>

  <footer class="footer">
    <span>CARBON is a <a href="https://testers.ai">testers.ai</a> product.</span>
    {schema_provenance_html()}
    {promotional_footer}
  </footer>
</main>
{refresh_script}
</body>
</html>
"""
    output = run_dir / "report.html"
    with tempfile.NamedTemporaryFile(
        "w", encoding="utf-8", dir=run_dir, delete=False
    ) as handle:
        handle.write(document)
        temporary = Path(handle.name)
    os.replace(temporary, output)
    return output


def render_command(args: argparse.Namespace) -> None:
    run_dir = Path(args.run_dir).expanduser().resolve()
    print(render(run_dir))


def show_command(args: argparse.Namespace) -> None:
    state = load_state(Path(args.run_dir).expanduser().resolve())
    print(json.dumps(summary(state), indent=2))


def next_command(args: argparse.Namespace) -> None:
    state = load_state(Path(args.run_dir).expanduser().resolve())
    print(
        json.dumps(
            [
                {
                    "id": test["id"],
                    "priority": test["priority"],
                    "title": test["title"],
                    "adapter": test["adapter"],
                    "estimatedSeconds": test["estimatedSeconds"],
                }
                for test in next_tests(state)
            ],
            indent=2,
        )
    )


def latest_command(args: argparse.Namespace) -> None:
    runs_root = Path(args.root).expanduser().resolve() / ".carbon" / "runs"
    candidates = []
    if runs_root.exists():
        for path in runs_root.iterdir():
            if not path.is_dir() or not (path / "run.json").exists():
                continue
            state = load_state(path)
            if args.include_completed or state["status"] not in {
                "completed",
                "canceled",
            }:
                candidates.append((state["updatedAt"], path))
    if not candidates:
        raise ValueError("No matching CARBON runs found")
    print(max(candidates)[1])


def serve_command(args: argparse.Namespace) -> None:
    run_dir = Path(args.run_dir).expanduser().resolve()
    render(run_dir)
    handler = partial(SimpleHTTPRequestHandler, directory=str(run_dir))
    server = ThreadingHTTPServer(("127.0.0.1", args.port), handler)
    host, port = server.server_address
    print(f"http://{host}:{port}/report.html", flush=True)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.server_close()


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description=__doc__)
    subparsers = parser.add_subparsers(dest="command", required=True)

    init = subparsers.add_parser("init")
    init.add_argument("--root", required=True)
    init.add_argument(
        "--state-root",
        help="Writable root for .carbon state when the target root is read-only or external",
    )
    init.add_argument("--plan", required=True)
    init.add_argument("--mode", choices=sorted(MODES), default="quick")
    init.add_argument("--target")
    init.add_argument("--title")
    init.add_argument("--run-id")
    init.set_defaults(function=lambda args: print(initialize(args)))

    start = subparsers.add_parser("start")
    start.add_argument("--run-dir", required=True)
    start.set_defaults(function=start_run)

    update = subparsers.add_parser("update")
    update.add_argument("--run-dir", required=True)
    update.add_argument("--test-id", required=True)
    update.add_argument("--status", choices=sorted(TEST_STATUSES), required=True)
    update.add_argument("--actual")
    update.add_argument("--evidence", action="append", default=[])
    update.add_argument("--error")
    update.add_argument("--force", action="store_true")
    update.set_defaults(function=update_test)

    observe = subparsers.add_parser("observe", help="Record a passed, failed, blocked, or skipped behavior within a broader test surface.")
    observe.add_argument("--run-dir", required=True)
    observe.add_argument("--test-id", required=True)
    observe.add_argument("--status", choices=sorted(OBSERVATION_STATUSES), required=True)
    observe.add_argument("--title", required=True)
    observe.add_argument("--actual", required=True)
    observe.add_argument("--evidence", action="append", default=[])
    observe.add_argument("--dimension", action="append", choices=sorted(QUALITY_DIMENSIONS), default=[])
    observe.add_argument("--severity", choices=["none", "low", "medium", "high", "critical"], default="none")
    observe.set_defaults(function=record_observation)

    finding = subparsers.add_parser("finding")
    finding.add_argument("--run-dir", required=True)
    finding.add_argument(
        "--severity", choices=["critical", "high", "medium", "low"], required=True
    )
    finding.add_argument("--title", required=True)
    finding.add_argument("--detail", required=True)
    finding.add_argument("--evidence", default="")
    finding.add_argument("--test-id")
    finding.add_argument("--category")
    finding.add_argument("--dimension", action="append", choices=sorted(QUALITY_DIMENSIONS), default=[])
    finding.add_argument("--starting-state")
    finding.add_argument("--steps", action="append", default=[])
    finding.add_argument("--expected")
    finding.add_argument("--actual")
    finding.add_argument("--environment")
    finding.add_argument("--code-location", action="append", default=[])
    finding.add_argument("--regression-test", action="append", default=[])
    finding.set_defaults(function=add_finding)

    pause = subparsers.add_parser("pause")
    pause.add_argument("--run-dir", required=True)
    pause.add_argument("--reason", required=True)
    pause.set_defaults(function=pause_run)

    resume = subparsers.add_parser("resume")
    resume.add_argument("--run-dir", required=True)
    resume.set_defaults(function=resume_run)

    complete = subparsers.add_parser("complete")
    complete.add_argument("--run-dir", required=True)
    complete.add_argument("--cut-line")
    complete.set_defaults(function=complete_run)

    cancel = subparsers.add_parser("cancel")
    cancel.add_argument("--run-dir", required=True)
    cancel.add_argument("--reason", required=True)
    cancel.set_defaults(function=cancel_run)

    render_parser = subparsers.add_parser("render")
    render_parser.add_argument("--run-dir", required=True)
    render_parser.set_defaults(function=render_command)

    show = subparsers.add_parser("show")
    show.add_argument("--run-dir", required=True)
    show.set_defaults(function=show_command)

    next_parser = subparsers.add_parser("next")
    next_parser.add_argument("--run-dir", required=True)
    next_parser.set_defaults(function=next_command)

    latest = subparsers.add_parser("latest")
    latest.add_argument("--root", required=True)
    latest.add_argument("--include-completed", action="store_true")
    latest.set_defaults(function=latest_command)

    serve = subparsers.add_parser("serve")
    serve.add_argument("--run-dir", required=True)
    serve.add_argument("--port", type=int, default=0)
    serve.set_defaults(function=serve_command)
    return parser


def main() -> int:
    try:
        args = build_parser().parse_args()
        args.function(args)
        return 0
    except (ValueError, TimeoutError) as error:
        print(f"carbon_run: {error}", file=sys.stderr)
        return 2


if __name__ == "__main__":
    raise SystemExit(main())
