Expose world-class preflight repair rows

This commit is contained in:
yaojingang
2026-06-17 03:49:07 +08:00
parent 48a503f8f6
commit e336777575
34 changed files with 1177 additions and 227 deletions
+42 -8
View File
@@ -79,6 +79,34 @@ def render_html_source_checks(rows: list[dict[str, Any]]) -> str:
)
def render_html_repair_rows(rows: list[dict[str, Any]]) -> str:
if not rows:
return "<p class=\"muted\">No repair rows listed.</p>"
return "".join(
"""
<article class="repair-row {status}">
<div>
<span>{repair_type}</span>
<strong>{target}</strong>
</div>
<dl>
<dt>Status</dt><dd>{status}</dd>
<dt>Reason</dt><dd>{reason}</dd>
<dt>Action</dt><dd>{action}</dd>
<dt>Evidence</dt><dd>does not count as completion</dd>
</dl>
</article>
""".format(
status=html_text(row.get("status", "")),
repair_type=html_text(row.get("repair_type", "")),
target=html_text(row.get("target", "")),
reason=html_text(row.get("blocking_reason", "")),
action=html_text(row.get("next_action", "")),
)
for row in rows
)
def render_html_artifact_roles(contract: dict[str, Any]) -> str:
cards = []
for role in contract.get("roles", []):
@@ -143,6 +171,10 @@ def render_html_item(item: dict[str, Any]) -> str:
<h4>Source Checks</h4>
<div class="check-grid">{render_html_source_checks(item.get('source_checklist', []))}</div>
</section>
<section class="check-section">
<h4>Repair Rows</h4>
<div class="repair-grid compact">{render_html_repair_rows(item.get('repair_checklist', []))}</div>
</section>
<section class="runbook">
<h4>Runbook</h4>
<ul>{html_list(item.get('runbook', []), 'No runbook steps listed.')}</ul>
@@ -159,6 +191,7 @@ def render_html(report: dict[str, Any]) -> str:
("Ready", summary["collection_ready_count"]),
("Blocked", summary["collection_blocked_count"]),
("Source", f"{summary['source_pass_count']}/{summary['source_check_count']}"),
("Repairs", f"{summary.get('repair_blocked_count', 0)}/{summary.get('repair_checklist_count', 0)}"),
]
stat_html = "".join(
f"<article><span>{html_text(label)}</span><strong>{html_text(value)}</strong></article>"
@@ -189,10 +222,10 @@ def render_html(report: dict[str, Any]) -> str:
h3 {{ margin:4px 0 10px; font-size:22px; letter-spacing:0; }}
h4 {{ margin:0 0 8px; font-size:16px; letter-spacing:0; }}
.lede {{ max-width:820px; color:var(--muted); font-size:20px; }}
.stats {{ display:grid; grid-template-columns:repeat(5, minmax(0,1fr)); gap:12px; margin:26px 0 0; }}
.stats article, .panel, .evidence-card, .check-row {{ border:1px solid var(--line); border-radius:8px; background:#fff; }}
.stats {{ display:grid; grid-template-columns:repeat(6, minmax(0,1fr)); gap:12px; margin:26px 0 0; }}
.stats article, .panel, .evidence-card, .check-row, .repair-row {{ border:1px solid var(--line); border-radius:8px; background:#fff; }}
.stats article {{ padding:16px; }}
.stats span, .muted, .evidence-card header span, .check-row span {{ color:var(--muted); }}
.stats span, .muted, .evidence-card header span, .check-row span, .repair-row span {{ color:var(--muted); }}
.stats strong {{ display:block; color:var(--ink); font-size:28px; line-height:1.15; overflow-wrap:anywhere; }}
.section {{ padding:32px 0; border-bottom:1px solid var(--line); }}
.two-col {{ display:grid; grid-template-columns:minmax(0,.45fr) minmax(0,1fr); gap:18px; align-items:start; }}
@@ -209,9 +242,9 @@ def render_html(report: dict[str, Any]) -> str:
.evidence-grid {{ display:grid; gap:18px; }}
.evidence-card {{ padding:20px; min-width:0; }}
.evidence-card.blocked {{ border-left:4px solid var(--block); }}
.evidence-card.ready-for-human-review, .evidence-card.ready-to-collect, .check-row.human-required, .check-row.external-required, .check-row.missing, .check-row.blocked {{ border-left:4px solid var(--warn); }}
.evidence-card.ready-for-submission, .check-row.pass {{ border-left:4px solid var(--pass); }}
.meta, .check-row dl {{ display:grid; grid-template-columns:96px minmax(0,1fr); gap:8px 12px; }}
.evidence-card.ready-for-human-review, .evidence-card.ready-to-collect, .check-row.human-required, .check-row.external-required, .check-row.missing, .check-row.blocked, .repair-row.blocked {{ border-left:4px solid var(--warn); }}
.evidence-card.ready-for-submission, .check-row.pass, .repair-row.ready {{ border-left:4px solid var(--pass); }}
.meta, .check-row dl, .repair-row dl {{ display:grid; grid-template-columns:96px minmax(0,1fr); gap:8px 12px; }}
dt {{ color:var(--ink); }}
dd {{ margin:0; min-width:0; overflow-wrap:anywhere; }}
code {{ font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:13px; overflow-wrap:anywhere; }}
@@ -219,11 +252,12 @@ def render_html(report: dict[str, Any]) -> str:
.next-action p {{ margin-top:0; }}
.next-action code {{ display:block; margin-top:8px; }}
.check-grid {{ display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:12px; }}
.check-row {{ padding:14px; min-width:0; }}
.repair-grid {{ display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:12px; }}
.check-row, .repair-row {{ padding:14px; min-width:0; }}
.check-section {{ margin-top:16px; }}
.notice {{ background:var(--soft); border-left:4px solid var(--ink); padding:16px; border-radius:8px; }}
li {{ overflow-wrap:anywhere; }}
@media (max-width:820px) {{ .stats, .two-col, .check-grid, .role-grid {{ grid-template-columns:1fr; }} h1 {{ font-size:38px; }} .topbar-inner {{ align-items:flex-start; flex-direction:column; }} }}
@media (max-width:820px) {{ .stats, .two-col, .check-grid, .repair-grid, .role-grid {{ grid-template-columns:1fr; }} h1 {{ font-size:38px; }} .topbar-inner {{ align-items:flex-start; flex-direction:column; }} }}
</style>
</head>
<body>