sparta_worlds 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +15 -0
- package/bin/worlds.js +53 -0
- package/dist/cli/argv.js +27 -0
- package/dist/cli/claims.js +250 -0
- package/dist/cli/commands.js +67 -0
- package/dist/cli/contract.js +142 -0
- package/dist/cli/demo.js +114 -0
- package/dist/cli/doctor.js +299 -0
- package/dist/cli/expect.js +197 -0
- package/dist/cli/home.js +18 -0
- package/dist/cli/import.js +530 -0
- package/dist/cli/index.js +1014 -0
- package/dist/cli/init.js +725 -0
- package/dist/cli/judge-command.js +184 -0
- package/dist/cli/judge.js +1495 -0
- package/dist/cli/load.js +8 -0
- package/dist/cli/packs.js +82 -0
- package/dist/cli/passk.js +1256 -0
- package/dist/cli/policy-starters.js +83 -0
- package/dist/cli/preload.js +45 -0
- package/dist/cli/report.js +579 -0
- package/dist/cli/rubric.js +727 -0
- package/dist/cli/run.js +266 -0
- package/dist/cli/scoreboard.js +432 -0
- package/dist/cli/selftest.js +157 -0
- package/dist/cli/stdio.js +25 -0
- package/dist/cli/tasks.js +1246 -0
- package/dist/cli/try.js +156 -0
- package/dist/cli/version.js +17 -0
- package/package.json +38 -0
- package/packs/cancellations.yaml +201 -0
- package/packs/credits.yaml +274 -0
- package/packs/invoices.yaml +206 -0
- package/packs/plan-changes.yaml +170 -0
- package/packs/refunds.yaml +437 -0
- package/preload/node-hooks.mjs +28 -0
- package/preload/node.mjs +19 -0
- package/preload/python/sitecustomize.py +88 -0
- package/preload/wrap.mjs +81 -0
- package/rubrics/acme-prod-library.json +1427 -0
- package/rubrics/acme-prod-shift.json +499 -0
- package/server/dist/admin/auth.js +145 -0
- package/server/dist/admin/routes.js +469 -0
- package/server/dist/admin/schemas.js +243 -0
- package/server/dist/app.js +112 -0
- package/server/dist/core/canonical.js +22 -0
- package/server/dist/core/db.js +294 -0
- package/server/dist/core/errors.js +136 -0
- package/server/dist/core/manager.js +337 -0
- package/server/dist/core/money.js +43 -0
- package/server/dist/core/nondeterministic.js +39 -0
- package/server/dist/core/paths.js +33 -0
- package/server/dist/core/prng.js +19 -0
- package/server/dist/core/protocol.js +1 -0
- package/server/dist/core/publicUrl.js +49 -0
- package/server/dist/core/seed.js +286 -0
- package/server/dist/core/seedExport.js +169 -0
- package/server/dist/core/seedLoader.js +156 -0
- package/server/dist/core/time.js +45 -0
- package/server/dist/core/world.js +361 -0
- package/server/dist/index.js +17 -0
- package/server/dist/main.js +42 -0
- package/server/dist/scenario/engine.js +172 -0
- package/server/dist/scenario/library.js +49 -0
- package/server/dist/twin/build.js +372 -0
- package/server/dist/twin/pagination.js +32 -0
- package/server/dist/twin/params.js +118 -0
- package/server/dist/twin/resources/charges.js +24 -0
- package/server/dist/twin/resources/customers.js +151 -0
- package/server/dist/twin/resources/events.js +26 -0
- package/server/dist/twin/resources/invoices.js +217 -0
- package/server/dist/twin/resources/payment_intents.js +146 -0
- package/server/dist/twin/resources/products.js +92 -0
- package/server/dist/twin/resources/refunds.js +103 -0
- package/server/dist/twin/resources/shared.js +113 -0
- package/server/dist/twin/resources/subscriptions.js +325 -0
- package/server/dist/twin/resources/tickets.js +128 -0
- package/server/dist/twin/router.js +279 -0
- package/server/dist/twin/unsupported.js +212 -0
- package/server/dist/webhooks/dispatcher.js +148 -0
- package/server/scenarios/auth-expiry.yaml +7 -0
- package/server/scenarios/intermittent-500s.yaml +9 -0
- package/server/scenarios/lock-timeouts.yaml +12 -0
- package/server/scenarios/rate-limit-storm.yaml +10 -0
- package/server/scenarios/slow-stripe.yaml +9 -0
- package/server/scenarios/stripe-storm-shift.yaml +15 -0
- package/server/scenarios/tickets-storm.yaml +10 -0
- package/server/scenarios/webhook-chaos.yaml +7 -0
- package/server/seeds/acme-prod-library.json +40470 -0
- package/server/seeds/acme-prod-shift.json +36544 -0
- package/server/seeds/acme-prod.json +35412 -0
- package/server/seeds/saas-billing-small.json +4792 -0
- package/server/seeds/saas-billing-smoke.json +160 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
export const esc = (s) => String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2
|
+
const ZERO_DECIMAL = new Set("bif clp djf gnf jpy kmf krw mga pyg rwf ugx vnd vuv xaf xof xpf".split(" "));
|
|
3
|
+
const THREE_DECIMAL = new Set("bhd jod kwd omr tnd".split(" "));
|
|
4
|
+
export function money(minor, currency = "usd") {
|
|
5
|
+
const c = currency.toLowerCase();
|
|
6
|
+
const sym = c === "usd" ? "$" : c === "eur" ? "€" : c === "gbp" ? "£" : `${c.toUpperCase()} `;
|
|
7
|
+
const decimals = ZERO_DECIMAL.has(c) ? 0 : THREE_DECIMAL.has(c) ? 3 : 2;
|
|
8
|
+
const sign = minor < 0 ? "−" : "";
|
|
9
|
+
const units = Math.abs(minor) / 10 ** decimals;
|
|
10
|
+
return `${sign}${sym}${units.toLocaleString("en-US", { minimumFractionDigits: decimals, maximumFractionDigits: decimals })}`;
|
|
11
|
+
}
|
|
12
|
+
function agentCell(r) {
|
|
13
|
+
if (r.timed_out)
|
|
14
|
+
return "timed out";
|
|
15
|
+
if (r.agent_exit !== null)
|
|
16
|
+
return `exit ${r.agent_exit}`;
|
|
17
|
+
if (r.agent_signal)
|
|
18
|
+
return `killed by ${esc(r.agent_signal)}`;
|
|
19
|
+
return "not run";
|
|
20
|
+
}
|
|
21
|
+
const isGraded = (r) => r.status === undefined || r.status === "passed" || r.status === "failed";
|
|
22
|
+
function verdictPill(r) {
|
|
23
|
+
if (r.pass)
|
|
24
|
+
return `<span class="pill pill-good">✓ PASS</span>`;
|
|
25
|
+
if (isGraded(r))
|
|
26
|
+
return `<span class="pill pill-bad">✕ FAIL</span>`;
|
|
27
|
+
return `<span class="pill pill-warn">⚠ ${esc(String(r.status).replace(/_/g, " ").toUpperCase())}</span>`;
|
|
28
|
+
}
|
|
29
|
+
function netMovementLine(runs) {
|
|
30
|
+
const nets = runs.map((r) => Object.entries(r.money)
|
|
31
|
+
.map(([c, m]) => money(m.net_movement, c))
|
|
32
|
+
.join(" · ") || "$0.00");
|
|
33
|
+
const uniq = [...new Set(nets)];
|
|
34
|
+
if (uniq.length === 1)
|
|
35
|
+
return { headline: uniq[0], sub: "identical across every run", disagree: false };
|
|
36
|
+
return { headline: `${uniq.length} different totals`, sub: `runs disagree: ${uniq.join(" vs ")}`, disagree: true };
|
|
37
|
+
}
|
|
38
|
+
const VERDICT_GLYPH = { correct: "✓", wrong: "✕", missed: "○", overreach: "⚠" };
|
|
39
|
+
const VERDICT_CLASS = {
|
|
40
|
+
correct: "c-ok",
|
|
41
|
+
wrong: "c-bad",
|
|
42
|
+
missed: "c-miss",
|
|
43
|
+
overreach: "c-over",
|
|
44
|
+
};
|
|
45
|
+
const POLICY_GLYPH = "·";
|
|
46
|
+
const POLICY_CLASS = "c-policy";
|
|
47
|
+
export function pct(x) {
|
|
48
|
+
const s = (x * 100).toFixed(1);
|
|
49
|
+
return `${s.endsWith(".0") ? s.slice(0, -2) : s}%`;
|
|
50
|
+
}
|
|
51
|
+
export function cliffSvg(pMean, n, observed) {
|
|
52
|
+
const W = 520, H = 150, L = 40, R = 16, T = 14, B = 28;
|
|
53
|
+
const px = (i) => L + ((i - 1) / Math.max(1, n - 1)) * (W - L - R);
|
|
54
|
+
const py = (p) => T + (1 - p) * (H - T - B);
|
|
55
|
+
const pts = Array.from({ length: n }, (_, k) => `${px(k + 1).toFixed(1)},${py(pMean ** (k + 1)).toFixed(1)}`).join(" ");
|
|
56
|
+
const endX = px(n).toFixed(1);
|
|
57
|
+
const predicted = pMean ** n;
|
|
58
|
+
const ticks = [0, 0.5, 1]
|
|
59
|
+
.map((p) => `<line x1="${L}" x2="${W - R}" y1="${py(p).toFixed(1)}" y2="${py(p).toFixed(1)}" class="cliff-grid"/><text x="${L - 6}" y="${(py(p) + 4).toFixed(1)}" class="cliff-tick" text-anchor="end">${pct(p)}</text>`)
|
|
60
|
+
.join("");
|
|
61
|
+
return `<svg viewBox="0 0 ${W} ${H}" class="cliff" role="img" aria-label="predicted flawless-shift rate against shift length">
|
|
62
|
+
${ticks}
|
|
63
|
+
<text x="${L}" y="${H - 8}" class="cliff-tick">1 ticket</text>
|
|
64
|
+
<text x="${W - R}" y="${H - 8}" class="cliff-tick" text-anchor="end">${n} tickets</text>
|
|
65
|
+
<polyline points="${pts}" class="cliff-line"/>
|
|
66
|
+
<circle cx="${endX}" cy="${py(predicted).toFixed(1)}" r="4.5" class="cliff-predicted"/>
|
|
67
|
+
<circle cx="${endX}" cy="${py(observed).toFixed(1)}" r="4.5" class="cliff-observed"/>
|
|
68
|
+
<text x="${(Number(endX) - 8).toFixed(1)}" y="${(predicted > 0.85 ? py(predicted) + 18 : py(predicted) - 8).toFixed(1)}" class="cliff-label" text-anchor="end">predicted ${pct(predicted)}</text>
|
|
69
|
+
</svg>`;
|
|
70
|
+
}
|
|
71
|
+
function crashTestPanel(tasks, runs, currency) {
|
|
72
|
+
const ct = tasks.crash_test;
|
|
73
|
+
const judged = runs.filter((r) => r.judge !== undefined);
|
|
74
|
+
const judgeMean = judged.length
|
|
75
|
+
? Math.round(judged.reduce((n, r) => n + (r.judge?.dollars_wrong_cents ?? 0), 0) / judged.length)
|
|
76
|
+
: null;
|
|
77
|
+
const shortHash = (h) => (h ? `${h.slice(0, 12)}…` : "—");
|
|
78
|
+
const rows = ct.tasks
|
|
79
|
+
.map((t) => {
|
|
80
|
+
const glyphs = ["correct", "wrong", "missed", "overreach"]
|
|
81
|
+
.filter((k) => t.verdicts[k] > 0)
|
|
82
|
+
.map((k) => `<span class="${VERDICT_CLASS[k]}">${VERDICT_GLYPH[k]}${t.verdicts[k]}</span>`)
|
|
83
|
+
.join(" ");
|
|
84
|
+
const acc = t.accuracy === null
|
|
85
|
+
? `<span class="${POLICY_CLASS}">policy</span>`
|
|
86
|
+
: `${t.passed}/${t.runs} <span class="s">(${pct(t.interval.low)}–${pct(t.interval.high)})</span>`;
|
|
87
|
+
const judge = t.judge
|
|
88
|
+
? Object.entries(t.judge.verdicts)
|
|
89
|
+
.sort(([, a], [, b]) => b - a)
|
|
90
|
+
.map(([v, n]) => `<span class="${JUDGE_CLASS[v] ?? ""}">${JUDGE_GLYPH[v] ?? ""}${n}</span>`)
|
|
91
|
+
.join(" ") +
|
|
92
|
+
(Object.keys(t.judge.diagnoses).filter((d) => d !== "none").length
|
|
93
|
+
? ` <span class="badge">${esc(Object.entries(t.judge.diagnoses)
|
|
94
|
+
.filter(([d]) => d !== "none")
|
|
95
|
+
.sort(([, a], [, b]) => b - a)
|
|
96
|
+
.map(([d, n]) => `${d} ×${n}`)
|
|
97
|
+
.join(", "))}</span>`
|
|
98
|
+
: "")
|
|
99
|
+
: "—";
|
|
100
|
+
const dollars = t.dollars_wrong_total === null ? "—" : esc(money(t.dollars_wrong_total, currency));
|
|
101
|
+
const worlds = t.world_ids.length ? esc(t.world_ids.map((w) => w ?? "—").join(" ")) : "—";
|
|
102
|
+
return `<tr><td class="num">${t.position}</td><td>${esc(t.label)}</td><td>${esc(t.requester)}</td><td><code>${esc(t.expected ?? "policy")}</code></td><td class="num">${acc}</td><td>${glyphs || "—"}</td><td>${judge}</td><td class="num">${dollars}</td><td class="worlds">${worlds}</td></tr>`;
|
|
103
|
+
})
|
|
104
|
+
.join("\n");
|
|
105
|
+
const worst = ct.worst_task
|
|
106
|
+
? `<p class="worst"><strong>Worst task: ${esc(ct.worst_task.label)}</strong> — failed ${ct.worst_task.failed_runs} of ${ct.runs} run${ct.runs === 1 ? "" : "s"}${ct.worst_task.dollars_wrong_total !== null ? `, ${esc(money(ct.worst_task.dollars_wrong_total, currency))} wrong` : ""}${ct.worst_task.replay ? `. Replay it:</p><pre class="cmd">${esc(ct.worst_task.replay)}</pre>` : ".</p>"}`
|
|
107
|
+
: `<p class="worst"><strong>No task failed a graded run.</strong></p>`;
|
|
108
|
+
return `
|
|
109
|
+
<section class="crash">
|
|
110
|
+
<h2>Crash test — ${esc(tasks.name)}${tasks.pack ? ` <span class="badge">pack ${esc(tasks.pack)}</span>` : ""} <span class="badge">${esc(tasks.mode)}</span></h2>
|
|
111
|
+
<div class="tiles">
|
|
112
|
+
<div class="tile ${ct.dollars_wrong_mean > 0 ? "bad" : "good"}"><div class="k">dollars wrong per run</div><div class="v num">${esc(money(ct.dollars_wrong_mean, currency))}</div><div class="s">the key's, over ${ct.graded_runs} graded run${ct.graded_runs === 1 ? "" : "s"}${judgeMean !== null ? ` · judge (advisory): ${esc(money(judgeMean, currency))}` : ""}</div></div>
|
|
113
|
+
<div class="tile ${ct.flawless_runs === ct.runs ? "good" : "bad"}"><div class="k">flawless runs</div><div class="v">${ct.flawless_runs}/${ct.runs}</div><div class="s">every task right, nothing else touched</div></div>
|
|
114
|
+
<div class="tile"><div class="k">tasks</div><div class="v">${ct.tasks.length}</div><div class="s">${tasks.keyed} keyed · ${tasks.policy_only} policy-only</div></div>
|
|
115
|
+
</div>
|
|
116
|
+
${worst}
|
|
117
|
+
<div class="grid-wrap"><table class="findings"><thead><tr><th class="num">#</th><th>task</th><th>requester</th><th>expected</th><th class="num">accuracy</th><th>key</th><th>judge</th><th class="num">dollars</th><th>worlds</th></tr></thead><tbody>
|
|
118
|
+
${rows}
|
|
119
|
+
</tbody></table></div>
|
|
120
|
+
<div class="meta-line">tasks file <code>${esc(tasks.name)}</code> sha256 <code>${esc(shortHash(tasks.hash))}</code> · seed <code>${esc(shortHash(tasks.seed_hash))}</code> (base <code>${esc(shortHash(tasks.base_seed_hash))}</code>) · rubric <code>${esc(shortHash(tasks.rubric_hash))}</code>${tasks.pack ? ` · pack <code>${esc(tasks.pack)}</code>` : ""}</div>
|
|
121
|
+
</section>
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
function shiftPanel(shift, runs) {
|
|
125
|
+
const s = shift.summary;
|
|
126
|
+
const mismatches = s.claims.claimed_not_done + s.claims.did_not_claimed + s.claims.amount_mismatch;
|
|
127
|
+
const classes = Object.entries(s.per_trap_class).sort(([a], [b]) => a.localeCompare(b));
|
|
128
|
+
const headerCells = runs.map((r) => `<th class="num">run ${r.run}</th>`).join("");
|
|
129
|
+
const rows = shift.rubric.tickets
|
|
130
|
+
.map((t) => {
|
|
131
|
+
const cells = runs
|
|
132
|
+
.map((r) => {
|
|
133
|
+
const tr = r.shift?.tickets.find((x) => x.id === t.id);
|
|
134
|
+
if (!tr)
|
|
135
|
+
return `<td class="num">–</td>`;
|
|
136
|
+
const flag = tr.claims.status !== "consistent" && tr.claims.status !== "silent" ? "!" : "";
|
|
137
|
+
const jv = r.judge?.tickets.find((x) => x.ticket_id === t.id);
|
|
138
|
+
if (tr.verdict === null || !tr.graded) {
|
|
139
|
+
const judged = jv
|
|
140
|
+
? `<span class="judge-mark${jv.verdict === "uncertain" ? " judge-abstain" : ""}" title="${esc(`judge: ${jv.verdict} (${jv.diagnosis}) — ${jv.reason}`)}">${jv.verdict === "uncertain" ? "?" : "⚖"}</span>`
|
|
141
|
+
: "";
|
|
142
|
+
const observed = tr.observed.length ? ` · did: ${tr.observed.join(", ")}` : "";
|
|
143
|
+
return `<td class="num cell ${POLICY_CLASS}" title="${esc(`policy-only (no key)${observed}`)}">${POLICY_GLYPH}${flag}${judged}</td>`;
|
|
144
|
+
}
|
|
145
|
+
const title = `${tr.verdict}${flag ? ` · reply ${tr.claims.status.replace(/_/g, " ")}` : ""}${tr.missing.length ? ` · missing: ${tr.missing.join(", ")}` : ""}${tr.extra.length ? ` · extra: ${tr.extra.join(", ")}` : ""}${tr.decoy_touches.length ? ` · touched: ${tr.decoy_touches.join(", ")}` : ""}`;
|
|
146
|
+
const mark = !jv
|
|
147
|
+
? ""
|
|
148
|
+
: jv.verdict === "uncertain"
|
|
149
|
+
? `<span class="judge-mark judge-abstain" title="${esc(`judge abstained: ${jv.reason}`)}">?</span>`
|
|
150
|
+
: jv.verdict !== tr.verdict
|
|
151
|
+
? `<span class="judge-mark" title="${esc(`judge: ${jv.verdict} (${jv.diagnosis}) — ${jv.reason}`)}">⚖</span>`
|
|
152
|
+
: "";
|
|
153
|
+
return `<td class="num cell ${VERDICT_CLASS[tr.verdict]}${tr.pass ? "" : " cell-fail"}" title="${esc(title)}">${VERDICT_GLYPH[tr.verdict]}${flag}${mark}</td>`;
|
|
154
|
+
})
|
|
155
|
+
.join("");
|
|
156
|
+
const p = s.per_ticket[t.id]?.p ?? 0;
|
|
157
|
+
return `<tr><td class="num">${esc(t.id)}</td><td>${esc(t.subject ?? `ticket ${t.id}`)}${t.trap_class ? ` <span class="badge">${esc(t.trap_class)}</span>` : ""}</td>${cells}<td class="num ${p === 1 ? "" : "s-bad"}">${pct(p)}</td></tr>`;
|
|
158
|
+
})
|
|
159
|
+
.join("\n");
|
|
160
|
+
const footer = runs.map((r) => `<td class="num">${r.shift?.pass ? "✓" : "✕"}</td>`).join("");
|
|
161
|
+
const shifts = `${s.runs} shift${s.runs === 1 ? "" : "s"}`;
|
|
162
|
+
return `
|
|
163
|
+
<h2>The shift, graded per ticket</h2>
|
|
164
|
+
<div class="meta-line">rubric <code>${esc(shift.rubric.name)}</code> · ${s.tickets} tickets × ${s.runs} run${s.runs === 1 ? "" : "s"} · claims checked ${esc(shift.rubric.claims)} (keyword-based, deterministic — a consistency check, not a model judge)</div>
|
|
165
|
+
<div class="tiles">
|
|
166
|
+
<div class="tile ${s.p_mean === 1 ? "good" : ""}"><div class="k">per-ticket accuracy p̄</div><div class="v num">${pct(s.p_mean)}</div><div class="s">mean over every ticket in every run</div></div>
|
|
167
|
+
<div class="tile ${s.observed_pass_rate === 1 ? "good" : "bad"}"><div class="k">observed flawless shifts</div><div class="v">${s.whole_shift_passes}/${s.runs}</div><div class="s">every ticket right and nothing else touched — ${shifts}</div></div>
|
|
168
|
+
<div class="tile"><div class="k">predicted flawless shift</div><div class="v num">${pct(s.predicted_pass_rate)}</div><div class="s">p̄^${s.tickets}: the independence model's prediction, not a measurement</div></div>
|
|
169
|
+
<div class="tile ${s.wrong_dollars_total > 0 ? "bad" : ""}"><div class="k">dollars wrong</div><div class="v num">${money(s.wrong_dollars_total, s.currency)}</div><div class="s">unexpected refunds/credits + refunds owed but not made</div></div>
|
|
170
|
+
<div class="tile ${mismatches > 0 ? "bad" : ""}"><div class="k">reply ≠ world</div><div class="v">${mismatches}</div><div class="s">${s.claims.claimed_not_done} claimed-not-done · ${s.claims.did_not_claimed} did-not-claimed · ${s.claims.amount_mismatch} amount</div></div>
|
|
171
|
+
</div>
|
|
172
|
+
<div class="cliff-wrap">
|
|
173
|
+
${cliffSvg(s.p_mean, s.tickets, s.observed_pass_rate)}
|
|
174
|
+
<p class="cliff-caption">Observed: ${s.whole_shift_passes} of ${shifts} flawless (○). At ${pct(s.p_mean)} per ticket the independence model predicts <strong>${pct(s.predicted_pass_rate)}</strong> for a ${s.tickets}-ticket shift (●); a failure that repeats on the same ticket every shift is exactly what the model ignores, so it overestimates. Failures compound with length; the workday is the unit.</p>
|
|
175
|
+
</div>
|
|
176
|
+
${classes.length > 1 ? `<table class="classes"><thead><tr><th>trap class</th><th class="num">passed</th><th class="num">graded</th><th class="num">accuracy</th></tr></thead><tbody>${classes.map(([c, v]) => `<tr><td>${esc(c)}</td><td class="num">${v.passed}</td><td class="num">${v.graded}</td><td class="num ${v.p === 1 ? "" : "s-bad"}">${pct(v.p)}</td></tr>`).join("")}</tbody></table>` : ""}
|
|
177
|
+
<div class="grid-wrap">
|
|
178
|
+
<table class="grid">
|
|
179
|
+
<thead><tr><th>#</th><th>ticket</th>${headerCells}<th class="num">p</th></tr></thead>
|
|
180
|
+
<tbody>
|
|
181
|
+
${rows}
|
|
182
|
+
<tr class="grid-foot"><td></td><td>whole shift</td>${footer}<td class="num">${pct(s.observed_pass_rate)}</td></tr>
|
|
183
|
+
</tbody>
|
|
184
|
+
</table>
|
|
185
|
+
</div>
|
|
186
|
+
<p class="legend">✓ correct · ✕ wrong · ○ missed · ⚠ overreach · <strong>!</strong> the reply disagrees with the world${runs.some((r) => r.judge) ? " · ⚖ the judge disagrees with the key (hover for its reason) · ? the judge abstained" : ""}</p>`;
|
|
187
|
+
}
|
|
188
|
+
const JUDGE_GLYPH = { ...VERDICT_GLYPH, uncertain: "?" };
|
|
189
|
+
const JUDGE_CLASS = { ...VERDICT_CLASS, uncertain: "c-miss" };
|
|
190
|
+
function judgePanel(judge, runs) {
|
|
191
|
+
const s = judge.summary;
|
|
192
|
+
const currency = runs.find((r) => r.shift)?.shift?.currency ?? "usd";
|
|
193
|
+
const a = s.agreement;
|
|
194
|
+
const agreementTile = a
|
|
195
|
+
? `<div class="tile ${a.rate === 1 ? "good" : a.rate !== null && a.rate < 0.9 ? "bad" : ""}"><div class="k">agrees with the key</div><div class="v num">${a.rate === null ? "—" : pct(a.rate)}</div><div class="s">${a.agree}/${a.compared} tickets${a.abstained ? ` · ${a.abstained} abstained` : ""}${a.kappa !== null ? ` · κ ${a.kappa.toFixed(2)}` : ""}</div></div>`
|
|
196
|
+
: "";
|
|
197
|
+
const costTile = s.cost_usd !== null
|
|
198
|
+
? `<div class="tile"><div class="k">judge cost</div><div class="v num">$${s.cost_usd.toFixed(2)}</div><div class="s">${s.usage.calls} model call${s.usage.calls === 1 ? "" : "s"} · apart from the agent's</div></div>`
|
|
199
|
+
: "";
|
|
200
|
+
const findings = s.findings_ranked
|
|
201
|
+
.map((f) => `<tr><td class="num">${f.run}</td><td class="num">${esc(f.ticket_id)}</td><td>${esc(f.subject)}</td><td><span class="${JUDGE_CLASS[f.verdict]}">${JUDGE_GLYPH[f.verdict]} ${esc(f.verdict)}</span></td><td><span class="badge">${esc(f.diagnosis)}</span></td><td class="num">${esc(money(f.dollars_wrong_cents, currency))}</td>${a ? `<td>${f.key ? esc(f.key) : "—"}</td>` : ""}<td class="reason">${esc(f.reason)}</td></tr>`)
|
|
202
|
+
.join("\n");
|
|
203
|
+
const disagreements = a?.disagreements.length
|
|
204
|
+
? `<details><summary>Where the judge and the key disagree (${a.disagreements.length})</summary><ul class="expects-failed">${a.disagreements.map((d) => `<li>run ${d.run} · ticket ${esc(d.ticket_id)}: key <strong>${esc(d.key)}</strong>, judge <strong>${esc(d.judge)}</strong> — ${esc(d.reason)}</li>`).join("")}</ul></details>`
|
|
205
|
+
: "";
|
|
206
|
+
const stray = runs.flatMap((r) => (r.judge?.unattributed ?? []).map((u) => ({ run: r.run, ...u })));
|
|
207
|
+
const strayList = stray.length
|
|
208
|
+
? `<ul class="expects-failed">${stray.map((u) => `<li>run ${u.run} · outside every ticket: <code>${esc(u.action)}</code>${u.customer ? ` (customer ${esc(u.customer)})` : ""}</li>`).join("")}</ul>`
|
|
209
|
+
: "";
|
|
210
|
+
return `
|
|
211
|
+
<h2>The judge (advisory)</h2>
|
|
212
|
+
<div class="meta-line">${esc(judge.model)}${judge.runs > 1 ? ` × ${judge.runs} samples per ticket` : ""} · one policy text, no answer key · reads the world's records, never the transcript · never changes pass or the exit code</div>
|
|
213
|
+
<div class="tiles">
|
|
214
|
+
<div class="tile ${s.findings > 0 ? "bad" : "good"}"><div class="k">findings</div><div class="v">${s.findings}</div><div class="s">wrong, missed or overreach over ${s.runs} run${s.runs === 1 ? "" : "s"}</div></div>
|
|
215
|
+
<div class="tile ${s.uncertain > 0 ? "warn" : ""}"><div class="k">uncertain</div><div class="v">${s.uncertain}</div><div class="s">the facts in view could not settle it</div></div>
|
|
216
|
+
<div class="tile ${s.dollars_wrong_total > 0 ? "bad" : ""}"><div class="k">dollars wrong (judge)</div><div class="v num">${esc(money(s.dollars_wrong_total, currency))}</div><div class="s">quoted from amounts in view, never computed</div></div>
|
|
217
|
+
${agreementTile}
|
|
218
|
+
${costTile}
|
|
219
|
+
</div>
|
|
220
|
+
${findings
|
|
221
|
+
? `<div class="grid-wrap"><table class="findings"><thead><tr><th class="num">run</th><th class="num">#</th><th>ticket</th><th>verdict</th><th>diagnosis</th><th class="num">dollars</th>${a ? "<th>key</th>" : ""}<th>reason</th></tr></thead><tbody>${findings}</tbody></table></div>`
|
|
222
|
+
: `<p class="legend">No findings.</p>`}
|
|
223
|
+
${disagreements}
|
|
224
|
+
${strayList}`;
|
|
225
|
+
}
|
|
226
|
+
function shiftRunDetail(s) {
|
|
227
|
+
const failed = s.tickets.filter((t) => t.graded && t.verdict !== null && !t.pass);
|
|
228
|
+
if (failed.length === 0 && s.unattributed.length === 0 && !s.budget.exceeded && s.unsupported_currencies.length === 0)
|
|
229
|
+
return "";
|
|
230
|
+
const rows = failed
|
|
231
|
+
.map((t) => {
|
|
232
|
+
const said = t.claims.excerpt ? `“${esc(t.claims.excerpt)}”` : t.replied ? "(replied)" : "(never opened)";
|
|
233
|
+
const world = [...t.observed.map((o) => esc(o)), ...(t.status ? [`ticket → ${esc(t.status)}`] : [])].join("<br>") ||
|
|
234
|
+
"nothing";
|
|
235
|
+
const why = t.verdict === "correct"
|
|
236
|
+
? `reply ${esc(t.claims.status.replace(/_/g, " "))}`
|
|
237
|
+
: [
|
|
238
|
+
...t.missing.map((m) => `missing ${esc(m)}`),
|
|
239
|
+
...t.extra.map((x) => `extra ${esc(x)}`),
|
|
240
|
+
...t.decoy_touches.map((x) => `touched ${esc(x)}`),
|
|
241
|
+
].join("; ") || (t.replied ? `status ${esc(t.status)}` : "never opened");
|
|
242
|
+
const verdict = t.verdict ?? "policy";
|
|
243
|
+
return `<div class="ticket-row"><div class="ticket-head"><strong>ticket ${esc(t.id)}</strong> ${t.subject ? esc(t.subject) : ""} — <span class="${VERDICT_CLASS[verdict] ?? POLICY_CLASS}">${VERDICT_GLYPH[verdict] ?? POLICY_GLYPH} ${esc(verdict)}</span> <span class="why">${why}</span></div>
|
|
244
|
+
<div class="contrast"><div class="contrast-cell"><div class="contrast-label">What the reply said</div><div class="contrast-quote said">${said}</div></div><div class="contrast-cell contrast-bad"><div class="contrast-label">What the world recorded</div><div class="contrast-quote"><code>${world}</code></div></div></div></div>`;
|
|
245
|
+
})
|
|
246
|
+
.join("\n");
|
|
247
|
+
const stray = s.unattributed.length
|
|
248
|
+
? `<ul class="expects-failed">${s.unattributed.map((u) => `<li>✕ outside the rubric: <code>${esc(u.action)}</code>${u.customer ? ` (customer ${esc(u.customer)})` : ""}</li>`).join("")}</ul>`
|
|
249
|
+
: "";
|
|
250
|
+
const budget = s.budget.exceeded
|
|
251
|
+
? `<ul class="expects-failed"><li>✕ shift budget exceeded: ${money(s.money.actual_credits, s.currency)} credited${s.budget.credits_total_max !== null ? ` against ${money(s.budget.credits_total_max, s.currency)}` : ""}</li></ul>`
|
|
252
|
+
: "";
|
|
253
|
+
const unsupported = s.unsupported_currencies.length
|
|
254
|
+
? `<ul class="expects-failed"><li>✕ monetary grading supports one currency per rubric (${esc(s.currency)}) — saw ${esc(s.unsupported_currencies.join(", "))}</li></ul>`
|
|
255
|
+
: "";
|
|
256
|
+
return `<div class="shift-detail"><div class="contrast-label">Tickets that did not pass (${failed.length} of ${s.tickets_total})</div>${rows}${stray}${budget}${unsupported}</div>`;
|
|
257
|
+
}
|
|
258
|
+
function runSection(r) {
|
|
259
|
+
const failedExpects = r.expectations.filter((e) => !e.pass);
|
|
260
|
+
const injectedCount = r.requests.filter((q) => q.injected).length;
|
|
261
|
+
const claimsSuccess = r.agent_exit === 0 && !r.timed_out;
|
|
262
|
+
const lied = isGraded(r) && claimsSuccess && !r.pass;
|
|
263
|
+
const timeline = r.requests
|
|
264
|
+
.map((q) => {
|
|
265
|
+
const cls = q.injected ? "req req-storm" : q.status >= 400 ? "req req-err" : "req";
|
|
266
|
+
const badge = q.injected
|
|
267
|
+
? `<span class="badge badge-storm">⛈ storm</span>`
|
|
268
|
+
: q.matched === 0
|
|
269
|
+
? `<span class="badge">not twinned</span>`
|
|
270
|
+
: "";
|
|
271
|
+
const mut = q.method !== "GET" ? `<span class="badge badge-mut">writes</span>` : "";
|
|
272
|
+
return `<div class="${cls}"><span class="req-seq">#${q.seq}</span><span class="req-m">${esc(q.method)}</span><span class="req-p">${esc(q.path)}</span>${mut}${badge}<span class="req-s ${q.status >= 400 ? "s-bad" : "s-ok"}">${q.status}</span></div>`;
|
|
273
|
+
})
|
|
274
|
+
.join("\n");
|
|
275
|
+
const objRow = (o, verb = "") => `<li>${verb}<code>${esc(o.id)}</code> — ${esc(o.resource)}${o.amount !== undefined ? `, <strong class="num">${esc(money(o.amount, o.currency))}</strong>` : ""}${o.extra ? ` (${esc(o.extra)})` : ""}</li>`;
|
|
276
|
+
const created = r.createdObjects.map((o) => objRow(o)).join("\n");
|
|
277
|
+
const changed = [
|
|
278
|
+
...(r.updatedObjects ?? []).map((o) => objRow(o)),
|
|
279
|
+
...(r.deletedObjects ?? []).map((o) => objRow(o, "deleted ")),
|
|
280
|
+
].join("\n");
|
|
281
|
+
return `
|
|
282
|
+
<section class="run ${r.pass ? "" : "run-failed"}">
|
|
283
|
+
<header class="run-head">
|
|
284
|
+
<h3>Run ${r.run} ${verdictPill(r)}</h3>
|
|
285
|
+
<div class="run-meta">${r.world_id ? `world <code>${esc(r.world_id)}</code>` : "no world"} · agent ${agentCell(r)} · ${(r.duration_ms / 1000).toFixed(1)}s${injectedCount ? ` · ${injectedCount} storm rejection${injectedCount === 1 ? "" : "s"}` : ""}</div>
|
|
286
|
+
</header>
|
|
287
|
+
${r.error !== undefined || r.cleanup_error !== undefined
|
|
288
|
+
? `
|
|
289
|
+
<ul class="expects-failed">
|
|
290
|
+
${r.error !== undefined ? `<li>⚠ <strong>${esc(String(r.status ?? "error").replace(/_/g, " "))}</strong> — ${esc(r.error)}${r.evidence ? ` · evidence: <code>${esc(r.evidence)}</code>` : ""}</li>` : ""}
|
|
291
|
+
${r.cleanup_error !== undefined ? `<li>⚠ world <code>${esc(r.world_id ?? "")}</code> was not destroyed — ${esc(r.cleanup_error)}</li>` : ""}
|
|
292
|
+
</ul>`
|
|
293
|
+
: ""}
|
|
294
|
+
${lied
|
|
295
|
+
? `
|
|
296
|
+
<div class="contrast">
|
|
297
|
+
<div class="contrast-cell"><div class="contrast-label">What the agent reported</div><div class="contrast-quote">“Done — exit 0 ✓”</div></div>
|
|
298
|
+
<div class="contrast-cell contrast-bad"><div class="contrast-label">What the world recorded</div><div class="contrast-quote">${r.createdObjects.filter((o) => o.resource === "refunds").length} refunds · ${Object.entries(r.money)
|
|
299
|
+
.map(([c, m]) => esc(money(m.net_movement, c)))
|
|
300
|
+
.join(" · ")} net</div></div>
|
|
301
|
+
</div>`
|
|
302
|
+
: ""}
|
|
303
|
+
${failedExpects.length > 0
|
|
304
|
+
? `
|
|
305
|
+
<ul class="expects-failed">
|
|
306
|
+
${failedExpects.map((e) => `<li>✕ <code>${esc(e.expr)}</code> — actual <strong>${esc(JSON.stringify(e.actual))}</strong></li>`).join("\n")}
|
|
307
|
+
</ul>`
|
|
308
|
+
: ""}
|
|
309
|
+
${r.shift ? `<div class="run-meta">tickets ${r.shift.tickets_passed}/${r.shift.tickets_total} passed · ${money(r.shift.money.wrong_dollars, r.shift.currency)} wrong</div>${shiftRunDetail(r.shift)}` : ""}
|
|
310
|
+
${r.judge ? `<div class="run-meta">⚖ judge: ${r.judge.findings} finding${r.judge.findings === 1 ? "" : "s"} · ${r.judge.uncertain} uncertain · ${money(r.judge.dollars_wrong_cents, r.shift?.currency ?? "usd")} wrong${r.judge.cost_usd !== null ? ` · $${r.judge.cost_usd.toFixed(2)}` : ""}</div>` : ""}
|
|
311
|
+
${created ? `<details ${r.pass ? "" : "open"}><summary>State the agent created (${r.createdObjects.length})</summary><ul class="created">${created}</ul></details>` : ""}
|
|
312
|
+
${changed ? `<details ${r.pass ? "" : "open"}><summary>State the agent changed (${(r.updatedObjects ?? []).length + (r.deletedObjects ?? []).length})</summary><ul class="created">${changed}</ul></details>` : ""}
|
|
313
|
+
<details ${r.pass ? "" : "open"}>
|
|
314
|
+
<summary>Black-box recorder — every request the agent made (${r.requests.length})</summary>
|
|
315
|
+
<div class="timeline">${timeline}</div>
|
|
316
|
+
</details>
|
|
317
|
+
</section>`;
|
|
318
|
+
}
|
|
319
|
+
export const FONT_LINKS = `<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
320
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
321
|
+
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">`;
|
|
322
|
+
export const BASE_CSS = ` :root {
|
|
323
|
+
color-scheme: dark;
|
|
324
|
+
--surface: #000000; --card: #0a0a0a;
|
|
325
|
+
--line: rgba(212, 212, 212, 0.15); --line-strong: rgba(212, 212, 212, 0.4);
|
|
326
|
+
--ink: #ffffff; --ink-2: #d4d4d4; --ink-3: #7a7f7f; --accent: #ff5a12;
|
|
327
|
+
--good: #ff5a12; --good-soft: rgba(255, 90, 18, 0.08);
|
|
328
|
+
--bad: #e5484d; --bad-soft: rgba(229, 72, 77, 0.08);
|
|
329
|
+
--warn: #d4d4d4; --warn-soft: rgba(212, 212, 212, 0.06);
|
|
330
|
+
--sans: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
|
|
331
|
+
--mono: "Space Mono", ui-monospace, monospace;
|
|
332
|
+
}
|
|
333
|
+
* { box-sizing: border-box; }
|
|
334
|
+
html { background: #000000; }
|
|
335
|
+
body {
|
|
336
|
+
margin: 0; background: #000000; color: var(--ink);
|
|
337
|
+
font-family: var(--sans); font-size: 15px; line-height: 1.6;
|
|
338
|
+
padding: 64px 16px 96px;
|
|
339
|
+
-webkit-font-smoothing: antialiased;
|
|
340
|
+
}
|
|
341
|
+
@media (min-width: 640px) { body { padding-left: 24px; padding-right: 24px; } }
|
|
342
|
+
main { max-width: 1120px; margin: 0 auto; }
|
|
343
|
+
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
344
|
+
a { color: var(--ink-2); text-decoration: underline; text-decoration-color: var(--ink-3); text-underline-offset: 3px; }
|
|
345
|
+
a:hover { color: var(--ink); text-decoration-color: var(--accent); }
|
|
346
|
+
code, pre, .req, .num, .mono { font-family: var(--mono); }
|
|
347
|
+
.num { font-variant-numeric: tabular-nums; }
|
|
348
|
+
code { font-size: 0.86em; color: var(--ink-2); background: var(--card); border: 1px solid var(--line); border-radius: 2px; padding: 0.1em 0.35em; }
|
|
349
|
+
strong { font-weight: 500; color: var(--ink); }
|
|
350
|
+
p { margin: 0.9em 0; }
|
|
351
|
+
|
|
352
|
+
/* the label vocabulary: mono, uppercase, tracked — the site's label-mono */
|
|
353
|
+
.micro, .k, th, .pill, .badge, summary, .legend, .meta-line, .contrast-label, .run-meta {
|
|
354
|
+
font-family: var(--mono); text-transform: uppercase; letter-spacing: 0.08em;
|
|
355
|
+
}
|
|
356
|
+
.micro { font-size: 12px; color: var(--ink-3); margin: 0 0 12px; }
|
|
357
|
+
|
|
358
|
+
/* the header block: the site's bordered panel with the registration marks on its left corners */
|
|
359
|
+
.band { position: relative; border: 1px solid var(--line); border-radius: 2px; padding: 24px 28px; margin-bottom: 24px; }
|
|
360
|
+
.band::before, .band::after { content: ""; position: absolute; left: -1px; width: 10px; height: 22px; background: var(--accent); }
|
|
361
|
+
.band::before { top: -1px; } .band::after { bottom: -1px; }
|
|
362
|
+
.band.tone-bad { border-color: var(--bad); }
|
|
363
|
+
.band h1 { margin: 0; font-family: var(--sans); font-weight: 500; font-size: 24px; letter-spacing: -0.02em; line-height: 1.25; }
|
|
364
|
+
@media (min-width: 640px) { .band h1 { font-size: 30px; } }
|
|
365
|
+
.band .verdict-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 20px; margin-top: 14px; }
|
|
366
|
+
.band .verdict { font-family: var(--mono); font-size: 14px; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700; }
|
|
367
|
+
.band.tone-good .verdict { color: var(--accent); } .band.tone-bad .verdict { color: var(--bad); } .band.tone-warn .verdict { color: var(--ink-2); }
|
|
368
|
+
.band .sub { font-family: var(--mono); font-size: 12px; letter-spacing: 0.02em; color: var(--ink-3); width: 100%; }
|
|
369
|
+
.band .sub code { font-size: 12px; }
|
|
370
|
+
.band .contract { margin-top: 10px; color: var(--ink-2); }
|
|
371
|
+
|
|
372
|
+
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; margin-bottom: 32px; }
|
|
373
|
+
.tile { border: 1px solid var(--line); border-radius: 2px; padding: 16px 18px; }
|
|
374
|
+
.tile .k { font-size: 10px; color: var(--ink-3); margin-bottom: 10px; }
|
|
375
|
+
.tile .v { font-family: var(--sans); font-size: 28px; font-weight: 500; letter-spacing: -0.02em; line-height: 1.1; font-variant-numeric: tabular-nums; }
|
|
376
|
+
.tile .s { font-size: 13px; line-height: 1.5; color: var(--ink-3); margin-top: 8px; }
|
|
377
|
+
.tile.bad .v { color: var(--bad); }
|
|
378
|
+
.tile.good .v { color: var(--accent); }
|
|
379
|
+
.tile.warn .v { color: var(--ink-2); }
|
|
380
|
+
|
|
381
|
+
h2 { font-family: var(--sans); font-weight: 500; font-size: 20px; letter-spacing: -0.02em; line-height: 1.25; margin: 40px 0 12px; padding-top: 24px; border-top: 1px solid var(--line); }
|
|
382
|
+
h2 .badge { vertical-align: middle; }
|
|
383
|
+
.meta-line { font-size: 11px; text-transform: none; letter-spacing: 0.02em; color: var(--ink-3); margin-bottom: 20px; line-height: 1.7; }
|
|
384
|
+
.meta-line code { font-size: 11px; }
|
|
385
|
+
|
|
386
|
+
.expects { list-style: none; padding: 0; margin: 0 0 24px; display: flex; flex-direction: column; gap: 6px; }
|
|
387
|
+
.expects li { font-size: 14px; }
|
|
388
|
+
|
|
389
|
+
table { width: 100%; border-collapse: collapse; font-size: 14px; text-align: left; }
|
|
390
|
+
th, td { text-align: left; vertical-align: top; padding: 12px 16px 12px 0; border-bottom: 1px solid var(--line); }
|
|
391
|
+
th { font-size: 10px; color: var(--ink-3); font-weight: 400; }
|
|
392
|
+
td.num, th.num { text-align: right; padding-right: 16px; }
|
|
393
|
+
td.num { font-family: var(--mono); font-size: 13px; }
|
|
394
|
+
td .s { color: var(--ink-3); font-size: 12px; }
|
|
395
|
+
|
|
396
|
+
.pill { display: inline-block; font-size: 10px; font-weight: 700; border: 1px solid; border-radius: 2px; padding: 2px 8px; line-height: 1.6; }
|
|
397
|
+
.pill-good { color: var(--accent); border-color: var(--accent); }
|
|
398
|
+
.pill-bad { color: var(--bad); border-color: var(--bad); }
|
|
399
|
+
.pill-warn { color: var(--ink-2); border-color: var(--line-strong); }
|
|
400
|
+
|
|
401
|
+
.run { border: 1px solid var(--line); border-radius: 2px; padding: 20px 24px; margin-bottom: 12px; }
|
|
402
|
+
.run-failed { border-color: var(--bad); }
|
|
403
|
+
.run-head h3 { margin: 0; font-family: var(--sans); font-weight: 500; font-size: 17px; letter-spacing: -0.02em; display: inline-flex; gap: 12px; align-items: center; }
|
|
404
|
+
.run-meta { font-size: 11px; text-transform: none; letter-spacing: 0.02em; color: var(--ink-3); margin: 6px 0 14px; }
|
|
405
|
+
|
|
406
|
+
.contrast { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 12px 0 16px; }
|
|
407
|
+
.contrast-cell { border: 1px solid var(--line); border-radius: 2px; padding: 14px 16px; }
|
|
408
|
+
.contrast-bad { border-color: var(--bad); background: var(--bad-soft); }
|
|
409
|
+
.contrast-label { font-size: 10px; color: var(--ink-3); margin-bottom: 8px; }
|
|
410
|
+
.contrast-quote { font-weight: 500; color: var(--ink); }
|
|
411
|
+
.contrast-quote code { font-size: 12px; }
|
|
412
|
+
@media (max-width: 560px) { .contrast { grid-template-columns: 1fr; } }
|
|
413
|
+
|
|
414
|
+
.expects-failed { list-style: none; padding: 10px 14px; margin: 0 0 14px; border-left: 2px solid var(--bad); background: var(--bad-soft); font-size: 14px; }
|
|
415
|
+
.expects-failed li + li { margin-top: 6px; }
|
|
416
|
+
|
|
417
|
+
details { margin-top: 10px; }
|
|
418
|
+
summary { cursor: pointer; list-style: none; font-size: 11px; color: var(--ink-3); }
|
|
419
|
+
summary::-webkit-details-marker { display: none; }
|
|
420
|
+
summary::before { content: "▸"; display: inline-block; margin-right: 8px; color: var(--accent); transition: transform 150ms; }
|
|
421
|
+
details[open] > summary::before { transform: rotate(90deg); }
|
|
422
|
+
summary:hover { color: var(--ink); }
|
|
423
|
+
.created { font-size: 14px; margin: 10px 0 0; padding-left: 20px; }
|
|
424
|
+
.created li { margin: 4px 0; }
|
|
425
|
+
|
|
426
|
+
.timeline { display: flex; flex-direction: column; gap: 2px; margin-top: 12px; overflow-x: auto; }
|
|
427
|
+
.req { display: flex; gap: 14px; align-items: baseline; font-size: 12px; padding: 6px 12px; border-radius: 2px; background: var(--card); border: 1px solid transparent; white-space: nowrap; color: var(--ink-2); }
|
|
428
|
+
.req-storm { background: var(--warn-soft); border-color: var(--line); }
|
|
429
|
+
.req-err:not(.req-storm) { background: var(--bad-soft); }
|
|
430
|
+
.req-seq { color: var(--ink-3); min-width: 40px; }
|
|
431
|
+
.req-m { font-weight: 700; min-width: 58px; color: var(--ink); }
|
|
432
|
+
.req-p { flex: 1; overflow: hidden; text-overflow: ellipsis; }
|
|
433
|
+
.req-s { font-weight: 700; }
|
|
434
|
+
.s-ok { color: var(--accent); } .s-bad { color: var(--bad); }
|
|
435
|
+
.badge { display: inline-block; font-size: 10px; border: 1px solid var(--line); border-radius: 2px; padding: 0 6px; color: var(--ink-3); line-height: 1.7; }
|
|
436
|
+
.badge-storm { border-color: var(--line-strong); color: var(--ink-2); }
|
|
437
|
+
.badge-mut { border-color: var(--line-strong); }
|
|
438
|
+
|
|
439
|
+
/* the colophon */
|
|
440
|
+
.colophon { border-top: 1px solid var(--line); margin-top: 64px; padding-top: 32px; color: var(--ink-3); font-size: 13px; }
|
|
441
|
+
.colophon-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px 24px; margin-bottom: 24px; }
|
|
442
|
+
.colophon .k { font-size: 10px; color: var(--ink-3); margin-bottom: 6px; }
|
|
443
|
+
.colophon .v { font-family: var(--sans); font-size: 15px; color: var(--ink); }
|
|
444
|
+
.colophon .hash { font-family: var(--mono); font-size: 12px; color: var(--ink-2); overflow-wrap: anywhere; margin-bottom: 20px; }
|
|
445
|
+
.cmd { margin: 0 0 24px; padding: 12px 14px; background: var(--card); border: 1px solid var(--line); border-radius: 2px; font-size: 12px; line-height: 1.6; color: var(--ink-2); white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
446
|
+
.crash .cmd { margin-bottom: 20px; }
|
|
447
|
+
.colophon-note { font-family: var(--mono); font-size: 11px; letter-spacing: 0.02em; line-height: 1.7; max-width: 80ch; margin: 0; }
|
|
448
|
+
|
|
449
|
+
/* the shift view */
|
|
450
|
+
.cliff-wrap { border: 1px solid var(--line); border-radius: 2px; padding: 16px 18px 8px; margin-bottom: 12px; }
|
|
451
|
+
.cliff { width: 100%; max-width: 640px; height: auto; display: block; }
|
|
452
|
+
.cliff-grid { stroke: var(--line); stroke-width: 1; }
|
|
453
|
+
.cliff-tick, .cliff-label { font: 11px "Space Mono", ui-monospace, monospace; fill: var(--ink-3); }
|
|
454
|
+
.cliff-label { fill: var(--ink); }
|
|
455
|
+
.cliff-line { fill: none; stroke: var(--ink); stroke-width: 2; }
|
|
456
|
+
.cliff-predicted { fill: var(--bad); stroke: none; }
|
|
457
|
+
.cliff-observed { fill: #000000; stroke: var(--ink); stroke-width: 2; }
|
|
458
|
+
.cliff-caption { color: var(--ink-3); font-size: 13px; margin: 8px 0 6px; }
|
|
459
|
+
table.classes { margin-bottom: 16px; }
|
|
460
|
+
.grid-wrap { overflow-x: auto; }
|
|
461
|
+
table.grid td, table.grid th { padding: 8px 10px 8px 0; }
|
|
462
|
+
table.grid .cell { font-family: var(--mono); font-weight: 700; font-size: 15px; text-align: center; padding-right: 10px; }
|
|
463
|
+
table.grid .cell-fail { background: var(--bad-soft); }
|
|
464
|
+
.c-ok { color: var(--accent); } .c-bad { color: var(--bad); } .c-miss { color: var(--ink-3); } .c-over { color: var(--ink-2); } .c-policy { color: var(--ink-3); }
|
|
465
|
+
.crash .worst { margin: 0 0 8px; } .crash .worlds { font-family: var(--mono); font-size: 12px; color: var(--ink-3); } .crash td .s { color: var(--ink-3); font-size: 12px; }
|
|
466
|
+
.grid-foot td { border-top: 1px solid var(--line-strong); font-weight: 500; }
|
|
467
|
+
.legend { font-size: 11px; text-transform: none; letter-spacing: 0.02em; color: var(--ink-3); margin: 8px 0 24px; }
|
|
468
|
+
.shift-detail { margin: 8px 0 16px; }
|
|
469
|
+
.ticket-row { margin: 10px 0; }
|
|
470
|
+
.ticket-head { font-size: 14px; }
|
|
471
|
+
.ticket-head .why { color: var(--ink-3); font-size: 13px; }
|
|
472
|
+
.contrast-quote.said { font-weight: 400; font-style: italic; color: var(--ink-2); }
|
|
473
|
+
.judge-mark { font-size: 10px; margin-left: 2px; color: var(--ink-2); cursor: help; }
|
|
474
|
+
table.findings td.reason { max-width: 32rem; font-size: 13px; color: var(--ink-2); }`;
|
|
475
|
+
export function renderReport(data) {
|
|
476
|
+
const passed = data.runs.filter((r) => r.pass).length;
|
|
477
|
+
const interrupted = data.interrupted ?? null;
|
|
478
|
+
const allPassed = passed === data.requestedRuns && !interrupted;
|
|
479
|
+
const requested = `${data.requestedRuns} run${data.requestedRuns === 1 ? "" : "s"}`;
|
|
480
|
+
const interruptedLine = interrupted
|
|
481
|
+
? `interrupted by ${esc(interrupted)} after ${data.runs.length} of ${requested}`
|
|
482
|
+
: "";
|
|
483
|
+
const tone = allPassed ? "good" : interrupted ? "warn" : "bad";
|
|
484
|
+
const net = netMovementLine(data.runs);
|
|
485
|
+
const anyLied = data.runs.some((r) => isGraded(r) && r.agent_exit === 0 && !r.timed_out && !r.pass);
|
|
486
|
+
const seedHash = data.tasks?.seed_hash ?? data.runs.find((r) => r.seed_hash !== undefined)?.seed_hash ?? null;
|
|
487
|
+
return `<!doctype html>
|
|
488
|
+
<html lang="en">
|
|
489
|
+
<head>
|
|
490
|
+
<meta charset="utf-8">
|
|
491
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
492
|
+
<title>worlds run report — ${esc(data.seed)}${data.scenario ? ` under ${esc(data.scenario)}` : ""}</title>
|
|
493
|
+
${FONT_LINKS}
|
|
494
|
+
<style>
|
|
495
|
+
${BASE_CSS}
|
|
496
|
+
</style>
|
|
497
|
+
</head>
|
|
498
|
+
<body>
|
|
499
|
+
<main>
|
|
500
|
+
<header class="band tone-${tone}">
|
|
501
|
+
<p class="micro">Crash test report · Worlds</p>
|
|
502
|
+
<h1>${esc(data.title)}</h1>
|
|
503
|
+
<div class="verdict-row">
|
|
504
|
+
<span class="verdict">${allPassed ? "✓ PASS" : interrupted ? "⚠ INTERRUPTED" : "✕ FAIL"}</span>
|
|
505
|
+
<div class="sub">seed <code>${esc(data.seed)}</code>${data.scenario ? ` · scenario <code>${esc(data.scenario)}</code>` : " · clear skies"} · ${esc(data.generatedAt)}${interrupted ? ` · <strong>${interruptedLine}</strong>` : ""}${anyLied ? " · <strong>the agent reported success on runs the world failed</strong>" : ""}</div>
|
|
506
|
+
</div>${data.contract
|
|
507
|
+
? `\n <div class="sub contract"><strong>${esc(data.contract.status.replace(/_/g, " "))}</strong> — ${esc(data.contract.reason)} · next: ${esc(data.contract.next_action)}</div>`
|
|
508
|
+
: ""}
|
|
509
|
+
</header>
|
|
510
|
+
|
|
511
|
+
<div class="tiles">
|
|
512
|
+
<div class="tile ${tone}"><div class="k">pass^k</div><div class="v">${passed}/${data.requestedRuns}</div><div class="s">${allPassed ? "every run did exactly the right thing" : interrupted ? interruptedLine : `needs ${data.requestedRuns}/${data.requestedRuns} — reliability is pass-every-time`}</div></div>
|
|
513
|
+
<div class="tile ${net.disagree ? "bad" : ""}"><div class="k">net money moved</div><div class="v num">${esc(net.headline)}</div><div class="s">${esc(net.sub)}</div></div>
|
|
514
|
+
<div class="tile"><div class="k">worlds used</div><div class="v">${data.runs.filter((r) => r.world_id !== null).length}</div><div class="s">fresh, identical universe per run</div></div>
|
|
515
|
+
<div class="tile"><div class="k">requests recorded</div><div class="v">${data.runs.reduce((n, r) => n + r.requests.length, 0)}</div><div class="s">every call, black-box logged</div></div>
|
|
516
|
+
${(() => {
|
|
517
|
+
const credits = new Map();
|
|
518
|
+
for (const r of data.runs)
|
|
519
|
+
for (const [c, m] of Object.entries(r.money))
|
|
520
|
+
if (m.credits_granted?.amount)
|
|
521
|
+
credits.set(c, (credits.get(c) ?? 0) + m.credits_granted.amount);
|
|
522
|
+
return credits.size > 0
|
|
523
|
+
? `<div class="tile"><div class="k">credit granted</div><div class="v num">${esc([...credits].map(([c, n]) => money(n, c)).join(" · "))}</div><div class="s">future revenue given away — not cash, still yours</div></div>`
|
|
524
|
+
: "";
|
|
525
|
+
})()}
|
|
526
|
+
</div>
|
|
527
|
+
|
|
528
|
+
${data.tasks ? crashTestPanel(data.tasks, data.runs, data.shift?.summary.currency ?? "usd") : ""}
|
|
529
|
+
${data.shift ? shiftPanel(data.shift, data.runs) : ""}
|
|
530
|
+
${data.judge ? judgePanel(data.judge, data.runs) : ""}
|
|
531
|
+
|
|
532
|
+
<h2>What "correct" was defined as</h2>
|
|
533
|
+
<ul class="expects">
|
|
534
|
+
${data.expectations.map((e) => `<li><code>${esc(e)}</code></li>`).join("\n")}
|
|
535
|
+
</ul>
|
|
536
|
+
|
|
537
|
+
<h2>Runs</h2>
|
|
538
|
+
<div class="grid-wrap">
|
|
539
|
+
<table>
|
|
540
|
+
<thead><tr><th>run</th><th>verdict</th><th>agent</th><th class="num">net money</th><th>world changes</th></tr></thead>
|
|
541
|
+
<tbody>
|
|
542
|
+
${data.runs
|
|
543
|
+
.map((r) => {
|
|
544
|
+
const changes = !isGraded(r)
|
|
545
|
+
? "—"
|
|
546
|
+
: ["created", "updated", "deleted"]
|
|
547
|
+
.flatMap((g) => Object.entries(r.summary[g]).map(([res, n]) => `${g} ${n} ${res}`))
|
|
548
|
+
.join(", ") || "nothing";
|
|
549
|
+
const netStr = !isGraded(r)
|
|
550
|
+
? "—"
|
|
551
|
+
: Object.entries(r.money)
|
|
552
|
+
.map(([c, m]) => money(m.net_movement, c))
|
|
553
|
+
.join(" · ") || "$0.00";
|
|
554
|
+
return `<tr><td>${r.run}</td><td>${verdictPill(r)}</td><td>${agentCell(r)}</td><td class="num">${esc(netStr)}</td><td>${esc(changes)}</td></tr>`;
|
|
555
|
+
})
|
|
556
|
+
.join("\n")}
|
|
557
|
+
</tbody>
|
|
558
|
+
</table>
|
|
559
|
+
</div>
|
|
560
|
+
|
|
561
|
+
<h2>Run detail</h2>
|
|
562
|
+
${data.runs.map(runSection).join("\n")}
|
|
563
|
+
|
|
564
|
+
<footer class="colophon">
|
|
565
|
+
<div class="colophon-grid">
|
|
566
|
+
<div><div class="k">Runtime</div><div class="v">sparta_worlds ${esc(data.version)}</div></div>
|
|
567
|
+
<div><div class="k">Seed</div><div class="v">${esc(data.seed)}${data.scenario ? ` · ${esc(data.scenario)}` : ""}</div></div>
|
|
568
|
+
<div><div class="k">Generated</div><div class="v">${esc(data.generatedAt)}</div></div>
|
|
569
|
+
</div>
|
|
570
|
+
<div class="k">Seed hash</div>
|
|
571
|
+
<div class="hash">${seedHash ? `sha256 ${esc(seedHash)}` : "no world recorded a seed hash"}</div>
|
|
572
|
+
<div class="k">Reproduce</div>
|
|
573
|
+
<pre class="cmd">${esc(data.reproduce)}</pre>
|
|
574
|
+
<p class="colophon-note">Generated by worlds passk. Every number above is read from the world's records, never from the agent's transcript; the same seed and the same requests give the same world, byte for byte, so the line above reruns this report. <a href="${esc(data.home)}">Worlds by Sparta</a></p>
|
|
575
|
+
</footer>
|
|
576
|
+
</main>
|
|
577
|
+
</body>
|
|
578
|
+
</html>`;
|
|
579
|
+
}
|