fabro/.fabro/workflows/code-review/templates/report.html
2026-08-28 13:53:59 -04:00

359 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Code review results</title>
<style>
:root {
--bg: #f7f7f5;
--surface: #ffffff;
--ink: #1c1c1a;
--muted: #6b6b66;
--line: #e3e3de;
--accent: #2f5f8f;
--high: #b0362c;
--high-bg: #fbeae8;
--medium: #9a6200;
--medium-bg: #fcf2df;
--low: #3c6e46;
--low-bg: #e9f2ea;
--chip-bg: #eeeeea;
--code-bg: #f2f2ee;
--highlight-bg: #fdf3d0;
--banner-bg: #fcf2df;
--banner-line: #e6c789;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #191917;
--surface: #21211f;
--ink: #e8e8e4;
--muted: #9a9a93;
--line: #343430;
--accent: #7aa7d0;
--high: #e0766c;
--high-bg: #3a2320;
--medium: #d9a04a;
--medium-bg: #362b19;
--low: #8dbb95;
--low-bg: #21301f;
--chip-bg: #2c2c29;
--code-bg: #1d1d1b;
--highlight-bg: #3a3320;
--banner-bg: #362b19;
--banner-line: #6d5a2c;
}
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif;
}
main { max-width: 60rem; margin: 0 auto; padding: 2rem 1.25rem 4rem; }
h1 { font-size: 1.5rem; margin: 0 0 0.25rem; }
h2 { font-size: 1.1rem; margin: 2rem 0 0.75rem; }
.meta { color: var(--muted); font-size: 0.9rem; margin: 0 0 1.5rem; }
.meta span + span::before { content: " · "; color: var(--line); }
.summary { font-size: 1rem; margin: 0 0 1rem; }
.banner {
background: var(--banner-bg);
border: 1px solid var(--banner-line);
border-radius: 8px;
padding: 0.75rem 1rem;
margin: 0 0 1.5rem;
font-size: 0.9rem;
}
.banner ul { margin: 0.25rem 0 0; padding-left: 1.25rem; }
.finding {
background: var(--surface);
border: 1px solid var(--line);
border-radius: 10px;
margin: 0 0 1rem;
padding: 1rem 1.25rem;
}
.finding-head { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: baseline; }
.finding-id { font-weight: 600; color: var(--muted); font-size: 0.85rem; }
.finding-title { font-weight: 600; font-size: 1rem; flex: 1 1 auto; }
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.5rem 0 0; }
.chip {
font-size: 0.75rem;
padding: 0.1rem 0.55rem;
border-radius: 999px;
background: var(--chip-bg);
color: var(--muted);
white-space: nowrap;
}
.chip.sev-HIGH { background: var(--high-bg); color: var(--high); }
.chip.sev-MEDIUM { background: var(--medium-bg); color: var(--medium); }
.chip.sev-LOW { background: var(--low-bg); color: var(--low); }
.location {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 0.85rem;
color: var(--accent);
margin: 0.5rem 0 0;
overflow-wrap: anywhere;
}
.finding p { margin: 0.6rem 0 0; }
.label { font-weight: 600; }
details { margin: 0.6rem 0 0; }
details summary { cursor: pointer; color: var(--muted); font-size: 0.9rem; }
pre.code {
background: var(--code-bg);
border: 1px solid var(--line);
border-radius: 8px;
padding: 0.6rem 0;
margin: 0.6rem 0 0;
overflow-x: auto;
font: 0.82rem/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
pre.code .row { display: block; padding: 0 0.9rem; white-space: pre; }
pre.code .row.hl { background: var(--highlight-bg); }
pre.code .num {
display: inline-block;
min-width: 3ch;
margin-right: 1ch;
text-align: right;
color: var(--muted);
user-select: none;
}
.coverage { color: var(--muted); font-size: 0.9rem; }
.coverage ul { padding-left: 1.25rem; margin: 0.5rem 0 0; }
.empty { color: var(--muted); font-style: italic; }
</style>
</head>
<body>
<main>
<h1>Code review results</h1>
<p class="meta" id="meta"></p>
<p class="summary" id="summary"></p>
<div id="banner"></div>
<section id="findings"></section>
<h2>Coverage</h2>
<div class="coverage" id="coverage"></div>
</main>
<script>
"use strict";
const DATA = __CODE_REVIEW_PAYLOAD__;
function el(tag, className, text) {
const node = document.createElement(tag);
if (className) node.className = className;
if (text !== undefined) node.textContent = text;
return node;
}
function renderMeta() {
const meta = DATA.meta || {};
const holder = document.getElementById("meta");
const parts = [
meta.target,
meta.revision,
"effort " + meta.effort + (meta.model ? " · model " + meta.model : ""),
"verification " + meta.verification,
"completion " + meta.completion,
meta.completed_at,
];
for (const part of parts) {
if (part) holder.appendChild(el("span", null, String(part)));
}
if (meta.guidance) {
const guidance = el("p", "meta");
guidance.appendChild(el("span", "label", "Guidance: "));
guidance.appendChild(document.createTextNode(meta.guidance));
holder.insertAdjacentElement("afterend", guidance);
}
const findings = DATA.findings || [];
const bugs = findings.filter(f => f.category === "correctness").length;
document.getElementById("summary").textContent =
findings.length + " finding(s) reported (" + bugs + " correctness, " +
(findings.length - bugs) + " cleanup)." +
(meta.effort === "low"
? " Low-effort single pass: findings were not independently verified."
: "");
}
function renderBanner() {
const reasons = DATA.partialReasons || [];
if (!reasons.length) return;
const banner = el("div", "banner");
banner.appendChild(el("span", "label", "Partial review."));
const list = el("ul");
for (const reason of reasons) list.appendChild(el("li", null, reason));
banner.appendChild(list);
document.getElementById("banner").appendChild(banner);
}
function renderCode(code) {
if (!code || !Array.isArray(code.lines) || !code.lines.length) return null;
const details = el("details");
details.open = true;
details.appendChild(el("summary", null, code.label || "Excerpt"));
const pre = el("pre", "code");
for (const line of code.lines) {
const row = el("span", line.highlight ? "row hl" : "row");
row.appendChild(el("span", "num", String(line.number)));
row.appendChild(document.createTextNode(line.text));
pre.appendChild(row);
}
details.appendChild(pre);
return details;
}
function renderSuggestedChange(finding) {
if (!finding.suggestion || !finding.location ||
!finding.location.existing_code) return null;
const details = el("details");
details.appendChild(el("summary", null, "Suggested change"));
details.appendChild(el("p", "label", "Before"));
details.appendChild(el("pre", "code", finding.location.existing_code));
details.appendChild(el("p", "label", "After"));
details.appendChild(el("pre", "code",
finding.suggestion.replacement_code || ""));
return details;
}
function renderFinding(finding) {
const card = el("article", "finding");
const head = el("div", "finding-head");
head.appendChild(el("span", "finding-id", finding.id));
head.appendChild(el("span", "finding-title", finding.short_summary));
card.appendChild(head);
const chips = el("div", "chips");
chips.appendChild(el("span", "chip sev-" + finding.severity,
finding.severity + " severity"));
chips.appendChild(el("span", "chip", finding.category));
chips.appendChild(el("span", "chip", finding.issue_type));
if ((finding.rule_ids || []).length) {
chips.appendChild(el("span", "chip",
"rule " + finding.rule_ids.join(", ")));
}
chips.appendChild(el("span", "chip", "verdict " + finding.verdict));
chips.appendChild(el("span", "chip", "confidence " + finding.confidence));
chips.appendChild(el("span", "chip",
finding.reports + " report(s): " + (finding.reporters || []).join(", ")));
card.appendChild(chips);
const loc = finding.location || {};
const span = loc.start_line && loc.end_line && loc.start_line !== loc.end_line
? loc.start_line + "-" + loc.end_line
: (loc.end_line || finding.line);
card.appendChild(el("p", "location", finding.file + ":" + span));
if ((finding.anchors || []).length) {
card.appendChild(el("p", "location", "Also reported at " +
finding.anchors.map(a => a.file + ":" + a.line + " (" + a.category +
", " + a.id + ")").join(", ") +
" — judged the same defect and folded in."));
}
if (finding.summary.trim() !== finding.short_summary.trim()) {
card.appendChild(el("p", null, finding.summary));
}
const scenario = el("p");
scenario.appendChild(el("span", "label", "Failure scenario. "));
scenario.appendChild(document.createTextNode(finding.failure_scenario));
card.appendChild(scenario);
if (finding.verdict_reasoning) {
const verifier = el("details");
verifier.appendChild(el("summary", null, "Verifier reasoning"));
verifier.appendChild(el("p", null, finding.verdict_reasoning));
card.appendChild(verifier);
}
const suggestion = renderSuggestedChange(finding);
if (suggestion) card.appendChild(suggestion);
const code = renderCode(finding.code);
if (code) card.appendChild(code);
return card;
}
function renderFindings() {
const holder = document.getElementById("findings");
const findings = DATA.findings || [];
if (!findings.length) {
holder.appendChild(el("p", "empty", "No findings survived review."));
return;
}
for (const finding of findings) holder.appendChild(renderFinding(finding));
}
function renderCoverage() {
const holder = document.getElementById("coverage");
const coverage = DATA.coverage || {};
const list = el("ul");
const finders = coverage.finders || {};
list.appendChild(el("li", null,
"Finder angles: " + finders.returned + " of " + finders.dispatched +
" returned a usable result."));
const verification = coverage.verification || {};
list.appendChild(el("li", null,
"Verification: " + verification.votesCompleted + " of " +
verification.votesDispatched + " verdict(s) returned (" +
verification.status + ")."));
const rules = coverage.rules;
if (rules) {
const effective = rules.effectiveChecksByFile || {};
let auditedFiles = 0;
const distinct = new Set();
for (const ids of Object.values(effective)) {
if ((ids || []).length) auditedFiles++;
(ids || []).forEach(id => distinct.add(id));
}
const cells = ((coverage.finders || {}).byKind || {})["rule-audit"] || {};
const counts = rules.counts || {};
const ruleFindings = (DATA.findings || [])
.filter(f => (f.rule_ids || []).length);
const filtered0 = (coverage.filteredFindingReports || []).length;
const folded = (((DATA.meta || {}).counts) || {}).duplicates || 0;
list.appendChild(el("li", null,
"Rules: audited " + distinct.size + " check(s) (" +
(counts.builtin_packs || 0) + " built-in + " +
(counts.repo_packs || 0) + " repository pack(s)) across " +
auditedFiles + " file(s) in " + (cells.returned || 0) + " of " +
(cells.dispatched || 0) + " audit cell(s); " + ruleFindings.length +
" violation(s) reported; " + filtered0 + " filtered; " +
folded + " folded."));
if (ruleFindings.length) {
const perCheck = {};
for (const finding of ruleFindings) {
for (const id of finding.rule_ids) {
perCheck[id] = (perCheck[id] || 0) + 1;
}
}
const item = el("li", null, "Violations by check:");
const sub = el("ul");
Object.entries(perCheck)
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
.forEach(([id, n]) => sub.appendChild(el("li", null, id + " x" + n)));
item.appendChild(sub);
list.appendChild(item);
}
}
const rejected = coverage.rejectedFindingReports || [];
if (rejected.length) {
const item = el("li", null,
"Rejected finding reports (" + rejected.length + "):");
const sub = el("ul");
for (const entry of rejected) sub.appendChild(el("li", null, entry));
item.appendChild(sub);
list.appendChild(item);
}
const filtered = coverage.filteredFindingReports || [];
if (filtered.length) {
const item = el("li", null,
"Filtered by review policy (" + filtered.length + "):");
const sub = el("ul");
for (const entry of filtered) sub.appendChild(el("li", null, entry));
item.appendChild(sub);
list.appendChild(item);
}
holder.appendChild(list);
}
renderMeta();
renderBanner();
renderFindings();
renderCoverage();
</script>
</body>
</html>