Sunstead trust scoring project
1<!doctype html>
2<html lang="en">
3<head>
4<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
5<title>Triage queue · Tangled trust</title>
6<style>
7 :root { --green:#1a7f37; --amber:#9a6700; --red:#cf222e; --bg:#f6f8fa; --card:#fff; --line:#d0d7de; }
8 body { font:14px/1.5 system-ui,sans-serif; margin:0; background:var(--bg); color:#1f2328; }
9 header { padding:16px 24px; background:var(--card); border-bottom:1px solid var(--line); }
10 h1 { margin:0; font-size:18px; } nav a { margin-right:14px; color:#0969da; text-decoration:none; }
11 main { max-width:1000px; margin:24px auto; padding:0 24px; }
12 .strip { display:flex; gap:12px; margin-bottom:20px; }
13 .metric { flex:1; background:var(--card); border:1px solid var(--line); border-radius:8px; padding:12px 16px; }
14 .metric b { display:block; font-size:24px; }
15 .group h2 { font-size:13px; text-transform:uppercase; letter-spacing:.04em; color:#656d76; margin:20px 0 8px; }
16 .row { background:var(--card); border:1px solid var(--line); border-radius:8px; padding:12px 16px; margin-bottom:8px; cursor:pointer; }
17 .row .top { display:flex; align-items:center; gap:12px; }
18 .pill { padding:2px 10px; border-radius:999px; color:#fff; font-weight:600; font-size:12px; }
19 .fast_lane{background:var(--green)} .normal_queue{background:var(--amber)} .needs_human{background:var(--red)}
20 .handle { font-weight:600; } .repo { color:#656d76; } .reason { color:#424a53; margin-left:auto; }
21 .detail { display:none; margin-top:10px; padding-top:10px; border-top:1px solid var(--line); font-size:13px; }
22 .row.open .detail { display:block; } .flag { color:var(--red); } code { background:#eaeef2; padding:1px 4px; border-radius:4px; }
23</style>
24</head>
25<body>
26<header>
27 <a href="/" style="display:block;margin-bottom:8px"><img src="/static/logo.svg" alt="CyberCred" height="24"></a>
28 <h1>Triage queue</h1>
29 <nav><a href="/">Triage</a><a href="/dashboard">Dashboard</a><a href="/leaderboard.html">Leaderboard</a><a href="/graph.html">Graph</a></nav>
30</header>
31<main>
32 <div class="strip" id="strip"></div>
33 <div id="groups"></div>
34</main>
35<script>
36const ORDER = ["needs_human","normal_queue","fast_lane"];
37const LABEL = {needs_human:"Needs review",normal_queue:"Normal queue",fast_lane:"Fast-lane"};
38fetch("/triage").then(r=>r.json()).then(rows=>{
39 const counts={fast_lane:0,normal_queue:0,needs_human:0};
40 rows.forEach(r=>counts[r.decision]++);
41 strip.innerHTML = `
42 <div class="metric"><b>${rows.length}</b>open PRs</div>
43 <div class="metric"><b>${counts.fast_lane}</b>fast-lane</div>
44 <div class="metric"><b>${counts.normal_queue}</b>normal</div>
45 <div class="metric"><b>${counts.needs_human}</b>needs review</div>`;
46 groups.innerHTML = ORDER.map(d=>{
47 const rs = rows.filter(r=>r.decision===d);
48 if(!rs.length) return "";
49 return `<div class="group"><h2>${LABEL[d]} (${rs.length})</h2>${rs.map(card).join("")}</div>`;
50 }).join("");
51 document.querySelectorAll(".row").forEach(el=>el.onclick=()=>el.classList.toggle("open"));
52});
53function card(r){
54 const e=r.explanation||{};
55 const path=(e.trust_path||[]).join(" → ")||"no path from seed";
56 const factors=(e.top_factors||[]).join(" · ");
57 const flags=(e.flags||[]).map(f=>`<div class="flag">⚑ [${f.severity}] ${f.type} @ ${f.location}: ${f.explanation}</div>`).join("");
58 return `<div class="row"><div class="top">
59 <span class="pill ${r.decision}">${(r.calibrated_prob*100).toFixed(0)}%</span>
60 <span class="handle">${r.handle||r.did}</span>
61 <span class="repo">${r.repo} · ${r.pr_id}</span>
62 <span class="reason">${factors||path}</span>
63 </div><div class="detail">
64 <div><b>Structural:</b> ${path}${factors?" · "+factors:""}</div>
65 ${e.content_summary?`<div><b>Claude:</b> ${e.content_summary}</div>`:"<div><i>No content review ran (cost-gated).</i></div>"}
66 ${flags}
67 </div></div>`;
68}
69</script>
70</body>
71</html>