SFT-v4-Dataset-Viewer / index.html
HerrHruby's picture
Upload viewer
3208311 verified
Raw History Blame Contribute Delete
52.5 kB
<!doctype html>
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>SFT v4 Dataset Viewer</title>
<style>
*,*::before,*::after{box-sizing:border-box}
:root{
color-scheme:dark;
--bg:#14161b; --panel:#1b1e26; --panel2:#22262f; --panel3:#2a2f3a;
--line:#333a49; --fg:#e7e9ef; --dim:#a3aabb; --faint:#7d8595;
--accent:#7aa2f7; --good:#79dd8a; --bad:#ff8078; --warn:#f2c66a;
--info:#6fd0e2; --muted:#8f97a8; --guided:#c4a2ff; --mark:#ffd866;
--shadow:0 1px 2px rgba(0,0,0,.4);
}
:root[data-theme="light"]{
color-scheme:light;
--bg:#f6f7f9; --panel:#ffffff; --panel2:#f0f2f6; --panel3:#e4e8ef;
--line:#d3d9e3; --fg:#161a21; --dim:#4d5566; --faint:#6b7385;
--accent:#2c5fd0; --good:#1c7a3a; --bad:#c02a20; --warn:#8a6100;
--info:#116b7c; --muted:#5e6678; --guided:#6b3fc4; --mark:#ffe066;
--shadow:0 1px 2px rgba(16,24,40,.08);
}
html,body{margin:0;padding:0}
body{
background:var(--bg); color:var(--fg); font:14px/1.55 -apple-system,BlinkMacSystemFont,
"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
button,input,select{font:inherit;color:inherit}
.hidden{display:none !important}
a{color:var(--accent)}
header.top{
position:sticky; top:0; z-index:20; background:var(--panel); border-bottom:1px solid var(--line);
padding:8px 14px; display:flex; flex-direction:column; gap:8px;
}
.top-row{display:flex; flex-wrap:wrap; align-items:center; gap:8px}
.brand{font-weight:700; letter-spacing:.02em}
.brand span{color:var(--dim); font-weight:400; margin-left:8px; font-size:12px}
.spacer{flex:1 1 auto}
.ctl{
background:var(--panel2); border:1px solid var(--line); color:var(--fg);
border-radius:6px; padding:4px 9px; cursor:pointer; font-size:12px; white-space:nowrap;
}
.ctl:hover{background:var(--panel3)}
.ctl.on{background:var(--accent); border-color:var(--accent); color:#0b1220; font-weight:600}
:root[data-theme="light"] .ctl.on{color:#fff}
.ctl.tiny{padding:2px 7px; font-size:11px}
.group{display:flex; flex-wrap:wrap; align-items:center; gap:4px}
.group-label{color:var(--faint); font-size:11px; text-transform:uppercase; letter-spacing:.08em; margin-right:2px}
input[type=text],input[type=search]{
background:var(--panel2); border:1px solid var(--line); border-radius:6px; padding:4px 8px; min-width:180px;
}
.hitcount{font-size:11px; color:var(--dim); min-width:70px}
.app{display:flex; align-items:flex-start; gap:0}
.sidebar{
width:330px; flex:0 0 330px; border-right:1px solid var(--line); background:var(--panel);
position:sticky; top:0; max-height:100vh; overflow:auto; padding:10px;
}
.runstats{font-size:11px; color:var(--dim); line-height:1.7; margin-bottom:8px}
.runstats b{color:var(--fg)}
.side-h{font-size:11px; text-transform:uppercase; letter-spacing:.08em; color:var(--faint); margin:12px 0 6px}
.traj{
display:block; width:100%; text-align:left; background:var(--panel2); border:1px solid var(--line);
border-left:3px solid var(--line); border-radius:6px; padding:6px 8px; margin-bottom:6px; cursor:pointer;
}
.traj:hover{background:var(--panel3)}
.traj.sel{border-left-color:var(--accent); background:var(--panel3)}
.traj.superseded{opacity:.62; border-style:dashed}
.traj .tid{font-weight:600; font-size:12px; word-break:break-all}
.traj .row{display:flex; flex-wrap:wrap; gap:4px; margin-top:4px}
.badge{
display:inline-block; font-size:10.5px; line-height:1.5; padding:1px 6px; border-radius:999px;
border:1px solid currentColor; white-space:nowrap;
}
.b-good{color:var(--good)} .b-bad{color:var(--bad)} .b-warn{color:var(--warn)}
.b-info{color:var(--info)} .b-muted{color:var(--muted)} .b-guided{color:var(--guided)}
.b-accent{color:var(--accent)} .b-plain{color:var(--dim)}
.badge.solid{background:currentColor}
.badge.solid span{color:var(--panel); font-weight:700}
main{flex:1 1 auto; min-width:0; padding:12px 16px 120px}
.card{
background:var(--panel); border:1px solid var(--line); border-radius:8px;
margin:0 0 10px; box-shadow:var(--shadow); overflow:hidden;
}
.card>.card-h{
display:flex; flex-wrap:wrap; align-items:center; gap:6px; padding:7px 10px;
background:var(--panel2); border-bottom:1px solid var(--line); font-weight:600; font-size:13px;
}
.card>.card-b{padding:9px 10px}
.card.nested{margin:8px 0 0; background:var(--panel2)}
.card.nested>.card-h{background:var(--panel3)}
details.card>summary{
list-style:none; cursor:pointer; display:flex; flex-wrap:wrap; align-items:center; gap:6px;
padding:7px 10px; background:var(--panel2); border-bottom:1px solid transparent; font-weight:600; font-size:13px;
}
details.card[open]>summary{border-bottom-color:var(--line)}
details.card>summary::-webkit-details-marker{display:none}
details.card>summary::before{content:"\25B8"; color:var(--faint); margin-right:2px}
details.card[open]>summary::before{content:"\25BE"}
.layer.card{border-color:var(--line)}
.layer.guided-layer{border-left:3px solid var(--guided)}
.cand{border-left:3px solid var(--line)}
.cand.guided-cand{border-left-color:var(--guided)}
.cand:target,.flash{animation:flash 1.4s ease-out}
@keyframes flash{from{background:rgba(122,162,247,.28)} to{background:transparent}}
.meta{display:flex; flex-wrap:wrap; gap:4px 14px; font-size:11.5px; color:var(--dim); margin:2px 0 6px}
.meta b{color:var(--fg); font-weight:600}
.lbl{font-size:11px; text-transform:uppercase; letter-spacing:.07em; color:var(--faint); margin:8px 0 3px}
.lbl:first-child{margin-top:0}
ul.plain{margin:2px 0 6px; padding-left:18px}
ul.plain li{margin:2px 0}
.err{
background:rgba(255,128,120,.14); border:1px solid var(--bad); color:var(--bad);
border-radius:6px; padding:5px 8px; margin:6px 0; font-size:12px; font-weight:600;
white-space:pre-wrap; word-break:break-word;
}
.err-h{font-weight:700; margin-bottom:3px}
.err-b{font-weight:600; font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
font-size:11.5px; margin-bottom:3px}
.err-f{font-weight:400; font-style:italic; opacity:.85}
.empty{color:var(--faint); font-style:italic; font-size:12px}
.cid{
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:11.5px;
background:var(--panel3); border:1px solid var(--line); border-radius:5px; padding:0 5px;
cursor:pointer; color:var(--accent);
}
.cid.plain{color:var(--dim); cursor:default}
.tb{margin:3px 0 8px}
.txt{
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
font-size:12.5px; line-height:1.62; white-space:pre-wrap; word-break:break-word;
background:var(--panel2); border:1px solid var(--line); border-radius:6px; padding:8px 10px;
overflow-x:auto; position:relative;
}
.txt.prose{font-family:inherit; font-size:13px; line-height:1.6}
.txt.clamped{max-height:16em; overflow:hidden}
.tb.clamp-on .txt.clamped::after{
content:""; position:absolute; left:0; right:0; bottom:0; height:3.2em; pointer-events:none;
background:linear-gradient(to bottom,transparent,var(--panel2));
}
.tb-tools{display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-top:3px}
.tb-note{font-size:11px; color:var(--faint)}
mark{background:var(--mark); color:#171717; border-radius:2px}
mark.here{outline:2px solid var(--accent)}
table.grid{border-collapse:collapse; width:100%; font-size:12.5px; margin:4px 0 6px}
table.grid th,table.grid td{border:1px solid var(--line); padding:4px 7px; text-align:left; vertical-align:top}
table.grid th{background:var(--panel2); font-weight:600; font-size:11px; text-transform:uppercase;
letter-spacing:.05em; color:var(--faint)}
.wrap-scroll{overflow-x:auto}
@media (max-width:900px){
/* stretch, not flex-start: in a column flex box flex-start sizes children to
max-content and the page would scroll sideways */
.app{flex-direction:column; align-items:stretch}
.sidebar{flex:none; max-height:44vh; border-right:none; border-bottom:1px solid var(--line);
position:static; width:100%}
main{padding:10px; width:100%}
.card-h,details.card>summary{word-break:break-word}
.meta span,.meta b{min-width:0; word-break:break-word}
input[type=text],input[type=search]{min-width:120px}
}
</style>
</head>
<body>
<header class="top">
<div class="top-row">
<div class="brand">SFT v4 Dataset Viewer<span id="run-name"></span></div>
<div class="spacer"></div>
<button class="ctl on" id="cot" title="Collapse everything before &lt;/think&gt;">CoT hidden</button>
<button class="ctl" id="raw" title="Open every raw provider output block">Raw hidden</button>
<button class="ctl" id="expand-all">Expand all</button>
<button class="ctl" id="collapse-layers">Collapse layers</button>
<button class="ctl" id="theme">Light theme</button>
</div>
<div class="top-row">
<span class="group-label">stages</span>
<span class="group" id="stage-chips"></span>
<button class="ctl tiny" id="stages-all">all</button>
<button class="ctl tiny" id="stages-none">none</button>
<div class="spacer"></div>
<input type="search" id="search" placeholder="search this trajectory" />
<button class="ctl tiny" id="prev">prev</button>
<button class="ctl tiny" id="next">next</button>
<button class="ctl tiny" id="only-matches">only matches</button>
<span class="hitcount" id="hits"></span>
</div>
</header>
<div class="app">
<aside class="sidebar">
<div class="runstats" id="stats"></div>
<div class="runstats" id="gen"></div>
<input type="text" id="traj-filter" placeholder="filter trajectories" style="width:100%" />
<div class="side-h">Trajectories</div>
<div id="traj-list"></div>
<div class="side-h hidden" id="filtered-h">Filtered out by F0</div>
<div id="filtered-list"></div>
</aside>
<main id="main"></main>
</div>
<script src="data/run-data.js"></script>
<script>
'use strict';
var DATA = window.__RUN_DATA__;
var THINK_OPEN = '<think>';
var THINK_CLOSE = '</think>';
var MAX_MARKS_PER_BLOCK = 300;
var LAZY_MIN = 24000; // chars: bigger blocks are only materialised on demand
var CLAMP_MIN = 700; // chars: shorter blocks need no expand control
var STAGES = [
['f0','F0'],['f1','F1'],['f2','F2'],['f3','F3'],['f4','F4'],['f5','F5'],
['answer','Final answer'],['judge','Judge'],['mr','MR target'],['term','Termination']
];
var CONTRIB_CLASS = {
new_information:'b-good', closes_gap:'b-good', correction_or_stronger:'b-good',
independent_verification:'b-info', useful_negative:'b-warn', redundant:'b-muted',
no_result:'b-muted', flawed_execution:'b-bad'
};
var STATUS_CLASS = {present:'b-good', partial:'b-warn', missing:'b-bad', not_applicable:'b-muted'};
var state = {
uid:null, stages:new Set(STAGES.map(function(s){return s[0];})),
hideCot:true, showRaw:false, query:'', filterMatches:false
};
var blocks = [];
var markNodes = [];
var markPos = -1;
var explorationIndex = {}; // exploration_id -> anchor id
function $(sel){ return document.querySelector(sel); }
function el(tag, cls, text){
var node = document.createElement(tag);
if(cls) node.className = cls;
if(text !== undefined && text !== null && text !== '') node.textContent = String(text);
return node;
}
function badge(text, cls){ return el('span', 'badge ' + (cls || 'b-plain'), text); }
function num(x){ return (x === null || x === undefined) ? '—' : String(x); }
// ---------------------------------------------------------------- text blocks
function postThink(text){
var i = text.lastIndexOf(THINK_CLOSE);
if(i < 0) return null;
return text.slice(i + THINK_CLOSE.length).replace(/^\s+/, '');
}
function displayText(b){
if(b.cot && state.hideCot && !b.showCot){
var tail = postThink(b.text);
if(tail !== null) return tail;
}
return b.text;
}
function countMatches(hay, needle){
if(!needle) return 0;
var h = hay.toLowerCase(), n = needle.toLowerCase(), c = 0, i = h.indexOf(n);
while(i >= 0){ c++; i = h.indexOf(n, i + n.length); }
return c;
}
function paint(node, text, query){
node.textContent = '';
if(!query){ node.textContent = text; return 0; }
var h = text.toLowerCase(), n = query.toLowerCase();
var frag = document.createDocumentFragment(), from = 0, hits = 0, i = h.indexOf(n);
while(i >= 0 && hits < MAX_MARKS_PER_BLOCK){
if(i > from) frag.appendChild(document.createTextNode(text.slice(from, i)));
var m = el('mark'); m.textContent = text.slice(i, i + n.length); frag.appendChild(m);
from = i + n.length; hits++;
i = h.indexOf(n, from);
}
frag.appendChild(document.createTextNode(text.slice(from)));
node.appendChild(frag);
return hits;
}
function refreshBlock(b){
var text = displayText(b);
var q = state.query;
var hits = q ? countMatches(text, q) : 0;
// a match hiding below the fold (or inside an unloaded block) is useless
if(hits > 0 && b.lazy && !b.loaded){ b.loaded = true; b.autoExpanded = true; }
if(hits > 0 && !b.expanded){ b.expanded = true; b.autoExpanded = true; }
if(hits === 0 && b.autoExpanded){ b.expanded = false; b.autoExpanded = false; }
var long = text.length > CLAMP_MIN;
var clamp = long && !b.expanded;
if(b.lazy && !b.loaded){
b.node.textContent = '(' + text.length.toLocaleString() + ' characters — press Load to render)';
b.node.classList.remove('clamped');
b.wrap.classList.remove('clamp-on');
} else {
paint(b.node, text, q);
b.node.classList.toggle('clamped', clamp);
b.wrap.classList.toggle('clamp-on', clamp);
}
b.matches = hits;
if(b.expandBtn){
b.expandBtn.classList.toggle('hidden', !(long || b.lazy));
b.expandBtn.textContent = (b.lazy && !b.loaded)
? 'Load (' + text.length.toLocaleString() + ' chars)'
: (b.expanded ? 'Collapse' : 'Expand (' + text.length.toLocaleString() + ' chars)');
}
if(b.cotBtn){
var tail = postThink(b.text);
var hidden = b.text.length - (tail === null ? b.text.length : tail.length);
var isHiding = state.hideCot && !b.showCot && tail !== null;
b.cotBtn.textContent = isHiding ? 'show CoT (' + hidden.toLocaleString() + ' chars)' : 'hide CoT';
b.cotBtn.classList.toggle('on', !isHiding);
b.cotBtn.classList.toggle('hidden', tail === null && !(b.text.indexOf(THINK_OPEN) >= 0));
}
if(b.note) b.note.textContent = b.noteText || '';
}
function refreshAll(){
blocks.forEach(refreshBlock);
markNodes = Array.prototype.slice.call(document.querySelectorAll('#main mark'));
markPos = -1;
var total = blocks.reduce(function(a, b){ return a + b.matches; }, 0);
$('#hits').textContent = state.query ? (total + ' match' + (total === 1 ? '' : 'es')) : '';
applyMatchFilter();
}
function textBlock(parent, text, opts){
opts = opts || {};
text = (text === null || text === undefined) ? '' : String(text);
if(!text && opts.hideEmpty) return null;
var wrap = el('div', 'tb');
if(opts.label) wrap.appendChild(el('div', 'lbl', opts.label));
if(!text){
wrap.appendChild(el('div', 'empty', '(empty)'));
parent.appendChild(wrap);
return null;
}
var node = el('div', 'txt' + (opts.prose ? ' prose' : ''));
wrap.appendChild(node);
var tools = el('div', 'tb-tools');
// any block that actually carries a closed think span is CoT-toggleable,
// whether or not the caller expected reasoning in that field
var isCot = !!opts.cot || text.indexOf(THINK_CLOSE) >= 0;
var b = {
node:node, wrap:wrap, text:text, cot:isCot, expanded:!!opts.expanded,
showCot:false, lazy:text.length > LAZY_MIN, loaded:false, matches:0, autoExpanded:false,
noteText:opts.note || '', note:null, expandBtn:null, cotBtn:null
};
var expandBtn = el('button', 'ctl tiny', 'Expand');
expandBtn.addEventListener('click', function(){
if(b.lazy && !b.loaded){ b.loaded = true; b.expanded = true; }
else b.expanded = !b.expanded;
refreshBlock(b);
markNodes = Array.prototype.slice.call(document.querySelectorAll('#main mark'));
});
tools.appendChild(expandBtn);
b.expandBtn = expandBtn;
if(isCot){
var cotBtn = el('button', 'ctl tiny', 'show CoT');
cotBtn.addEventListener('click', function(){ b.showCot = !b.showCot; refreshBlock(b); });
tools.appendChild(cotBtn);
b.cotBtn = cotBtn;
}
var note = el('span', 'tb-note', opts.note || '');
tools.appendChild(note);
b.note = note;
wrap.appendChild(tools);
parent.appendChild(wrap);
blocks.push(b);
refreshBlock(b);
return b;
}
// ---------------------------------------------------------------- primitives
function card(title, stage, opts){
opts = opts || {};
var root, body;
if(opts.collapsible){
root = el('details', 'card' + (opts.cls ? ' ' + opts.cls : ''));
if(opts.open) root.open = true;
var head = el('summary');
head.appendChild(el('span', null, title));
(opts.badges || []).forEach(function(b){ head.appendChild(b); });
root.appendChild(head);
body = el('div', 'card-b');
root.appendChild(body);
} else {
root = el('div', 'card' + (opts.cls ? ' ' + opts.cls : ''));
var h = el('div', 'card-h');
h.appendChild(el('span', null, title));
(opts.badges || []).forEach(function(b){ h.appendChild(b); });
root.appendChild(h);
body = el('div', 'card-b');
root.appendChild(body);
}
if(stage) root.dataset.stage = stage;
return {root:root, body:body};
}
function metaRow(parent, pairs){
var row = el('div', 'meta');
pairs.forEach(function(p){
if(p[1] === null || p[1] === undefined || p[1] === '') return;
var span = el('span', null, p[0] + ': ');
span.appendChild(el('b', null, String(p[1])));
row.appendChild(span);
});
if(row.childNodes.length) parent.appendChild(row);
}
function parseError(parent, value, where){
if(!value) return;
// Say plainly that the pipeline failed to produce this, and that the message
// is the recorded reason. Printing a bare `parse_error: ...` under a heading
// reads as though the viewer failed to render something that exists.
var box = el('div', 'err');
box.appendChild(el('div', 'err-h',
'The pipeline produced no ' + (where || 'output') + ' here.'));
box.appendChild(el('div', 'err-b', 'Recorded reason: ' + value));
box.appendChild(el('div', 'err-f',
'This is stored run data, not a display problem \\u2014 the phase failed and ' +
'the record was kept for audit.'));
parent.appendChild(box);
}
function modelNote(meta){
if(!meta) return '';
var bits = [];
if(meta.model) bits.push(meta.model);
if(meta.attempts > 1) bits.push(meta.attempts + ' attempts');
if(meta.output_tokens) bits.push(meta.output_tokens.toLocaleString() + ' out-tok');
if(meta.reasoning_tokens) bits.push(meta.reasoning_tokens.toLocaleString() + ' reason-tok');
if(meta.latency_s) bits.push(meta.latency_s + 's');
return bits.join(' · ');
}
function bulletList(parent, label, items){
if(!items || !items.length) return;
parent.appendChild(el('div', 'lbl', label + ' (' + items.length + ')'));
var ul = el('ul', 'plain');
items.forEach(function(x){ ul.appendChild(el('li', null, x)); });
parent.appendChild(ul);
}
function anchorFor(layerIdx, cid){ return 'cand-' + layerIdx + '-' + cid; }
function cidChip(cid, layerIdx, linkable){
var chip = el('span', 'cid' + (linkable ? '' : ' plain'), cid);
if(linkable){
chip.title = 'jump to ' + cid;
chip.addEventListener('click', function(){
var target = document.getElementById(anchorFor(layerIdx, cid));
if(!target) return;
if(target.tagName === 'DETAILS') target.open = true;
var parentDetails = target.closest('details.layer');
if(parentDetails) parentDetails.open = true;
target.scrollIntoView({block:'center'});
target.classList.remove('flash');
void target.offsetWidth;
target.classList.add('flash');
});
}
return chip;
}
function explorationChip(id){
var chip = el('span', 'cid', id);
var anchor = explorationIndex[id];
if(!anchor){ chip.className = 'cid plain'; return chip; }
chip.addEventListener('click', function(){
var target = document.getElementById(anchor);
if(!target) return;
var parentDetails = target.closest('details.layer');
if(parentDetails) parentDetails.open = true;
target.scrollIntoView({block:'center'});
target.classList.remove('flash');
void target.offsetWidth;
target.classList.add('flash');
});
return chip;
}
function chipRow(parent, label, ids, layerIdx, linkable){
if(!ids || !ids.length) return;
var row = el('div', 'meta');
row.appendChild(el('span', null, label + ': '));
ids.forEach(function(id){ row.appendChild(cidChip(id, layerIdx, linkable)); });
parent.appendChild(row);
}
// ---------------------------------------------------------------- sidebar
function trajBadges(s, entry){
var out = [];
if(s.source) out.push(badge(s.source, 'b-plain'));
out.push(badge(s.terminated ? 'terminated' : 'incomplete', s.terminated ? 'b-good' : 'b-warn'));
if(s.guided) out.push(badge('guided', 'b-guided'));
if(s.minimum_layers_waived) out.push(badge('minimum waived', 'b-warn'));
out.push(badge(s.n_layers + ' layer' + (s.n_layers === 1 ? '' : 's'), 'b-info'));
if(s.normalized !== null && s.normalized !== undefined){
var cls = s.normalized >= 0.75 ? 'b-good' : (s.normalized >= 0.4 ? 'b-warn' : 'b-bad');
out.push(badge(scoreText(s), cls));
} else {
out.push(badge('no score', 'b-muted'));
}
if(s.excluded_reason) out.push(badge(s.excluded_reason, 'b-bad'));
if(s.parse_errors) out.push(badge(s.parse_errors + ' parse_error', 'b-bad'));
if(entry && entry.superseded) out.push(badge('superseded rev ' + entry.revision, 'b-muted'));
else if(entry && entry.revision_count > 1) out.push(badge('rev ' + entry.revision + '/' + entry.revision_count, 'b-accent'));
return out;
}
function scoreText(s){
if(s.score === null || s.score === undefined) return 'no score';
var raw = String(s.score) + (s.score_scale ? ' of ' + s.score_scale.split('-').pop() : '');
if(s.normalized === null || s.normalized === undefined) return raw;
return raw + ' (' + s.normalized.toFixed(2) + ')';
}
function renderSidebar(){
var list = $('#traj-list');
list.textContent = '';
var q = ($('#traj-filter').value || '').toLowerCase();
var shown = 0;
DATA.trajectories.forEach(function(entry){
var s = entry.summary;
var hay = (s.problem_id + ' ' + s.source + ' ' + s.excluded_reason + ' ' + s.trajectory_id).toLowerCase();
if(q && hay.indexOf(q) < 0) return;
shown++;
var item = el('button', 'traj' + (entry.superseded ? ' superseded' : '') + (entry.uid === state.uid ? ' sel' : ''));
item.appendChild(el('div', 'tid', s.problem_id || entry.identity));
var row = el('div', 'row');
trajBadges(s, entry).forEach(function(b){ row.appendChild(b); });
item.appendChild(row);
item.addEventListener('click', function(){ select(entry.uid); });
list.appendChild(item);
});
if(!shown) list.appendChild(el('div', 'empty', 'no trajectory matches this filter'));
var flist = $('#filtered-list');
flist.textContent = '';
$('#filtered-h').classList.toggle('hidden', !DATA.filtered.length);
DATA.filtered.forEach(function(row){
var hay = (row.problem_id + ' ' + row.source + ' ' + row.reason).toLowerCase();
if(q && hay.indexOf(q) < 0) return;
var item = el('button', 'traj' + (row.uid === state.uid ? ' sel' : ''));
item.appendChild(el('div', 'tid', row.problem_id));
var r = el('div', 'row');
if(row.source) r.appendChild(badge(row.source, 'b-plain'));
r.appendChild(badge('F0 rejected', 'b-bad'));
if(row.f0 && row.f0.validity) r.appendChild(badge(row.f0.validity, 'b-warn'));
item.appendChild(r);
item.addEventListener('click', function(){ select(row.uid); });
flist.appendChild(item);
});
}
// ---------------------------------------------------------------- sections
function renderProblem(main, p, outcome, s, entry){
var head = card('Problem · ' + (p.problem_id || '(no id)'), null, {badges:trajBadges(s, entry)});
metaRow(head.body, [
['trajectory', outcome.trajectory_id], ['version', outcome.version_id],
['final_layer', outcome.final_layer], ['terminated', outcome.terminated],
['guided', outcome.guided], ['excluded_reason', outcome.excluded_reason || '—'],
['productive_layers', outcome.productive_layers],
['minimum_layers', outcome.minimum_layers],
['minimum waived', outcome.minimum_layers_waived ? (outcome.minimum_waiver_reason || 'yes') : 'no'],
['MR targets', s.n_mr_targets], ['candidates', s.n_candidates],
['executions', s.n_executions], ['retained', s.n_retained]
]);
if(entry.superseded){
head.body.appendChild(el('div', 'err',
'This is revision ' + entry.revision + ' of ' + entry.revision_count +
' for this identity; a later line in trajectories.jsonl supersedes it.'));
}
textBlock(head.body, p.problem, {label:'Problem statement', prose:true});
textBlock(head.body, p.rubric, {label:'Rubric', prose:true});
if(p.reference_solution) textBlock(head.body, p.reference_solution, {label:'Reference solution (metadata)', prose:true});
main.appendChild(head.root);
}
function renderF0(main, f0){
if(!f0) return;
var vcls = f0.validity === 'valid' ? 'b-good' : (f0.validity === 'invalid' ? 'b-bad' : 'b-warn');
var c = card('F0 — problem validity & solution checklist', 'f0', {
collapsible:true, open:true,
badges:[badge(f0.validity || 'unknown', vcls),
badge((f0.checklist || []).length + ' checklist items', 'b-info')]
});
parseError(c.body, f0.parse_error, 'f0');
metaRow(c.body, [['model', modelNote(f0.model)]]);
bulletList(c.body, 'Validity issues', f0.validity_issues);
if((f0.checklist || []).length){
var wrap = el('div', 'wrap-scroll');
var t = el('table', 'grid');
var thead = el('tr');
['item', 'importance', 'verify?', 'obligation', 'acceptable evidence'].forEach(function(h){
thead.appendChild(el('th', null, h));
});
t.appendChild(thead);
f0.checklist.forEach(function(item){
var tr = el('tr');
var idcell = el('td');
idcell.appendChild(cidChip(item.item_id, -1, false));
tr.appendChild(idcell);
tr.appendChild(el('td', null, item.importance || '—'));
tr.appendChild(el('td', null, item.verification_needed ? 'yes' : 'no'));
tr.appendChild(el('td', null, item.obligation));
var ev = el('td');
if((item.acceptable_evidence || []).length){
var ul = el('ul', 'plain');
item.acceptable_evidence.forEach(function(x){ ul.appendChild(el('li', null, x)); });
ev.appendChild(ul);
} else ev.appendChild(el('span', 'empty', '—'));
tr.appendChild(ev);
t.appendChild(tr);
});
wrap.appendChild(t);
c.body.appendChild(wrap);
}
rawBlock(c.body, 'F0 raw output', f0.raw_output);
main.appendChild(c.root);
}
function rawBlock(parent, label, text, note){
if(!text) return;
var d = el('details', 'card nested');
var s = el('summary');
s.appendChild(el('span', null, label));
s.appendChild(badge('raw', 'b-muted'));
s.appendChild(el('span', 'tb-note', ' ' + text.length.toLocaleString() + ' chars'));
d.appendChild(s);
var body = el('div', 'card-b');
d.appendChild(body);
d.classList.add('raw-wrap');
if(state.showRaw) d.open = true;
textBlock(body, text, {cot:true, note:note || ''});
parent.appendChild(d);
}
function renderAssessment(parent, a){
if(!a) return;
bulletList(parent, 'Last-layer assessment', a.last_layer_assessment);
bulletList(parent, 'Current strategies', a.current_strategies);
bulletList(parent, 'Open questions & unknowns', a.open_questions_and_unknowns);
bulletList(parent, 'Claims needing verification', a.claims_needing_verification);
}
function chainRows(layer){
var order = [], map = {};
function touch(cid){
cid = cid || '(no id)';
if(!map[cid]){ map[cid] = {cid:cid, cand:null, assess:null, execs:[], retries:[], review:null}; order.push(cid); }
return map[cid];
}
((layer.f1 || {}).candidates || []).forEach(function(c){ touch(c.candidate_id).cand = c; });
((layer.f2 || {}).assessments || []).forEach(function(a){ touch(a.candidate_id).assess = a; });
(layer.executions || []).forEach(function(e){ touch(e.candidate_id).execs.push(e); });
(layer.retry_executions || []).forEach(function(e){
var row = touch(e.candidate_id);
var already = row.execs.some(function(x){ return x.attempt === e.attempt && x.report === e.report; });
if(!already) row.retries.push(e);
});
((layer.f4 || {}).reviews || []).forEach(function(r){ touch(r.candidate_id).review = r; });
return order.map(function(cid){ return map[cid]; });
}
function renderCandidate(parent, layer, row){
var cand = row.cand, assess = row.assess, review = row.review;
var guided = !!(cand && cand.guided) || row.execs.some(function(e){ return e.guided; });
var badges = [];
if(cand && cand.candidate_type) badges.push(badge(cand.candidate_type, 'b-accent'));
if(assess) badges.push(badge('F2 ' + assess.decision + (assess.rank >= 0 ? ' #' + assess.rank : ''),
assess.decision === 'pass' ? 'b-good' : 'b-bad'));
if(row.execs.length) badges.push(badge(row.execs.length + ' execution' + (row.execs.length === 1 ? '' : 's'), 'b-info'));
if(row.retries.length) badges.push(badge(row.retries.length + ' retry', 'b-warn'));
if(review){
badges.push(badge(review.contribution || 'no contribution', CONTRIB_CLASS[review.contribution] || 'b-muted'));
badges.push(badge(review.retain ? 'retained' : 'dropped', review.retain ? 'b-good' : 'b-muted'));
}
if(guided) badges.push(badge('guided', 'b-guided'));
var c = card('candidate ' + row.cid, null, {
collapsible:true, open:true, badges:badges,
cls:'nested cand' + (guided ? ' guided-cand' : '')
});
c.root.id = anchorFor(layer.layer_idx, row.cid);
if(cand){
var f1 = el('div');
f1.dataset.stage = 'f1';
f1.appendChild(el('div', 'lbl', 'F1 proposal'));
metaRow(f1, [['type', cand.candidate_type], ['guided', cand.guided ? 'yes' : ''],
['source_item_id', cand.source_item_id || '']]);
textBlock(f1, cand.direction, {label:'direction', prose:true});
textBlock(f1, cand.target, {label:'target', prose:true, hideEmpty:true});
textBlock(f1, cand.why_valuable, {label:'why valuable', prose:true, hideEmpty:true});
c.body.appendChild(f1);
}
if(assess){
var f2 = el('div');
f2.dataset.stage = 'f2';
f2.appendChild(el('div', 'lbl', 'F2 verdict'));
metaRow(f2, [['decision', assess.decision], ['rank', assess.rank]]);
chipRow(f2, 'overlaps with', assess.overlaps_with, layer.layer_idx, true);
textBlock(f2, assess.expected_information_value, {label:'expected information value', prose:true, hideEmpty:true});
textBlock(f2, assess.critique, {label:'critique', prose:true, hideEmpty:true});
textBlock(f2, assess.reasoning, {label:'reasoning', prose:true, hideEmpty:true});
c.body.appendChild(f2);
}
row.execs.concat(row.retries).forEach(function(e, i){
var f3 = el('div');
f3.dataset.stage = 'f3';
var isRetryOnly = i >= row.execs.length;
f3.appendChild(el('div', 'lbl',
'F3 execution' + (e.attempt ? ' · attempt ' + e.attempt : '') + (isRetryOnly ? ' (retry record)' : '')));
parseError(f3, e.parse_error, 'f3');
metaRow(f3, [['guided', e.guided ? 'yes' : ''], ['model', modelNote(e.model)]]);
textBlock(f3, e.report, {label:'report (durable target)', note:'durable'});
rawBlock(f3, 'F3 raw provider output', e.raw_output);
c.body.appendChild(f3);
});
if(review){
var f4 = el('div');
f4.dataset.stage = 'f4';
f4.appendChild(el('div', 'lbl', 'F4 review'));
parseError(f4, review.parse_error, 'f4 review');
var flags = el('div', 'meta');
// usable/faithful/retain read good when true, obvious_flaw when false;
// forward_rejection_supported is informational either way
[['usable', review.usable], ['faithful', review.faithful], ['obvious_flaw', review.obvious_flaw],
['retain', review.retain]
].forEach(function(p){
var good = p[0] === 'obvious_flaw' ? !p[1] : !!p[1];
flags.appendChild(badge(p[0] + ': ' + (p[1] ? 'yes' : 'no'), good ? 'b-good' : 'b-bad'));
});
flags.appendChild(badge('forward_rejection_supported: ' + (review.forward_rejection_supported ? 'yes' : 'no'),
review.forward_rejection_supported ? 'b-info' : 'b-muted'));
flags.appendChild(badge('contribution: ' + (review.contribution || '—'),
CONTRIB_CLASS[review.contribution] || 'b-muted'));
if(review.redundancy_cause && review.redundancy_cause !== 'not_applicable'){
flags.appendChild(badge('redundancy: ' + review.redundancy_cause, 'b-warn'));
}
f4.appendChild(flags);
textBlock(f4, review.forward_rejection_reason, {label:'forward rejection reason', prose:true, hideEmpty:true});
if(review.summary){
var sm = el('div');
sm.appendChild(el('div', 'lbl', 'frontier summary' +
(review.summary.exploration_id ? ' · exploration ' + review.summary.exploration_id : '')));
textBlock(sm, review.summary.detailed_summary, {label:'detailed summary', prose:true, hideEmpty:true});
textBlock(sm, review.summary.rationale, {label:'rationale', prose:true, hideEmpty:true});
textBlock(sm, review.summary.core_result, {label:'core result', prose:true, hideEmpty:true});
f4.appendChild(sm);
}
c.body.appendChild(f4);
}
parent.appendChild(c.root);
}
function renderF5(parent, f5, layerIdx){
if(!f5) return;
var c = card('F5 — checklist audit & termination decision', 'f5', {
collapsible:true, open:true, cls:'nested',
badges:[badge(f5.decision || 'no decision', f5.decision === 'terminate' ? 'b-good' : 'b-warn')]
.concat(f5.confirmation_used ? [badge('confirmation used', 'b-info')] : [])
.concat(f5.guided_item_id ? [badge('guided item ' + f5.guided_item_id, 'b-guided')] : [])
});
parseError(c.body, f5.parse_error, 'f5');
metaRow(c.body, [['model', modelNote(f5.model)]]);
if((f5.checklist || []).length){
var wrap = el('div', 'wrap-scroll');
var t = el('table', 'grid');
var head = el('tr');
['item', 'status', 'supporting', 'verification', 'explanation'].forEach(function(h){ head.appendChild(el('th', null, h)); });
t.appendChild(head);
f5.checklist.forEach(function(item){
var tr = el('tr');
var idc = el('td'); idc.appendChild(cidChip(item.item_id, -1, false)); tr.appendChild(idc);
var st = el('td'); st.appendChild(badge(item.status || '—', STATUS_CLASS[item.status] || 'b-muted')); tr.appendChild(st);
var sup = el('td');
(item.supporting_explorations || []).forEach(function(x){ sup.appendChild(explorationChip(x)); });
if(!(item.supporting_explorations || []).length) sup.appendChild(el('span', 'empty', '—'));
tr.appendChild(sup);
tr.appendChild(el('td', null, item.verification || '—'));
tr.appendChild(el('td', null, item.explanation || '—'));
t.appendChild(tr);
});
wrap.appendChild(t);
c.body.appendChild(wrap);
}
bulletList(c.body, 'Major unverified claims', f5.major_unverified_claims);
textBlock(c.body, f5.reason, {label:'decision reason', prose:true, hideEmpty:true});
textBlock(c.body, f5.guided_diagnostic, {label:'guided diagnostic', prose:true, hideEmpty:true});
rawBlock(c.body, 'F5 raw output', f5.raw_output);
if(f5.initial_raw_output && f5.initial_raw_output !== f5.raw_output){
rawBlock(c.body, 'F5 initial raw output (before confirmation)', f5.initial_raw_output);
}
parent.appendChild(c.root);
}
function renderMR(parent, mr){
var badges = [badge('layer ' + mr.layer_idx, 'b-info')];
if(mr.parse_error) badges.push(badge('parse_error', 'b-bad'));
var c = card('MR target — layer ' + mr.layer_idx, 'mr', {
collapsible:true, open:true, cls:'nested', badges:badges
});
parseError(c.body, mr.parse_error, 'mr target');
metaRow(c.body, [['model', modelNote(mr.model)],
['assistant_target chars', (mr.assistant_target || '').length.toLocaleString()]]);
chipRow(c.body, 'selected', mr.selected_candidate_ids, mr.layer_idx, true);
chipRow(c.body, 'rejected', mr.rejected_candidate_ids, mr.layer_idx, true);
chipRow(c.body, 'dropped after synthesis failure', mr.dropped_candidate_ids, mr.layer_idx, true);
textBlock(c.body, mr.assistant_target, {label:'assistant target (training text)', cot:true});
textBlock(c.body, mr.stocktake, {label:'stocktake', cot:true, hideEmpty:true});
textBlock(c.body, mr.deliberation, {label:'deliberation', cot:true, hideEmpty:true});
(mr.direction_fragments || []).forEach(function(f){
var d = el('div');
var lbl = el('div', 'lbl');
lbl.appendChild(document.createTextNode('fragment '));
lbl.appendChild(cidChip(f.candidate_id, mr.layer_idx, true));
lbl.appendChild(document.createTextNode(' '));
lbl.appendChild(badge(f.disposition || '—', f.disposition === 'selected' ? 'b-good' : 'b-muted'));
d.appendChild(lbl);
textBlock(d, f.continuation, {cot:true});
c.body.appendChild(d);
});
rawBlock(c.body, 'failed synthesis raw output', mr.failure_raw_output);
parent.appendChild(c.root);
}
function renderLayer(main, layer, mrByLayer){
var badges = [];
if(layer.guided) badges.push(badge('guided layer', 'b-guided'));
if(layer.productive) badges.push(badge('productive', 'b-good'));
if(layer.termination_deferred) badges.push(badge('termination deferred', 'b-warn'));
var f1 = layer.f1 || {}, f2 = layer.f2 || {}, f4 = layer.f4 || {}, f5 = layer.f5 || {};
var passes = (f2.assessments || []).filter(function(a){ return a.decision === 'pass'; }).length;
badges.push(badge((f1.candidates || []).length + ' candidates', 'b-plain'));
badges.push(badge(passes + ' pass', 'b-good'));
badges.push(badge((layer.executions || []).length + ' executions', 'b-info'));
var retained = (f4.reviews || []).filter(function(r){ return r.retain; }).length;
badges.push(badge(retained + ' retained', 'b-accent'));
if(f4.escalated) badges.push(badge('F4 escalated', 'b-warn'));
if(f5.decision) badges.push(badge('F5 ' + f5.decision, f5.decision === 'terminate' ? 'b-good' : 'b-warn'));
var errs = countErrors(layer);
if(errs) badges.push(badge(errs + ' parse_error', 'b-bad'));
var c = card('Layer ' + layer.layer_idx, null, {
collapsible:true, open:false, badges:badges,
cls:'layer' + (layer.guided ? ' guided-layer' : '')
});
textBlock(c.body, layer.search_context, {label:'search-budget context', prose:true, hideEmpty:true});
c.root.classList.add('layer');
var fb = el('div');
fb.dataset.stage = 'f1';
textBlock(fb, layer.frontier_before, {label:'frontier before this layer', hideEmpty:true});
c.body.appendChild(fb);
if(layer.f1){
var f1c = card('F1 — assessment & candidate proposals', 'f1', {
collapsible:true, open:true, cls:'nested',
badges:[badge((layer.f1.candidates || []).length + ' candidates', 'b-plain')]
});
parseError(f1c.body, layer.f1.parse_error, 'f1');
metaRow(f1c.body, [['model', modelNote(layer.f1.model)]]);
renderAssessment(f1c.body, layer.f1.assessment);
rawBlock(f1c.body, 'F1 raw output', layer.f1.raw_output);
c.body.appendChild(f1c.root);
}
if(layer.unguided_f1){
var uc = card('F1 before teacher guidance — what the model proposed unaided', 'f1', {
collapsible:true, open:false, cls:'nested',
badges:[badge('unguided', 'b-muted'),
badge((layer.unguided_f1.candidates || []).length + ' candidates', 'b-plain')]
});
var ownIds = {};
(layer.unguided_f1.candidates || []).forEach(function(x){ ownIds[x.candidate_id] = true; });
var added = ((layer.f1 || {}).candidates || [])
.filter(function(x){ return !ownIds[x.candidate_id]; })
.map(function(x){ return x.candidate_id; });
if(added.length) chipRow(uc.body, 'candidates added by guidance', added, layer.layer_idx, true);
parseError(uc.body, layer.unguided_f1.parse_error, 'unguided f1');
renderAssessment(uc.body, layer.unguided_f1.assessment);
(layer.unguided_f1.candidates || []).forEach(function(cand){
var d = el('div');
var lbl = el('div', 'lbl');
lbl.appendChild(cidChip(cand.candidate_id, -1, false));
lbl.appendChild(document.createTextNode(' '));
lbl.appendChild(badge(cand.candidate_type || '—', 'b-accent'));
d.appendChild(lbl);
textBlock(d, cand.direction, {prose:true});
uc.body.appendChild(d);
});
rawBlock(uc.body, 'unguided F1 raw output', layer.unguided_f1.raw_output);
c.body.appendChild(uc.root);
}
var chain = chainRows(layer);
if(chain.length){
var cc = card('F1 → F2 → F3 → F4 per candidate', null, {
collapsible:true, open:true, cls:'nested',
badges:[badge(chain.length + ' candidate ids', 'b-plain')]
});
chain.forEach(function(row){ renderCandidate(cc.body, layer, row); });
c.body.appendChild(cc.root);
}
if(layer.f2 && (layer.f2.raw_output || layer.f2.parse_error)){
var f2c = card('F2 — raw verdict output', 'f2', {collapsible:true, open:false, cls:'nested'});
parseError(f2c.body, layer.f2.parse_error, 'f2');
metaRow(f2c.body, [['model', modelNote(layer.f2.model)]]);
textBlock(f2c.body, layer.f2.raw_output, {cot:true, hideEmpty:true});
c.body.appendChild(f2c.root);
}
if(layer.f4){
var f4c = card('F4 — review pass output', 'f4', {
collapsible:true, open:false, cls:'nested',
badges:(layer.f4.escalated ? [badge('escalated', 'b-warn')] : [])
});
parseError(f4c.body, layer.f4.parse_error, 'f4');
metaRow(f4c.body, [['model', modelNote(layer.f4.model)]]);
rawBlock(f4c.body, 'F4 raw output', layer.f4.raw_output);
if(layer.f4.initial_raw_output && layer.f4.initial_raw_output !== layer.f4.raw_output){
rawBlock(f4c.body, 'F4 initial raw output (before escalation)', layer.f4.initial_raw_output);
}
c.body.appendChild(f4c.root);
}
renderF5(c.body, layer.f5, layer.layer_idx);
(mrByLayer[layer.layer_idx] || []).forEach(function(mr){ renderMR(c.body, mr); });
main.appendChild(c.root);
}
function countErrors(node){
var n = 0;
if(node && typeof node === 'object'){
if(Array.isArray(node)) node.forEach(function(x){ n += countErrors(x); });
else Object.keys(node).forEach(function(k){
if(k === 'parse_error'){ if(node[k]) n++; }
else n += countErrors(node[k]);
});
}
return n;
}
function renderAnswer(main, fa){
if(!fa) return;
var c = card('Final answer', 'answer', {
collapsible:true, open:true,
badges:[badge(fa.terminated ? 'terminated' : 'not terminated', fa.terminated ? 'b-good' : 'b-warn')]
});
parseError(c.body, fa.parse_error, 'final answer');
metaRow(c.body, [['model', modelNote(fa.model)]]);
textBlock(c.body, fa.answer, {label:'answer (durable)'});
textBlock(c.body, fa.frontier, {label:'frontier the answer was written from', hideEmpty:true});
rawBlock(c.body, 'final answer raw output', fa.raw_output);
main.appendChild(c.root);
}
function renderJudge(main, j){
if(!j) return;
var cls = (j.normalized === null || j.normalized === undefined) ? 'b-muted'
: (j.normalized >= 0.75 ? 'b-good' : (j.normalized >= 0.4 ? 'b-warn' : 'b-bad'));
var text = (j.score === null || j.score === undefined)
? 'no score'
: num(j.score) + (j.score_max ? '/' + j.score_max : '') +
(j.normalized === null || j.normalized === undefined ? '' : ' (' + j.normalized.toFixed(2) + ')');
var c = card('Final judge', 'judge', {
collapsible:true, open:true,
badges:[badge(text, cls), badge('scale ' + (j.score_scale || '?'), 'b-plain')]
});
parseError(c.body, j.parse_error, 'judge');
metaRow(c.body, [['raw score', num(j.score)], ['scale', j.score_scale],
['normalized', j.normalized === null || j.normalized === undefined ? '—' : j.normalized.toFixed(3)],
['model', modelNote(j.model)]]);
textBlock(c.body, j.explanation, {label:'explanation', prose:true});
rawBlock(c.body, 'judge raw output', j.raw_output);
main.appendChild(c.root);
}
function renderTermination(main, t){
if(!t) return;
var c = card('Termination target', 'term', {
collapsible:true, open:true,
badges:t.parse_error ? [badge('parse_error', 'b-bad')] : []
});
parseError(c.body, t.parse_error, 'termination target');
metaRow(c.body, [['model', modelNote(t.model)],
['assistant_target chars', (t.assistant_target || '').length.toLocaleString()]]);
textBlock(c.body, t.assistant_target, {label:'assistant target (training text)', cot:true});
textBlock(c.body, t.cot, {label:'cot', cot:true, hideEmpty:true});
textBlock(c.body, t.rationale, {label:'rationale', cot:true, hideEmpty:true});
main.appendChild(c.root);
}
function renderFiltered(main, row){
var head = card('F0-rejected problem · ' + row.problem_id, null, {
badges:[badge('filtered out', 'b-bad')].concat(row.source ? [badge(row.source, 'b-plain')] : [])
});
metaRow(head.body, [['reason', row.reason || '—'],
['validity', row.f0 ? row.f0.validity : '—']]);
textBlock(head.body, row.problem, {label:'Problem statement', prose:true});
textBlock(head.body, row.rubric, {label:'Rubric', prose:true, hideEmpty:true});
main.appendChild(head.root);
renderF0(main, row.f0);
}
// ---------------------------------------------------------------- selection
function findTrajectory(uid){
for(var i = 0; i < DATA.trajectories.length; i++){
if(DATA.trajectories[i].uid === uid) return DATA.trajectories[i];
}
return null;
}
function findFiltered(uid){
for(var i = 0; i < DATA.filtered.length; i++){
if(DATA.filtered[i].uid === uid) return DATA.filtered[i];
}
return null;
}
function select(uid){
state.uid = uid;
blocks = [];
explorationIndex = {};
var main = $('#main');
main.textContent = '';
var entry = findTrajectory(uid);
if(entry){
var d = entry.data;
(d.layers || []).forEach(function(layer){
(((layer.f4 || {}).reviews) || []).forEach(function(r){
if(r.summary && r.summary.exploration_id){
explorationIndex[r.summary.exploration_id] = anchorFor(layer.layer_idx, r.candidate_id);
}
});
});
renderProblem(main, d.problem, d.outcome, entry.summary, entry);
renderF0(main, d.f0);
var mrByLayer = {};
(d.mr_targets || []).forEach(function(mr){
(mrByLayer[mr.layer_idx] = mrByLayer[mr.layer_idx] || []).push(mr);
});
var seen = {};
(d.layers || []).forEach(function(layer){
seen[layer.layer_idx] = true;
renderLayer(main, layer, mrByLayer);
});
if(!(d.layers || []).length){
main.appendChild(el('div', 'empty',
'This trajectory has no layers' +
(d.outcome.excluded_reason ? ' (excluded_reason: ' + d.outcome.excluded_reason + ')' : '') + '.'));
}
var orphans = (d.mr_targets || []).filter(function(mr){ return !seen[mr.layer_idx]; });
if(orphans.length){
var oc = card('MR targets without a matching layer', 'mr', {collapsible:true, open:true});
orphans.forEach(function(mr){ renderMR(oc.body, mr); });
main.appendChild(oc.root);
}
renderAnswer(main, d.final_answer);
renderJudge(main, d.final_judge);
renderTermination(main, d.termination_target);
} else {
var f = findFiltered(uid);
if(f) renderFiltered(main, f);
}
applyStageFilter();
refreshAll();
renderSidebar();
window.scrollTo(0, 0);
}
// ---------------------------------------------------------------- controls
function applyStageFilter(){
document.querySelectorAll('#main [data-stage]').forEach(function(n){
n.classList.toggle('hidden', !state.stages.has(n.dataset.stage));
});
document.querySelectorAll('#main .cand').forEach(function(c){
c.classList.toggle('hidden', !c.querySelector('[data-stage]:not(.hidden)'));
});
document.querySelectorAll('#main .layer').forEach(function(l){
l.classList.toggle('hidden', !l.querySelector('[data-stage]:not(.hidden)'));
});
}
function applyMatchFilter(){
var on = state.filterMatches && state.query;
document.querySelectorAll('#main .card').forEach(function(c){
if(!on){ c.classList.remove('nomatch'); c.style.display = ''; return; }
c.style.display = c.querySelector('mark') ? '' : 'none';
});
}
function jumpMark(delta){
if(!markNodes.length) return;
if(markPos >= 0 && markNodes[markPos]) markNodes[markPos].classList.remove('here');
markPos = (markPos + delta + markNodes.length) % markNodes.length;
var m = markNodes[markPos];
var d = m.closest('details');
while(d){ d.open = true; d = d.parentElement ? d.parentElement.closest('details') : null; }
m.classList.add('here');
m.scrollIntoView({block:'center'});
}
function buildStageChips(){
var host = $('#stage-chips');
STAGES.forEach(function(s){
var b = el('button', 'ctl tiny on', s[1]);
b.addEventListener('click', function(){
if(state.stages.has(s[0])) state.stages.delete(s[0]); else state.stages.add(s[0]);
b.classList.toggle('on', state.stages.has(s[0]));
applyStageFilter();
});
b.dataset.stage = s[0];
host.appendChild(b);
});
}
function setAllStages(on){
state.stages = on ? new Set(STAGES.map(function(s){ return s[0]; })) : new Set();
document.querySelectorAll('#stage-chips .ctl').forEach(function(b){ b.classList.toggle('on', on); });
applyStageFilter();
}
function setTheme(theme){
document.documentElement.setAttribute('data-theme', theme);
$('#theme').textContent = theme === 'dark' ? 'Light theme' : 'Dark theme';
try { localStorage.setItem('mtv4-theme', theme); } catch(e) {}
}
function init(){
$('#run-name').textContent = DATA.run_name || DATA.run_dir;
var st = DATA.stats;
$('#stats').textContent =
st.trajectories + ' trajectories · ' + st.terminated + ' terminated · ' + st.guided + ' guided · ' +
st.minimum_waived + ' minimum waived · ' +
st.excluded + ' excluded · ' + st.superseded + ' superseded · ' + st.filtered + ' F0-filtered · ' +
st.parse_errors + ' parse errors · mean score ' +
(st.mean_normalized === null || st.mean_normalized === undefined ? '—' : st.mean_normalized);
$('#gen').textContent = DATA.run_dir + ' · generated ' + DATA.generated_at;
buildStageChips();
$('#stages-all').addEventListener('click', function(){ setAllStages(true); });
$('#stages-none').addEventListener('click', function(){ setAllStages(false); });
$('#cot').addEventListener('click', function(){
state.hideCot = !state.hideCot;
this.classList.toggle('on', state.hideCot);
this.textContent = state.hideCot ? 'CoT hidden' : 'CoT shown';
refreshAll();
});
$('#raw').addEventListener('click', function(){
state.showRaw = !state.showRaw;
this.classList.toggle('on', state.showRaw);
this.textContent = state.showRaw ? 'Raw shown' : 'Raw hidden';
document.querySelectorAll('#main .raw-wrap').forEach(function(d){ d.open = state.showRaw; });
});
$('#expand-all').addEventListener('click', function(){
document.querySelectorAll('#main details').forEach(function(d){ d.open = true; });
});
$('#collapse-layers').addEventListener('click', function(){
document.querySelectorAll('#main details.layer').forEach(function(d){ d.open = false; });
});
var timer = null;
$('#search').addEventListener('input', function(){
var value = this.value;
if(timer) clearTimeout(timer);
timer = setTimeout(function(){ state.query = value; refreshAll(); }, 160);
});
$('#next').addEventListener('click', function(){ jumpMark(1); });
$('#prev').addEventListener('click', function(){ jumpMark(-1); });
$('#only-matches').addEventListener('click', function(){
state.filterMatches = !state.filterMatches;
this.classList.toggle('on', state.filterMatches);
applyMatchFilter();
});
$('#traj-filter').addEventListener('input', renderSidebar);
$('#theme').addEventListener('click', function(){
setTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');
});
var saved = 'dark';
try { saved = localStorage.getItem('mtv4-theme') || 'dark'; } catch(e) {}
setTheme(saved);
renderSidebar();
var first = DATA.trajectories.length ? DATA.trajectories[0].uid
: (DATA.filtered.length ? DATA.filtered[0].uid : null);
if(first) select(first);
else $('#main').appendChild(el('div', 'empty', 'This run directory contains no trajectories.'));
}
init();
</script>
</body>
</html>