HerrHruby commited on
Commit
3208311
·
verified ·
1 Parent(s): 33a8111

Upload viewer

Browse files
Files changed (1) hide show
  1. index.html +1116 -249
index.html CHANGED
@@ -1,292 +1,1159 @@
1
  <!doctype html>
2
- <html lang="en">
3
  <head>
4
  <meta charset="utf-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
  <title>SFT v4 Dataset Viewer</title>
7
- <link rel="stylesheet" href="vendor/katex.min.css" />
8
- <script src="vendor/katex.min.js"></script>
9
- <script src="vendor/marked.min.js"></script>
10
  <style>
11
- :root{
12
- --bg:#fbfbfd; --panel:#fff; --ink:#16181d; --muted:#5d6470; --line:#e3e6ec;
13
- --accent:#3b5bdb; --accent-soft:#eef2ff; --mr:#7048e8; --mr-soft:#f3f0ff;
14
- --exec:#0b7285; --exec-soft:#e7f5f8; --warn:#c92a2a; --ok:#2b8a3e;
15
- --code:#f4f5f8;
16
- }
17
- @media (prefers-color-scheme: dark){
18
- :root:not([data-theme="light"]){
19
- --bg:#101216; --panel:#171a20; --ink:#e6e8ec; --muted:#9aa2b1; --line:#272b33;
20
- --accent:#8ea2ff; --accent-soft:#1c2333; --mr:#b197fc; --mr-soft:#221c33;
21
- --exec:#66d9e8; --exec-soft:#12262b; --warn:#ff8787; --ok:#69db7c;
22
- --code:#1c2027;
23
- }
24
- }
25
- :root[data-theme="dark"]{
26
- --bg:#101216; --panel:#171a20; --ink:#e6e8ec; --muted:#9aa2b1; --line:#272b33;
27
- --accent:#8ea2ff; --accent-soft:#1c2333; --mr:#b197fc; --mr-soft:#221c33;
28
- --exec:#66d9e8; --exec-soft:#12262b; --warn:#ff8787; --ok:#69db7c;
29
- --code:#1c2027;
30
- }
31
- *{box-sizing:border-box}
32
- body{margin:0;background:var(--bg);color:var(--ink);
33
- font:15px/1.62 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
34
- header{position:sticky;top:0;z-index:20;background:var(--panel);
35
- border-bottom:1px solid var(--line);padding:12px 20px;
36
- display:flex;gap:16px;align-items:baseline;flex-wrap:wrap}
37
- header h1{margin:0;font-size:16px;letter-spacing:-.01em}
38
- header .sub{color:var(--muted);font-size:13px}
39
- header .spacer{flex:1}
40
- .layout{display:grid;grid-template-columns:290px minmax(0,1fr);gap:0;
41
- align-items:start;min-height:calc(100vh - 52px)}
42
- @media (max-width:900px){ .layout{grid-template-columns:1fr} #side{position:static;height:auto;max-height:none;border-right:0;border-bottom:1px solid var(--line)} }
43
- #side{position:sticky;top:52px;height:calc(100vh - 52px);overflow-y:auto;
44
- border-right:1px solid var(--line);background:var(--panel);padding:10px}
45
- .item{padding:9px 10px;border-radius:8px;cursor:pointer;border:1px solid transparent}
46
- .item:hover{background:var(--accent-soft)}
47
- .item.on{background:var(--accent-soft);border-color:var(--accent)}
48
- .item .pid{font:12px/1.35 ui-monospace,SFMono-Regular,Menlo,monospace;
49
- word-break:break-all;color:var(--ink)}
50
- .item .meta{margin-top:3px;display:flex;gap:6px;flex-wrap:wrap}
51
- .tag{font-size:11px;padding:1px 7px;border-radius:999px;border:1px solid var(--line);
52
- color:var(--muted);white-space:nowrap}
53
- .tag.d{background:var(--accent-soft);border-color:transparent;color:var(--accent)}
54
- .tag.s{background:var(--exec-soft);border-color:transparent;color:var(--exec)}
55
- .tag.cap{background:var(--mr-soft);border-color:transparent;color:var(--mr)}
56
- main{padding:20px;min-width:0;max-width:1080px}
57
- .card{background:var(--panel);border:1px solid var(--line);border-radius:12px;
58
- padding:16px 18px;margin-bottom:14px}
59
- h2{font-size:15px;margin:0 0 10px;letter-spacing:-.01em}
60
- h3{font-size:13px;margin:16px 0 6px;color:var(--muted);
61
- text-transform:uppercase;letter-spacing:.06em}
62
- .prose{overflow-wrap:anywhere}
63
- .prose p{margin:.5em 0}
64
- .prose pre{background:var(--code);padding:10px 12px;border-radius:8px;overflow-x:auto}
65
- .prose code{background:var(--code);padding:1px 5px;border-radius:4px;
66
- font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}
67
- .prose pre code{background:none;padding:0}
68
- .katex{font-size:1.01em}
69
- .katex-display{overflow-x:auto;overflow-y:hidden;margin:8px 0}
70
- .rawtex{color:var(--warn)}
71
- .layer{border:1px solid var(--line);border-radius:10px;margin-bottom:12px;overflow:hidden}
72
- .layer>summary{cursor:pointer;padding:10px 14px;background:var(--accent-soft);
73
- font-weight:600;font-size:14px;list-style:none;display:flex;
74
- gap:10px;align-items:center;flex-wrap:wrap}
75
- .layer>summary::-webkit-details-marker{display:none}
76
- .layer>summary::before{content:"▸";color:var(--accent);font-size:12px}
77
- .layer[open]>summary::before{content:"▾"}
78
- .layer .body{padding:12px 14px}
79
- .mr{background:var(--mr-soft);border-left:3px solid var(--mr);
80
- border-radius:0 8px 8px 0;padding:12px 14px;margin:6px 0}
81
- .mr .lab{font-size:11px;text-transform:uppercase;letter-spacing:.06em;
82
- color:var(--mr);font-weight:700;margin-bottom:6px}
83
- .ex{background:var(--exec-soft);border-left:3px solid var(--exec);
84
- border-radius:0 8px 8px 0;padding:10px 12px;margin:8px 0}
85
- .ex .lab{font-size:11px;color:var(--exec);font-weight:700;margin-bottom:4px}
86
- .dir{font-style:italic;color:var(--muted);margin-bottom:6px}
87
- .frag{display:inline-block;font-size:11px;padding:1px 7px;border-radius:999px;margin:0 4px 4px 0}
88
- .frag.sel{background:var(--ok);color:#fff}
89
- .frag.rej{background:var(--muted);color:var(--bg)}
90
- .kv{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;font-size:13px}
91
- .kv dt{color:var(--muted)}
92
- .kv dd{margin:0}
93
- details.raw>summary{cursor:pointer;color:var(--muted);font-size:13px;padding:4px 0}
94
- .note{color:var(--muted);font-size:13px}
95
- button.theme{background:none;border:1px solid var(--line);border-radius:8px;
96
- color:var(--muted);padding:4px 10px;cursor:pointer;font-size:12px}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  </style>
98
  </head>
99
  <body>
100
- <header>
101
- <h1>SFT v4 Dataset Viewer</h1>
102
- <span class="sub" id="hdr">loading…</span>
103
- <span class="spacer"></span>
104
- <button class="theme" onclick="toggleTheme()">theme</button>
105
- </header>
106
 
107
- <div class="layout">
108
- <nav id="side"></nav>
109
- <main id="main"><p class="note">Loading trajectories…</p></main>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  </div>
111
 
 
112
  <script>
113
- const DATA = "data/trajectories.json";
114
- let state = { rows: [], i: 0 };
115
-
116
- function toggleTheme(){
117
- const cur = document.documentElement.getAttribute("data-theme");
118
- const dark = cur ? cur === "dark"
119
- : window.matchMedia("(prefers-color-scheme: dark)").matches;
120
- document.documentElement.setAttribute("data-theme", dark ? "light" : "dark");
121
- }
122
- const escapeHtml = s => (s||"").replace(/[&<>"']/g, c =>
123
- ({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[c]));
124
 
125
- // Math is extracted to placeholders before markdown runs, then restored, so
126
- // marked never mangles LaTeX (and $ inside code stays untouched).
127
- const MATH = [
128
- { re: /\$\$([\s\S]+?)\$\$/g, display: true },
129
- { re: /\\\[([\s\S]+?)\\\]/g, display: true },
130
- { re: /\\\(([\s\S]+?)\\\)/g, display: false },
131
- { re: /(?<![\\$])\$(?!\$)([^\n$]+?)\$(?!\$)/g, display: false },
132
  ];
133
- function katexSpan(tex, display){
134
- if (window.katex){
135
- try { return katex.renderToString(tex, {displayMode:display, throwOnError:false, output:"html"}); }
136
- catch(e){}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  }
138
- return `<code class="rawtex">${escapeHtml(tex)}</code>`;
139
- }
140
- function md(s){
141
- if(!s) return "";
142
- const spans=[]; let text=s;
143
- for(const {re,display} of MATH){
144
- text = text.replace(re,(_m,tex)=>{ const t=`@@M${spans.length}@@`;
145
- spans.push(katexSpan(tex.trim(),display)); return t; });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  }
147
- let html;
148
- try { html = window.marked ? marked.parse(text,{breaks:true}) : "<pre>"+escapeHtml(text)+"</pre>"; }
149
- catch(e){ html = "<pre>"+escapeHtml(text)+"</pre>"; }
150
- return html.replace(/@@M(\d+)@@/g,(_m,i)=>spans[+i] ?? "");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  }
152
- function prose(t){ const d=document.createElement("div"); d.className="prose";
153
- d.innerHTML=md(t||""); return d; }
154
 
155
- // The MR target ships as "<think>…</think>" followed by <direction> tags. Split
156
- // them so the reasoning reads as prose and the commitments stand out.
157
- function splitTarget(t){
158
- const out={think:"",dirs:[]};
159
- if(!t) return out;
160
- const m = t.match(/<think>([\s\S]*?)<\/think>/);
161
- out.think = m ? m[1].trim() : t;
162
- const rest = m ? t.slice(m.index + m[0].length) : "";
163
- const re = /<direction>([\s\S]*?)<\/direction>/g; let d;
164
- while((d = re.exec(rest))) out.dirs.push(d[1].trim());
 
 
 
 
 
 
 
 
165
  return out;
166
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
- function sidebar(){
169
- const nav=document.getElementById("side"); nav.innerHTML="";
170
- state.rows.forEach((r,i)=>{
171
- const el=document.createElement("div");
172
- el.className="item"+(i===state.i?" on":"");
173
- const capped = r.outcome.excluded_reason === "guided_step_cap";
174
- el.innerHTML =
175
- `<div class="pid">${escapeHtml(r.problem_id)}</div>
176
- <div class="meta">
177
- <span class="tag d">depth ${r.outcome.productive_layers ?? "?"}</span>
178
- ${r.score!=null?`<span class="tag s">score ${r.score.toFixed(2)}</span>`:""}
179
- ${capped?`<span class="tag cap">step-cap</span>`:""}
180
- </div>`;
181
- el.onclick=()=>{ state.i=i; sidebar(); render(); window.scrollTo(0,0); };
182
- nav.appendChild(el);
183
  });
184
  }
185
 
186
- function render(){
187
- const r = state.rows[state.i];
188
- const m = document.getElementById("main");
189
- m.innerHTML="";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
- const head=document.createElement("div"); head.className="card";
192
- head.innerHTML=`<h2>${escapeHtml(r.problem_id)}</h2>
193
- <dl class="kv">
194
- <dt>source</dt><dd>${escapeHtml(r.source||"—")}</dd>
195
- <dt>run</dt><dd>${escapeHtml(r.run)}</dd>
196
- <dt>outcome</dt><dd>${escapeHtml(r.outcome.excluded_reason||"COMPLETE")}
197
- ${r.outcome.terminated?" (self-terminated)":""}</dd>
198
- <dt>layers</dt><dd>${r.outcome.productive_layers ?? "?"}</dd>
199
- <dt>judge</dt><dd>${r.score!=null?r.score.toFixed(2):"—"}</dd>
200
- </dl>`;
201
- head.appendChild(Object.assign(document.createElement("h3"),{textContent:"Problem"}));
202
- head.appendChild(prose(r.problem));
203
- if(r.rubric){
204
- const d=document.createElement("details"); d.className="raw";
205
- d.innerHTML="<summary>rubric</summary>";
206
- d.appendChild(prose(r.rubric)); head.appendChild(d);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  }
208
- m.appendChild(head);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
- r.layers.forEach(L=>{
211
- const det=document.createElement("details"); det.className="layer";
212
- if(L.layer_idx===0) det.open=true;
213
- const kept=L.reviews.filter(x=>x.retain).length;
214
- det.innerHTML=`<summary>Layer ${L.layer_idx}
215
- <span class="tag">${L.executions.length} executions</span>
216
- <span class="tag">${kept} retained</span></summary>`;
217
- const body=document.createElement("div"); body.className="body";
218
 
219
- if(L.mr_target){
220
- const t=splitTarget(L.mr_target);
221
- const box=document.createElement("div"); box.className="mr";
222
- box.innerHTML=`<div class="lab">MR reasoning — the SFT target for this layer</div>`;
223
- box.appendChild(prose(t.think));
224
- if(t.dirs.length){
225
- const h=document.createElement("h3"); h.textContent="committed directions";
226
- box.appendChild(h);
227
- t.dirs.forEach(d=>box.appendChild(prose("- "+d)));
228
- }
229
- if(L.mr_fragments.length){
230
- const f=document.createElement("div"); f.style.marginTop="8px";
231
- f.innerHTML = L.mr_fragments.map(x=>
232
- `<span class="frag ${x.disposition==="selected"?"sel":"rej"}">${escapeHtml(x.candidate_id||"?")} · ${escapeHtml(x.disposition||"")}</span>`
233
- ).join("");
234
- box.appendChild(f);
235
- }
236
- body.appendChild(box);
237
- }
238
 
239
- L.executions.forEach(e=>{
240
- const box=document.createElement("div"); box.className="ex";
241
- box.innerHTML=`<div class="lab">execution ${escapeHtml(e.candidate_id||"")}</div>
242
- <div class="dir">${escapeHtml(e.direction||"")}</div>`;
243
- const d=document.createElement("details"); d.className="raw";
244
- d.innerHTML="<summary>execution report</summary>";
245
- d.appendChild(prose(e.report)); box.appendChild(d);
246
- body.appendChild(box);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  });
 
 
 
248
 
249
- const kepts=L.reviews.filter(x=>x.retain&&x.summary);
250
- if(kepts.length){
251
- const h=document.createElement("h3"); h.textContent="retained into the frontier";
252
- body.appendChild(h);
253
- kepts.forEach(x=>body.appendChild(prose(x.summary)));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  }
255
- det.appendChild(body); m.appendChild(det);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
 
258
- if(r.termination_target){
259
- const c=document.createElement("div"); c.className="card";
260
- c.innerHTML="<h2>Termination decision</h2>";
261
- const t=splitTarget(r.termination_target);
262
- c.appendChild(prose(t.think)); m.appendChild(c);
 
 
 
 
 
263
  }
264
- if(r.final_answer){
265
- const c=document.createElement("div"); c.className="card";
266
- c.innerHTML="<h2>Final answer</h2>";
267
- c.appendChild(prose(r.final_answer));
268
- if(r.judge_explanation){
269
- const d=document.createElement("details"); d.className="raw";
270
- d.innerHTML="<summary>judge</summary>";
271
- d.appendChild(prose(r.judge_explanation)); c.appendChild(d);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  }
273
- m.appendChild(c);
 
 
 
 
 
 
 
 
 
 
 
274
  }
 
 
 
 
275
  }
276
 
277
- fetch(DATA).then(r=>r.json()).then(j=>{
278
- state.rows = j.trajectories || [];
279
- const d = state.rows.map(r=>r.outcome.productive_layers).filter(x=>x!=null);
280
- const s = state.rows.map(r=>r.score).filter(x=>x!=null);
281
- document.getElementById("hdr").textContent =
282
- `${state.rows.length} trajectories · depth ${Math.min(...d)}–${Math.max(...d)}`
283
- + (s.length?` · judge ${Math.min(...s).toFixed(1)}–${Math.max(...s).toFixed(1)}`:"")
284
- + " · MR shown is the F1-ordered (repaired) target";
285
- sidebar(); render();
286
- }).catch(e=>{
287
- document.getElementById("main").innerHTML =
288
- `<p class="note">Failed to load ${DATA}: ${escapeHtml(String(e))}</p>`;
289
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  </script>
291
  </body>
292
  </html>
 
1
  <!doctype html>
2
+ <html lang="en" data-theme="dark">
3
  <head>
4
  <meta charset="utf-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
  <title>SFT v4 Dataset Viewer</title>
 
 
 
7
  <style>
8
+ *,*::before,*::after{box-sizing:border-box}
9
+ :root{
10
+ color-scheme:dark;
11
+ --bg:#14161b; --panel:#1b1e26; --panel2:#22262f; --panel3:#2a2f3a;
12
+ --line:#333a49; --fg:#e7e9ef; --dim:#a3aabb; --faint:#7d8595;
13
+ --accent:#7aa2f7; --good:#79dd8a; --bad:#ff8078; --warn:#f2c66a;
14
+ --info:#6fd0e2; --muted:#8f97a8; --guided:#c4a2ff; --mark:#ffd866;
15
+ --shadow:0 1px 2px rgba(0,0,0,.4);
16
+ }
17
+ :root[data-theme="light"]{
18
+ color-scheme:light;
19
+ --bg:#f6f7f9; --panel:#ffffff; --panel2:#f0f2f6; --panel3:#e4e8ef;
20
+ --line:#d3d9e3; --fg:#161a21; --dim:#4d5566; --faint:#6b7385;
21
+ --accent:#2c5fd0; --good:#1c7a3a; --bad:#c02a20; --warn:#8a6100;
22
+ --info:#116b7c; --muted:#5e6678; --guided:#6b3fc4; --mark:#ffe066;
23
+ --shadow:0 1px 2px rgba(16,24,40,.08);
24
+ }
25
+ html,body{margin:0;padding:0}
26
+ body{
27
+ background:var(--bg); color:var(--fg); font:14px/1.55 -apple-system,BlinkMacSystemFont,
28
+ "Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
29
+ }
30
+ button,input,select{font:inherit;color:inherit}
31
+ .hidden{display:none !important}
32
+ a{color:var(--accent)}
33
+
34
+ header.top{
35
+ position:sticky; top:0; z-index:20; background:var(--panel); border-bottom:1px solid var(--line);
36
+ padding:8px 14px; display:flex; flex-direction:column; gap:8px;
37
+ }
38
+ .top-row{display:flex; flex-wrap:wrap; align-items:center; gap:8px}
39
+ .brand{font-weight:700; letter-spacing:.02em}
40
+ .brand span{color:var(--dim); font-weight:400; margin-left:8px; font-size:12px}
41
+ .spacer{flex:1 1 auto}
42
+ .ctl{
43
+ background:var(--panel2); border:1px solid var(--line); color:var(--fg);
44
+ border-radius:6px; padding:4px 9px; cursor:pointer; font-size:12px; white-space:nowrap;
45
+ }
46
+ .ctl:hover{background:var(--panel3)}
47
+ .ctl.on{background:var(--accent); border-color:var(--accent); color:#0b1220; font-weight:600}
48
+ :root[data-theme="light"] .ctl.on{color:#fff}
49
+ .ctl.tiny{padding:2px 7px; font-size:11px}
50
+ .group{display:flex; flex-wrap:wrap; align-items:center; gap:4px}
51
+ .group-label{color:var(--faint); font-size:11px; text-transform:uppercase; letter-spacing:.08em; margin-right:2px}
52
+ input[type=text],input[type=search]{
53
+ background:var(--panel2); border:1px solid var(--line); border-radius:6px; padding:4px 8px; min-width:180px;
54
+ }
55
+ .hitcount{font-size:11px; color:var(--dim); min-width:70px}
56
+
57
+ .app{display:flex; align-items:flex-start; gap:0}
58
+ .sidebar{
59
+ width:330px; flex:0 0 330px; border-right:1px solid var(--line); background:var(--panel);
60
+ position:sticky; top:0; max-height:100vh; overflow:auto; padding:10px;
61
+ }
62
+ .runstats{font-size:11px; color:var(--dim); line-height:1.7; margin-bottom:8px}
63
+ .runstats b{color:var(--fg)}
64
+ .side-h{font-size:11px; text-transform:uppercase; letter-spacing:.08em; color:var(--faint); margin:12px 0 6px}
65
+ .traj{
66
+ display:block; width:100%; text-align:left; background:var(--panel2); border:1px solid var(--line);
67
+ border-left:3px solid var(--line); border-radius:6px; padding:6px 8px; margin-bottom:6px; cursor:pointer;
68
+ }
69
+ .traj:hover{background:var(--panel3)}
70
+ .traj.sel{border-left-color:var(--accent); background:var(--panel3)}
71
+ .traj.superseded{opacity:.62; border-style:dashed}
72
+ .traj .tid{font-weight:600; font-size:12px; word-break:break-all}
73
+ .traj .row{display:flex; flex-wrap:wrap; gap:4px; margin-top:4px}
74
+
75
+ .badge{
76
+ display:inline-block; font-size:10.5px; line-height:1.5; padding:1px 6px; border-radius:999px;
77
+ border:1px solid currentColor; white-space:nowrap;
78
+ }
79
+ .b-good{color:var(--good)} .b-bad{color:var(--bad)} .b-warn{color:var(--warn)}
80
+ .b-info{color:var(--info)} .b-muted{color:var(--muted)} .b-guided{color:var(--guided)}
81
+ .b-accent{color:var(--accent)} .b-plain{color:var(--dim)}
82
+ .badge.solid{background:currentColor}
83
+ .badge.solid span{color:var(--panel); font-weight:700}
84
+
85
+ main{flex:1 1 auto; min-width:0; padding:12px 16px 120px}
86
+ .card{
87
+ background:var(--panel); border:1px solid var(--line); border-radius:8px;
88
+ margin:0 0 10px; box-shadow:var(--shadow); overflow:hidden;
89
+ }
90
+ .card>.card-h{
91
+ display:flex; flex-wrap:wrap; align-items:center; gap:6px; padding:7px 10px;
92
+ background:var(--panel2); border-bottom:1px solid var(--line); font-weight:600; font-size:13px;
93
+ }
94
+ .card>.card-b{padding:9px 10px}
95
+ .card.nested{margin:8px 0 0; background:var(--panel2)}
96
+ .card.nested>.card-h{background:var(--panel3)}
97
+ details.card>summary{
98
+ list-style:none; cursor:pointer; display:flex; flex-wrap:wrap; align-items:center; gap:6px;
99
+ padding:7px 10px; background:var(--panel2); border-bottom:1px solid transparent; font-weight:600; font-size:13px;
100
+ }
101
+ details.card[open]>summary{border-bottom-color:var(--line)}
102
+ details.card>summary::-webkit-details-marker{display:none}
103
+ details.card>summary::before{content:"\25B8"; color:var(--faint); margin-right:2px}
104
+ details.card[open]>summary::before{content:"\25BE"}
105
+ .layer.card{border-color:var(--line)}
106
+ .layer.guided-layer{border-left:3px solid var(--guided)}
107
+ .cand{border-left:3px solid var(--line)}
108
+ .cand.guided-cand{border-left-color:var(--guided)}
109
+ .cand:target,.flash{animation:flash 1.4s ease-out}
110
+ @keyframes flash{from{background:rgba(122,162,247,.28)} to{background:transparent}}
111
+
112
+ .meta{display:flex; flex-wrap:wrap; gap:4px 14px; font-size:11.5px; color:var(--dim); margin:2px 0 6px}
113
+ .meta b{color:var(--fg); font-weight:600}
114
+ .lbl{font-size:11px; text-transform:uppercase; letter-spacing:.07em; color:var(--faint); margin:8px 0 3px}
115
+ .lbl:first-child{margin-top:0}
116
+ ul.plain{margin:2px 0 6px; padding-left:18px}
117
+ ul.plain li{margin:2px 0}
118
+ .err{
119
+ background:rgba(255,128,120,.14); border:1px solid var(--bad); color:var(--bad);
120
+ border-radius:6px; padding:5px 8px; margin:6px 0; font-size:12px; font-weight:600;
121
+ white-space:pre-wrap; word-break:break-word;
122
+ }
123
+ .err-h{font-weight:700; margin-bottom:3px}
124
+ .err-b{font-weight:600; font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
125
+ font-size:11.5px; margin-bottom:3px}
126
+ .err-f{font-weight:400; font-style:italic; opacity:.85}
127
+ .empty{color:var(--faint); font-style:italic; font-size:12px}
128
+ .cid{
129
+ font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:11.5px;
130
+ background:var(--panel3); border:1px solid var(--line); border-radius:5px; padding:0 5px;
131
+ cursor:pointer; color:var(--accent);
132
+ }
133
+ .cid.plain{color:var(--dim); cursor:default}
134
+
135
+ .tb{margin:3px 0 8px}
136
+ .txt{
137
+ font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
138
+ font-size:12.5px; line-height:1.62; white-space:pre-wrap; word-break:break-word;
139
+ background:var(--panel2); border:1px solid var(--line); border-radius:6px; padding:8px 10px;
140
+ overflow-x:auto; position:relative;
141
+ }
142
+ .txt.prose{font-family:inherit; font-size:13px; line-height:1.6}
143
+ .txt.clamped{max-height:16em; overflow:hidden}
144
+ .tb.clamp-on .txt.clamped::after{
145
+ content:""; position:absolute; left:0; right:0; bottom:0; height:3.2em; pointer-events:none;
146
+ background:linear-gradient(to bottom,transparent,var(--panel2));
147
+ }
148
+ .tb-tools{display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-top:3px}
149
+ .tb-note{font-size:11px; color:var(--faint)}
150
+ mark{background:var(--mark); color:#171717; border-radius:2px}
151
+ mark.here{outline:2px solid var(--accent)}
152
+
153
+ table.grid{border-collapse:collapse; width:100%; font-size:12.5px; margin:4px 0 6px}
154
+ table.grid th,table.grid td{border:1px solid var(--line); padding:4px 7px; text-align:left; vertical-align:top}
155
+ table.grid th{background:var(--panel2); font-weight:600; font-size:11px; text-transform:uppercase;
156
+ letter-spacing:.05em; color:var(--faint)}
157
+ .wrap-scroll{overflow-x:auto}
158
+
159
+ @media (max-width:900px){
160
+ /* stretch, not flex-start: in a column flex box flex-start sizes children to
161
+ max-content and the page would scroll sideways */
162
+ .app{flex-direction:column; align-items:stretch}
163
+ .sidebar{flex:none; max-height:44vh; border-right:none; border-bottom:1px solid var(--line);
164
+ position:static; width:100%}
165
+ main{padding:10px; width:100%}
166
+ .card-h,details.card>summary{word-break:break-word}
167
+ .meta span,.meta b{min-width:0; word-break:break-word}
168
+ input[type=text],input[type=search]{min-width:120px}
169
+ }
170
  </style>
171
  </head>
172
  <body>
 
 
 
 
 
 
173
 
174
+ <header class="top">
175
+ <div class="top-row">
176
+ <div class="brand">SFT v4 Dataset Viewer<span id="run-name"></span></div>
177
+ <div class="spacer"></div>
178
+ <button class="ctl on" id="cot" title="Collapse everything before &lt;/think&gt;">CoT hidden</button>
179
+ <button class="ctl" id="raw" title="Open every raw provider output block">Raw hidden</button>
180
+ <button class="ctl" id="expand-all">Expand all</button>
181
+ <button class="ctl" id="collapse-layers">Collapse layers</button>
182
+ <button class="ctl" id="theme">Light theme</button>
183
+ </div>
184
+ <div class="top-row">
185
+ <span class="group-label">stages</span>
186
+ <span class="group" id="stage-chips"></span>
187
+ <button class="ctl tiny" id="stages-all">all</button>
188
+ <button class="ctl tiny" id="stages-none">none</button>
189
+ <div class="spacer"></div>
190
+ <input type="search" id="search" placeholder="search this trajectory" />
191
+ <button class="ctl tiny" id="prev">prev</button>
192
+ <button class="ctl tiny" id="next">next</button>
193
+ <button class="ctl tiny" id="only-matches">only matches</button>
194
+ <span class="hitcount" id="hits"></span>
195
+ </div>
196
+ </header>
197
+ <div class="app">
198
+ <aside class="sidebar">
199
+ <div class="runstats" id="stats"></div>
200
+ <div class="runstats" id="gen"></div>
201
+ <input type="text" id="traj-filter" placeholder="filter trajectories" style="width:100%" />
202
+ <div class="side-h">Trajectories</div>
203
+ <div id="traj-list"></div>
204
+ <div class="side-h hidden" id="filtered-h">Filtered out by F0</div>
205
+ <div id="filtered-list"></div>
206
+ </aside>
207
+ <main id="main"></main>
208
  </div>
209
 
210
+ <script src="data/run-data.js"></script>
211
  <script>
212
+ 'use strict';
213
+ var DATA = window.__RUN_DATA__;
214
+ var THINK_OPEN = '<think>';
215
+ var THINK_CLOSE = '</think>';
216
+ var MAX_MARKS_PER_BLOCK = 300;
217
+ var LAZY_MIN = 24000; // chars: bigger blocks are only materialised on demand
218
+ var CLAMP_MIN = 700; // chars: shorter blocks need no expand control
 
 
 
 
219
 
220
+ var STAGES = [
221
+ ['f0','F0'],['f1','F1'],['f2','F2'],['f3','F3'],['f4','F4'],['f5','F5'],
222
+ ['answer','Final answer'],['judge','Judge'],['mr','MR target'],['term','Termination']
 
 
 
 
223
  ];
224
+ var CONTRIB_CLASS = {
225
+ new_information:'b-good', closes_gap:'b-good', correction_or_stronger:'b-good',
226
+ independent_verification:'b-info', useful_negative:'b-warn', redundant:'b-muted',
227
+ no_result:'b-muted', flawed_execution:'b-bad'
228
+ };
229
+ var STATUS_CLASS = {present:'b-good', partial:'b-warn', missing:'b-bad', not_applicable:'b-muted'};
230
+
231
+ var state = {
232
+ uid:null, stages:new Set(STAGES.map(function(s){return s[0];})),
233
+ hideCot:true, showRaw:false, query:'', filterMatches:false
234
+ };
235
+ var blocks = [];
236
+ var markNodes = [];
237
+ var markPos = -1;
238
+ var explorationIndex = {}; // exploration_id -> anchor id
239
+
240
+ function $(sel){ return document.querySelector(sel); }
241
+ function el(tag, cls, text){
242
+ var node = document.createElement(tag);
243
+ if(cls) node.className = cls;
244
+ if(text !== undefined && text !== null && text !== '') node.textContent = String(text);
245
+ return node;
246
+ }
247
+ function badge(text, cls){ return el('span', 'badge ' + (cls || 'b-plain'), text); }
248
+ function num(x){ return (x === null || x === undefined) ? '—' : String(x); }
249
+
250
+ // ---------------------------------------------------------------- text blocks
251
+ function postThink(text){
252
+ var i = text.lastIndexOf(THINK_CLOSE);
253
+ if(i < 0) return null;
254
+ return text.slice(i + THINK_CLOSE.length).replace(/^\s+/, '');
255
+ }
256
+ function displayText(b){
257
+ if(b.cot && state.hideCot && !b.showCot){
258
+ var tail = postThink(b.text);
259
+ if(tail !== null) return tail;
260
+ }
261
+ return b.text;
262
+ }
263
+ function countMatches(hay, needle){
264
+ if(!needle) return 0;
265
+ var h = hay.toLowerCase(), n = needle.toLowerCase(), c = 0, i = h.indexOf(n);
266
+ while(i >= 0){ c++; i = h.indexOf(n, i + n.length); }
267
+ return c;
268
+ }
269
+ function paint(node, text, query){
270
+ node.textContent = '';
271
+ if(!query){ node.textContent = text; return 0; }
272
+ var h = text.toLowerCase(), n = query.toLowerCase();
273
+ var frag = document.createDocumentFragment(), from = 0, hits = 0, i = h.indexOf(n);
274
+ while(i >= 0 && hits < MAX_MARKS_PER_BLOCK){
275
+ if(i > from) frag.appendChild(document.createTextNode(text.slice(from, i)));
276
+ var m = el('mark'); m.textContent = text.slice(i, i + n.length); frag.appendChild(m);
277
+ from = i + n.length; hits++;
278
+ i = h.indexOf(n, from);
279
+ }
280
+ frag.appendChild(document.createTextNode(text.slice(from)));
281
+ node.appendChild(frag);
282
+ return hits;
283
+ }
284
+ function refreshBlock(b){
285
+ var text = displayText(b);
286
+ var q = state.query;
287
+ var hits = q ? countMatches(text, q) : 0;
288
+ // a match hiding below the fold (or inside an unloaded block) is useless
289
+ if(hits > 0 && b.lazy && !b.loaded){ b.loaded = true; b.autoExpanded = true; }
290
+ if(hits > 0 && !b.expanded){ b.expanded = true; b.autoExpanded = true; }
291
+ if(hits === 0 && b.autoExpanded){ b.expanded = false; b.autoExpanded = false; }
292
+ var long = text.length > CLAMP_MIN;
293
+ var clamp = long && !b.expanded;
294
+ if(b.lazy && !b.loaded){
295
+ b.node.textContent = '(' + text.length.toLocaleString() + ' characters — press Load to render)';
296
+ b.node.classList.remove('clamped');
297
+ b.wrap.classList.remove('clamp-on');
298
+ } else {
299
+ paint(b.node, text, q);
300
+ b.node.classList.toggle('clamped', clamp);
301
+ b.wrap.classList.toggle('clamp-on', clamp);
302
+ }
303
+ b.matches = hits;
304
+ if(b.expandBtn){
305
+ b.expandBtn.classList.toggle('hidden', !(long || b.lazy));
306
+ b.expandBtn.textContent = (b.lazy && !b.loaded)
307
+ ? 'Load (' + text.length.toLocaleString() + ' chars)'
308
+ : (b.expanded ? 'Collapse' : 'Expand (' + text.length.toLocaleString() + ' chars)');
309
+ }
310
+ if(b.cotBtn){
311
+ var tail = postThink(b.text);
312
+ var hidden = b.text.length - (tail === null ? b.text.length : tail.length);
313
+ var isHiding = state.hideCot && !b.showCot && tail !== null;
314
+ b.cotBtn.textContent = isHiding ? 'show CoT (' + hidden.toLocaleString() + ' chars)' : 'hide CoT';
315
+ b.cotBtn.classList.toggle('on', !isHiding);
316
+ b.cotBtn.classList.toggle('hidden', tail === null && !(b.text.indexOf(THINK_OPEN) >= 0));
317
+ }
318
+ if(b.note) b.note.textContent = b.noteText || '';
319
+ }
320
+ function refreshAll(){
321
+ blocks.forEach(refreshBlock);
322
+ markNodes = Array.prototype.slice.call(document.querySelectorAll('#main mark'));
323
+ markPos = -1;
324
+ var total = blocks.reduce(function(a, b){ return a + b.matches; }, 0);
325
+ $('#hits').textContent = state.query ? (total + ' match' + (total === 1 ? '' : 'es')) : '';
326
+ applyMatchFilter();
327
+ }
328
+ function textBlock(parent, text, opts){
329
+ opts = opts || {};
330
+ text = (text === null || text === undefined) ? '' : String(text);
331
+ if(!text && opts.hideEmpty) return null;
332
+ var wrap = el('div', 'tb');
333
+ if(opts.label) wrap.appendChild(el('div', 'lbl', opts.label));
334
+ if(!text){
335
+ wrap.appendChild(el('div', 'empty', '(empty)'));
336
+ parent.appendChild(wrap);
337
+ return null;
338
  }
339
+ var node = el('div', 'txt' + (opts.prose ? ' prose' : ''));
340
+ wrap.appendChild(node);
341
+ var tools = el('div', 'tb-tools');
342
+ // any block that actually carries a closed think span is CoT-toggleable,
343
+ // whether or not the caller expected reasoning in that field
344
+ var isCot = !!opts.cot || text.indexOf(THINK_CLOSE) >= 0;
345
+ var b = {
346
+ node:node, wrap:wrap, text:text, cot:isCot, expanded:!!opts.expanded,
347
+ showCot:false, lazy:text.length > LAZY_MIN, loaded:false, matches:0, autoExpanded:false,
348
+ noteText:opts.note || '', note:null, expandBtn:null, cotBtn:null
349
+ };
350
+ var expandBtn = el('button', 'ctl tiny', 'Expand');
351
+ expandBtn.addEventListener('click', function(){
352
+ if(b.lazy && !b.loaded){ b.loaded = true; b.expanded = true; }
353
+ else b.expanded = !b.expanded;
354
+ refreshBlock(b);
355
+ markNodes = Array.prototype.slice.call(document.querySelectorAll('#main mark'));
356
+ });
357
+ tools.appendChild(expandBtn);
358
+ b.expandBtn = expandBtn;
359
+ if(isCot){
360
+ var cotBtn = el('button', 'ctl tiny', 'show CoT');
361
+ cotBtn.addEventListener('click', function(){ b.showCot = !b.showCot; refreshBlock(b); });
362
+ tools.appendChild(cotBtn);
363
+ b.cotBtn = cotBtn;
364
+ }
365
+ var note = el('span', 'tb-note', opts.note || '');
366
+ tools.appendChild(note);
367
+ b.note = note;
368
+ wrap.appendChild(tools);
369
+ parent.appendChild(wrap);
370
+ blocks.push(b);
371
+ refreshBlock(b);
372
+ return b;
373
+ }
374
+
375
+ // ---------------------------------------------------------------- primitives
376
+ function card(title, stage, opts){
377
+ opts = opts || {};
378
+ var root, body;
379
+ if(opts.collapsible){
380
+ root = el('details', 'card' + (opts.cls ? ' ' + opts.cls : ''));
381
+ if(opts.open) root.open = true;
382
+ var head = el('summary');
383
+ head.appendChild(el('span', null, title));
384
+ (opts.badges || []).forEach(function(b){ head.appendChild(b); });
385
+ root.appendChild(head);
386
+ body = el('div', 'card-b');
387
+ root.appendChild(body);
388
+ } else {
389
+ root = el('div', 'card' + (opts.cls ? ' ' + opts.cls : ''));
390
+ var h = el('div', 'card-h');
391
+ h.appendChild(el('span', null, title));
392
+ (opts.badges || []).forEach(function(b){ h.appendChild(b); });
393
+ root.appendChild(h);
394
+ body = el('div', 'card-b');
395
+ root.appendChild(body);
396
+ }
397
+ if(stage) root.dataset.stage = stage;
398
+ return {root:root, body:body};
399
+ }
400
+ function metaRow(parent, pairs){
401
+ var row = el('div', 'meta');
402
+ pairs.forEach(function(p){
403
+ if(p[1] === null || p[1] === undefined || p[1] === '') return;
404
+ var span = el('span', null, p[0] + ': ');
405
+ span.appendChild(el('b', null, String(p[1])));
406
+ row.appendChild(span);
407
+ });
408
+ if(row.childNodes.length) parent.appendChild(row);
409
+ }
410
+ function parseError(parent, value, where){
411
+ if(!value) return;
412
+ // Say plainly that the pipeline failed to produce this, and that the message
413
+ // is the recorded reason. Printing a bare `parse_error: ...` under a heading
414
+ // reads as though the viewer failed to render something that exists.
415
+ var box = el('div', 'err');
416
+ box.appendChild(el('div', 'err-h',
417
+ 'The pipeline produced no ' + (where || 'output') + ' here.'));
418
+ box.appendChild(el('div', 'err-b', 'Recorded reason: ' + value));
419
+ box.appendChild(el('div', 'err-f',
420
+ 'This is stored run data, not a display problem \\u2014 the phase failed and ' +
421
+ 'the record was kept for audit.'));
422
+ parent.appendChild(box);
423
+ }
424
+ function modelNote(meta){
425
+ if(!meta) return '';
426
+ var bits = [];
427
+ if(meta.model) bits.push(meta.model);
428
+ if(meta.attempts > 1) bits.push(meta.attempts + ' attempts');
429
+ if(meta.output_tokens) bits.push(meta.output_tokens.toLocaleString() + ' out-tok');
430
+ if(meta.reasoning_tokens) bits.push(meta.reasoning_tokens.toLocaleString() + ' reason-tok');
431
+ if(meta.latency_s) bits.push(meta.latency_s + 's');
432
+ return bits.join(' · ');
433
+ }
434
+ function bulletList(parent, label, items){
435
+ if(!items || !items.length) return;
436
+ parent.appendChild(el('div', 'lbl', label + ' (' + items.length + ')'));
437
+ var ul = el('ul', 'plain');
438
+ items.forEach(function(x){ ul.appendChild(el('li', null, x)); });
439
+ parent.appendChild(ul);
440
+ }
441
+ function anchorFor(layerIdx, cid){ return 'cand-' + layerIdx + '-' + cid; }
442
+ function cidChip(cid, layerIdx, linkable){
443
+ var chip = el('span', 'cid' + (linkable ? '' : ' plain'), cid);
444
+ if(linkable){
445
+ chip.title = 'jump to ' + cid;
446
+ chip.addEventListener('click', function(){
447
+ var target = document.getElementById(anchorFor(layerIdx, cid));
448
+ if(!target) return;
449
+ if(target.tagName === 'DETAILS') target.open = true;
450
+ var parentDetails = target.closest('details.layer');
451
+ if(parentDetails) parentDetails.open = true;
452
+ target.scrollIntoView({block:'center'});
453
+ target.classList.remove('flash');
454
+ void target.offsetWidth;
455
+ target.classList.add('flash');
456
+ });
457
  }
458
+ return chip;
459
+ }
460
+ function explorationChip(id){
461
+ var chip = el('span', 'cid', id);
462
+ var anchor = explorationIndex[id];
463
+ if(!anchor){ chip.className = 'cid plain'; return chip; }
464
+ chip.addEventListener('click', function(){
465
+ var target = document.getElementById(anchor);
466
+ if(!target) return;
467
+ var parentDetails = target.closest('details.layer');
468
+ if(parentDetails) parentDetails.open = true;
469
+ target.scrollIntoView({block:'center'});
470
+ target.classList.remove('flash');
471
+ void target.offsetWidth;
472
+ target.classList.add('flash');
473
+ });
474
+ return chip;
475
+ }
476
+ function chipRow(parent, label, ids, layerIdx, linkable){
477
+ if(!ids || !ids.length) return;
478
+ var row = el('div', 'meta');
479
+ row.appendChild(el('span', null, label + ': '));
480
+ ids.forEach(function(id){ row.appendChild(cidChip(id, layerIdx, linkable)); });
481
+ parent.appendChild(row);
482
  }
 
 
483
 
484
+ // ---------------------------------------------------------------- sidebar
485
+ function trajBadges(s, entry){
486
+ var out = [];
487
+ if(s.source) out.push(badge(s.source, 'b-plain'));
488
+ out.push(badge(s.terminated ? 'terminated' : 'incomplete', s.terminated ? 'b-good' : 'b-warn'));
489
+ if(s.guided) out.push(badge('guided', 'b-guided'));
490
+ if(s.minimum_layers_waived) out.push(badge('minimum waived', 'b-warn'));
491
+ out.push(badge(s.n_layers + ' layer' + (s.n_layers === 1 ? '' : 's'), 'b-info'));
492
+ if(s.normalized !== null && s.normalized !== undefined){
493
+ var cls = s.normalized >= 0.75 ? 'b-good' : (s.normalized >= 0.4 ? 'b-warn' : 'b-bad');
494
+ out.push(badge(scoreText(s), cls));
495
+ } else {
496
+ out.push(badge('no score', 'b-muted'));
497
+ }
498
+ if(s.excluded_reason) out.push(badge(s.excluded_reason, 'b-bad'));
499
+ if(s.parse_errors) out.push(badge(s.parse_errors + ' parse_error', 'b-bad'));
500
+ if(entry && entry.superseded) out.push(badge('superseded rev ' + entry.revision, 'b-muted'));
501
+ else if(entry && entry.revision_count > 1) out.push(badge('rev ' + entry.revision + '/' + entry.revision_count, 'b-accent'));
502
  return out;
503
  }
504
+ function scoreText(s){
505
+ if(s.score === null || s.score === undefined) return 'no score';
506
+ var raw = String(s.score) + (s.score_scale ? ' of ' + s.score_scale.split('-').pop() : '');
507
+ if(s.normalized === null || s.normalized === undefined) return raw;
508
+ return raw + ' (' + s.normalized.toFixed(2) + ')';
509
+ }
510
+ function renderSidebar(){
511
+ var list = $('#traj-list');
512
+ list.textContent = '';
513
+ var q = ($('#traj-filter').value || '').toLowerCase();
514
+ var shown = 0;
515
+ DATA.trajectories.forEach(function(entry){
516
+ var s = entry.summary;
517
+ var hay = (s.problem_id + ' ' + s.source + ' ' + s.excluded_reason + ' ' + s.trajectory_id).toLowerCase();
518
+ if(q && hay.indexOf(q) < 0) return;
519
+ shown++;
520
+ var item = el('button', 'traj' + (entry.superseded ? ' superseded' : '') + (entry.uid === state.uid ? ' sel' : ''));
521
+ item.appendChild(el('div', 'tid', s.problem_id || entry.identity));
522
+ var row = el('div', 'row');
523
+ trajBadges(s, entry).forEach(function(b){ row.appendChild(b); });
524
+ item.appendChild(row);
525
+ item.addEventListener('click', function(){ select(entry.uid); });
526
+ list.appendChild(item);
527
+ });
528
+ if(!shown) list.appendChild(el('div', 'empty', 'no trajectory matches this filter'));
529
 
530
+ var flist = $('#filtered-list');
531
+ flist.textContent = '';
532
+ $('#filtered-h').classList.toggle('hidden', !DATA.filtered.length);
533
+ DATA.filtered.forEach(function(row){
534
+ var hay = (row.problem_id + ' ' + row.source + ' ' + row.reason).toLowerCase();
535
+ if(q && hay.indexOf(q) < 0) return;
536
+ var item = el('button', 'traj' + (row.uid === state.uid ? ' sel' : ''));
537
+ item.appendChild(el('div', 'tid', row.problem_id));
538
+ var r = el('div', 'row');
539
+ if(row.source) r.appendChild(badge(row.source, 'b-plain'));
540
+ r.appendChild(badge('F0 rejected', 'b-bad'));
541
+ if(row.f0 && row.f0.validity) r.appendChild(badge(row.f0.validity, 'b-warn'));
542
+ item.appendChild(r);
543
+ item.addEventListener('click', function(){ select(row.uid); });
544
+ flist.appendChild(item);
545
  });
546
  }
547
 
548
+ // ---------------------------------------------------------------- sections
549
+ function renderProblem(main, p, outcome, s, entry){
550
+ var head = card('Problem · ' + (p.problem_id || '(no id)'), null, {badges:trajBadges(s, entry)});
551
+ metaRow(head.body, [
552
+ ['trajectory', outcome.trajectory_id], ['version', outcome.version_id],
553
+ ['final_layer', outcome.final_layer], ['terminated', outcome.terminated],
554
+ ['guided', outcome.guided], ['excluded_reason', outcome.excluded_reason || '—'],
555
+ ['productive_layers', outcome.productive_layers],
556
+ ['minimum_layers', outcome.minimum_layers],
557
+ ['minimum waived', outcome.minimum_layers_waived ? (outcome.minimum_waiver_reason || 'yes') : 'no'],
558
+ ['MR targets', s.n_mr_targets], ['candidates', s.n_candidates],
559
+ ['executions', s.n_executions], ['retained', s.n_retained]
560
+ ]);
561
+ if(entry.superseded){
562
+ head.body.appendChild(el('div', 'err',
563
+ 'This is revision ' + entry.revision + ' of ' + entry.revision_count +
564
+ ' for this identity; a later line in trajectories.jsonl supersedes it.'));
565
+ }
566
+ textBlock(head.body, p.problem, {label:'Problem statement', prose:true});
567
+ textBlock(head.body, p.rubric, {label:'Rubric', prose:true});
568
+ if(p.reference_solution) textBlock(head.body, p.reference_solution, {label:'Reference solution (metadata)', prose:true});
569
+ main.appendChild(head.root);
570
+ }
571
+ function renderF0(main, f0){
572
+ if(!f0) return;
573
+ var vcls = f0.validity === 'valid' ? 'b-good' : (f0.validity === 'invalid' ? 'b-bad' : 'b-warn');
574
+ var c = card('F0 — problem validity & solution checklist', 'f0', {
575
+ collapsible:true, open:true,
576
+ badges:[badge(f0.validity || 'unknown', vcls),
577
+ badge((f0.checklist || []).length + ' checklist items', 'b-info')]
578
+ });
579
+ parseError(c.body, f0.parse_error, 'f0');
580
+ metaRow(c.body, [['model', modelNote(f0.model)]]);
581
+ bulletList(c.body, 'Validity issues', f0.validity_issues);
582
+ if((f0.checklist || []).length){
583
+ var wrap = el('div', 'wrap-scroll');
584
+ var t = el('table', 'grid');
585
+ var thead = el('tr');
586
+ ['item', 'importance', 'verify?', 'obligation', 'acceptable evidence'].forEach(function(h){
587
+ thead.appendChild(el('th', null, h));
588
+ });
589
+ t.appendChild(thead);
590
+ f0.checklist.forEach(function(item){
591
+ var tr = el('tr');
592
+ var idcell = el('td');
593
+ idcell.appendChild(cidChip(item.item_id, -1, false));
594
+ tr.appendChild(idcell);
595
+ tr.appendChild(el('td', null, item.importance || '—'));
596
+ tr.appendChild(el('td', null, item.verification_needed ? 'yes' : 'no'));
597
+ tr.appendChild(el('td', null, item.obligation));
598
+ var ev = el('td');
599
+ if((item.acceptable_evidence || []).length){
600
+ var ul = el('ul', 'plain');
601
+ item.acceptable_evidence.forEach(function(x){ ul.appendChild(el('li', null, x)); });
602
+ ev.appendChild(ul);
603
+ } else ev.appendChild(el('span', 'empty', '—'));
604
+ tr.appendChild(ev);
605
+ t.appendChild(tr);
606
+ });
607
+ wrap.appendChild(t);
608
+ c.body.appendChild(wrap);
609
+ }
610
+ rawBlock(c.body, 'F0 raw output', f0.raw_output);
611
+ main.appendChild(c.root);
612
+ }
613
+ function rawBlock(parent, label, text, note){
614
+ if(!text) return;
615
+ var d = el('details', 'card nested');
616
+ var s = el('summary');
617
+ s.appendChild(el('span', null, label));
618
+ s.appendChild(badge('raw', 'b-muted'));
619
+ s.appendChild(el('span', 'tb-note', ' ' + text.length.toLocaleString() + ' chars'));
620
+ d.appendChild(s);
621
+ var body = el('div', 'card-b');
622
+ d.appendChild(body);
623
+ d.classList.add('raw-wrap');
624
+ if(state.showRaw) d.open = true;
625
+ textBlock(body, text, {cot:true, note:note || ''});
626
+ parent.appendChild(d);
627
+ }
628
+ function renderAssessment(parent, a){
629
+ if(!a) return;
630
+ bulletList(parent, 'Last-layer assessment', a.last_layer_assessment);
631
+ bulletList(parent, 'Current strategies', a.current_strategies);
632
+ bulletList(parent, 'Open questions & unknowns', a.open_questions_and_unknowns);
633
+ bulletList(parent, 'Claims needing verification', a.claims_needing_verification);
634
+ }
635
+ function chainRows(layer){
636
+ var order = [], map = {};
637
+ function touch(cid){
638
+ cid = cid || '(no id)';
639
+ if(!map[cid]){ map[cid] = {cid:cid, cand:null, assess:null, execs:[], retries:[], review:null}; order.push(cid); }
640
+ return map[cid];
641
+ }
642
+ ((layer.f1 || {}).candidates || []).forEach(function(c){ touch(c.candidate_id).cand = c; });
643
+ ((layer.f2 || {}).assessments || []).forEach(function(a){ touch(a.candidate_id).assess = a; });
644
+ (layer.executions || []).forEach(function(e){ touch(e.candidate_id).execs.push(e); });
645
+ (layer.retry_executions || []).forEach(function(e){
646
+ var row = touch(e.candidate_id);
647
+ var already = row.execs.some(function(x){ return x.attempt === e.attempt && x.report === e.report; });
648
+ if(!already) row.retries.push(e);
649
+ });
650
+ ((layer.f4 || {}).reviews || []).forEach(function(r){ touch(r.candidate_id).review = r; });
651
+ return order.map(function(cid){ return map[cid]; });
652
+ }
653
+ function renderCandidate(parent, layer, row){
654
+ var cand = row.cand, assess = row.assess, review = row.review;
655
+ var guided = !!(cand && cand.guided) || row.execs.some(function(e){ return e.guided; });
656
+ var badges = [];
657
+ if(cand && cand.candidate_type) badges.push(badge(cand.candidate_type, 'b-accent'));
658
+ if(assess) badges.push(badge('F2 ' + assess.decision + (assess.rank >= 0 ? ' #' + assess.rank : ''),
659
+ assess.decision === 'pass' ? 'b-good' : 'b-bad'));
660
+ if(row.execs.length) badges.push(badge(row.execs.length + ' execution' + (row.execs.length === 1 ? '' : 's'), 'b-info'));
661
+ if(row.retries.length) badges.push(badge(row.retries.length + ' retry', 'b-warn'));
662
+ if(review){
663
+ badges.push(badge(review.contribution || 'no contribution', CONTRIB_CLASS[review.contribution] || 'b-muted'));
664
+ badges.push(badge(review.retain ? 'retained' : 'dropped', review.retain ? 'b-good' : 'b-muted'));
665
+ }
666
+ if(guided) badges.push(badge('guided', 'b-guided'));
667
+ var c = card('candidate ' + row.cid, null, {
668
+ collapsible:true, open:true, badges:badges,
669
+ cls:'nested cand' + (guided ? ' guided-cand' : '')
670
+ });
671
+ c.root.id = anchorFor(layer.layer_idx, row.cid);
672
 
673
+ if(cand){
674
+ var f1 = el('div');
675
+ f1.dataset.stage = 'f1';
676
+ f1.appendChild(el('div', 'lbl', 'F1 proposal'));
677
+ metaRow(f1, [['type', cand.candidate_type], ['guided', cand.guided ? 'yes' : ''],
678
+ ['source_item_id', cand.source_item_id || '']]);
679
+ textBlock(f1, cand.direction, {label:'direction', prose:true});
680
+ textBlock(f1, cand.target, {label:'target', prose:true, hideEmpty:true});
681
+ textBlock(f1, cand.why_valuable, {label:'why valuable', prose:true, hideEmpty:true});
682
+ c.body.appendChild(f1);
683
+ }
684
+ if(assess){
685
+ var f2 = el('div');
686
+ f2.dataset.stage = 'f2';
687
+ f2.appendChild(el('div', 'lbl', 'F2 verdict'));
688
+ metaRow(f2, [['decision', assess.decision], ['rank', assess.rank]]);
689
+ chipRow(f2, 'overlaps with', assess.overlaps_with, layer.layer_idx, true);
690
+ textBlock(f2, assess.expected_information_value, {label:'expected information value', prose:true, hideEmpty:true});
691
+ textBlock(f2, assess.critique, {label:'critique', prose:true, hideEmpty:true});
692
+ textBlock(f2, assess.reasoning, {label:'reasoning', prose:true, hideEmpty:true});
693
+ c.body.appendChild(f2);
694
+ }
695
+ row.execs.concat(row.retries).forEach(function(e, i){
696
+ var f3 = el('div');
697
+ f3.dataset.stage = 'f3';
698
+ var isRetryOnly = i >= row.execs.length;
699
+ f3.appendChild(el('div', 'lbl',
700
+ 'F3 execution' + (e.attempt ? ' · attempt ' + e.attempt : '') + (isRetryOnly ? ' (retry record)' : '')));
701
+ parseError(f3, e.parse_error, 'f3');
702
+ metaRow(f3, [['guided', e.guided ? 'yes' : ''], ['model', modelNote(e.model)]]);
703
+ textBlock(f3, e.report, {label:'report (durable target)', note:'durable'});
704
+ rawBlock(f3, 'F3 raw provider output', e.raw_output);
705
+ c.body.appendChild(f3);
706
+ });
707
+ if(review){
708
+ var f4 = el('div');
709
+ f4.dataset.stage = 'f4';
710
+ f4.appendChild(el('div', 'lbl', 'F4 review'));
711
+ parseError(f4, review.parse_error, 'f4 review');
712
+ var flags = el('div', 'meta');
713
+ // usable/faithful/retain read good when true, obvious_flaw when false;
714
+ // forward_rejection_supported is informational either way
715
+ [['usable', review.usable], ['faithful', review.faithful], ['obvious_flaw', review.obvious_flaw],
716
+ ['retain', review.retain]
717
+ ].forEach(function(p){
718
+ var good = p[0] === 'obvious_flaw' ? !p[1] : !!p[1];
719
+ flags.appendChild(badge(p[0] + ': ' + (p[1] ? 'yes' : 'no'), good ? 'b-good' : 'b-bad'));
720
+ });
721
+ flags.appendChild(badge('forward_rejection_supported: ' + (review.forward_rejection_supported ? 'yes' : 'no'),
722
+ review.forward_rejection_supported ? 'b-info' : 'b-muted'));
723
+ flags.appendChild(badge('contribution: ' + (review.contribution || '—'),
724
+ CONTRIB_CLASS[review.contribution] || 'b-muted'));
725
+ if(review.redundancy_cause && review.redundancy_cause !== 'not_applicable'){
726
+ flags.appendChild(badge('redundancy: ' + review.redundancy_cause, 'b-warn'));
727
+ }
728
+ f4.appendChild(flags);
729
+ textBlock(f4, review.forward_rejection_reason, {label:'forward rejection reason', prose:true, hideEmpty:true});
730
+ if(review.summary){
731
+ var sm = el('div');
732
+ sm.appendChild(el('div', 'lbl', 'frontier summary' +
733
+ (review.summary.exploration_id ? ' · exploration ' + review.summary.exploration_id : '')));
734
+ textBlock(sm, review.summary.detailed_summary, {label:'detailed summary', prose:true, hideEmpty:true});
735
+ textBlock(sm, review.summary.rationale, {label:'rationale', prose:true, hideEmpty:true});
736
+ textBlock(sm, review.summary.core_result, {label:'core result', prose:true, hideEmpty:true});
737
+ f4.appendChild(sm);
738
+ }
739
+ c.body.appendChild(f4);
740
+ }
741
+ parent.appendChild(c.root);
742
+ }
743
+ function renderF5(parent, f5, layerIdx){
744
+ if(!f5) return;
745
+ var c = card('F5 — checklist audit & termination decision', 'f5', {
746
+ collapsible:true, open:true, cls:'nested',
747
+ badges:[badge(f5.decision || 'no decision', f5.decision === 'terminate' ? 'b-good' : 'b-warn')]
748
+ .concat(f5.confirmation_used ? [badge('confirmation used', 'b-info')] : [])
749
+ .concat(f5.guided_item_id ? [badge('guided item ' + f5.guided_item_id, 'b-guided')] : [])
750
+ });
751
+ parseError(c.body, f5.parse_error, 'f5');
752
+ metaRow(c.body, [['model', modelNote(f5.model)]]);
753
+ if((f5.checklist || []).length){
754
+ var wrap = el('div', 'wrap-scroll');
755
+ var t = el('table', 'grid');
756
+ var head = el('tr');
757
+ ['item', 'status', 'supporting', 'verification', 'explanation'].forEach(function(h){ head.appendChild(el('th', null, h)); });
758
+ t.appendChild(head);
759
+ f5.checklist.forEach(function(item){
760
+ var tr = el('tr');
761
+ var idc = el('td'); idc.appendChild(cidChip(item.item_id, -1, false)); tr.appendChild(idc);
762
+ var st = el('td'); st.appendChild(badge(item.status || '—', STATUS_CLASS[item.status] || 'b-muted')); tr.appendChild(st);
763
+ var sup = el('td');
764
+ (item.supporting_explorations || []).forEach(function(x){ sup.appendChild(explorationChip(x)); });
765
+ if(!(item.supporting_explorations || []).length) sup.appendChild(el('span', 'empty', '—'));
766
+ tr.appendChild(sup);
767
+ tr.appendChild(el('td', null, item.verification || '—'));
768
+ tr.appendChild(el('td', null, item.explanation || '—'));
769
+ t.appendChild(tr);
770
+ });
771
+ wrap.appendChild(t);
772
+ c.body.appendChild(wrap);
773
+ }
774
+ bulletList(c.body, 'Major unverified claims', f5.major_unverified_claims);
775
+ textBlock(c.body, f5.reason, {label:'decision reason', prose:true, hideEmpty:true});
776
+ textBlock(c.body, f5.guided_diagnostic, {label:'guided diagnostic', prose:true, hideEmpty:true});
777
+ rawBlock(c.body, 'F5 raw output', f5.raw_output);
778
+ if(f5.initial_raw_output && f5.initial_raw_output !== f5.raw_output){
779
+ rawBlock(c.body, 'F5 initial raw output (before confirmation)', f5.initial_raw_output);
780
  }
781
+ parent.appendChild(c.root);
782
+ }
783
+ function renderMR(parent, mr){
784
+ var badges = [badge('layer ' + mr.layer_idx, 'b-info')];
785
+ if(mr.parse_error) badges.push(badge('parse_error', 'b-bad'));
786
+ var c = card('MR target — layer ' + mr.layer_idx, 'mr', {
787
+ collapsible:true, open:true, cls:'nested', badges:badges
788
+ });
789
+ parseError(c.body, mr.parse_error, 'mr target');
790
+ metaRow(c.body, [['model', modelNote(mr.model)],
791
+ ['assistant_target chars', (mr.assistant_target || '').length.toLocaleString()]]);
792
+ chipRow(c.body, 'selected', mr.selected_candidate_ids, mr.layer_idx, true);
793
+ chipRow(c.body, 'rejected', mr.rejected_candidate_ids, mr.layer_idx, true);
794
+ chipRow(c.body, 'dropped after synthesis failure', mr.dropped_candidate_ids, mr.layer_idx, true);
795
+ textBlock(c.body, mr.assistant_target, {label:'assistant target (training text)', cot:true});
796
+ textBlock(c.body, mr.stocktake, {label:'stocktake', cot:true, hideEmpty:true});
797
+ textBlock(c.body, mr.deliberation, {label:'deliberation', cot:true, hideEmpty:true});
798
+ (mr.direction_fragments || []).forEach(function(f){
799
+ var d = el('div');
800
+ var lbl = el('div', 'lbl');
801
+ lbl.appendChild(document.createTextNode('fragment '));
802
+ lbl.appendChild(cidChip(f.candidate_id, mr.layer_idx, true));
803
+ lbl.appendChild(document.createTextNode(' '));
804
+ lbl.appendChild(badge(f.disposition || '—', f.disposition === 'selected' ? 'b-good' : 'b-muted'));
805
+ d.appendChild(lbl);
806
+ textBlock(d, f.continuation, {cot:true});
807
+ c.body.appendChild(d);
808
+ });
809
+ rawBlock(c.body, 'failed synthesis raw output', mr.failure_raw_output);
810
+ parent.appendChild(c.root);
811
+ }
812
+ function renderLayer(main, layer, mrByLayer){
813
+ var badges = [];
814
+ if(layer.guided) badges.push(badge('guided layer', 'b-guided'));
815
+ if(layer.productive) badges.push(badge('productive', 'b-good'));
816
+ if(layer.termination_deferred) badges.push(badge('termination deferred', 'b-warn'));
817
+ var f1 = layer.f1 || {}, f2 = layer.f2 || {}, f4 = layer.f4 || {}, f5 = layer.f5 || {};
818
+ var passes = (f2.assessments || []).filter(function(a){ return a.decision === 'pass'; }).length;
819
+ badges.push(badge((f1.candidates || []).length + ' candidates', 'b-plain'));
820
+ badges.push(badge(passes + ' pass', 'b-good'));
821
+ badges.push(badge((layer.executions || []).length + ' executions', 'b-info'));
822
+ var retained = (f4.reviews || []).filter(function(r){ return r.retain; }).length;
823
+ badges.push(badge(retained + ' retained', 'b-accent'));
824
+ if(f4.escalated) badges.push(badge('F4 escalated', 'b-warn'));
825
+ if(f5.decision) badges.push(badge('F5 ' + f5.decision, f5.decision === 'terminate' ? 'b-good' : 'b-warn'));
826
+ var errs = countErrors(layer);
827
+ if(errs) badges.push(badge(errs + ' parse_error', 'b-bad'));
828
 
829
+ var c = card('Layer ' + layer.layer_idx, null, {
830
+ collapsible:true, open:false, badges:badges,
831
+ cls:'layer' + (layer.guided ? ' guided-layer' : '')
832
+ });
833
+ textBlock(c.body, layer.search_context, {label:'search-budget context', prose:true, hideEmpty:true});
834
+ c.root.classList.add('layer');
 
 
835
 
836
+ var fb = el('div');
837
+ fb.dataset.stage = 'f1';
838
+ textBlock(fb, layer.frontier_before, {label:'frontier before this layer', hideEmpty:true});
839
+ c.body.appendChild(fb);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
840
 
841
+ if(layer.f1){
842
+ var f1c = card('F1 — assessment & candidate proposals', 'f1', {
843
+ collapsible:true, open:true, cls:'nested',
844
+ badges:[badge((layer.f1.candidates || []).length + ' candidates', 'b-plain')]
845
+ });
846
+ parseError(f1c.body, layer.f1.parse_error, 'f1');
847
+ metaRow(f1c.body, [['model', modelNote(layer.f1.model)]]);
848
+ renderAssessment(f1c.body, layer.f1.assessment);
849
+ rawBlock(f1c.body, 'F1 raw output', layer.f1.raw_output);
850
+ c.body.appendChild(f1c.root);
851
+ }
852
+ if(layer.unguided_f1){
853
+ var uc = card('F1 before teacher guidance — what the model proposed unaided', 'f1', {
854
+ collapsible:true, open:false, cls:'nested',
855
+ badges:[badge('unguided', 'b-muted'),
856
+ badge((layer.unguided_f1.candidates || []).length + ' candidates', 'b-plain')]
857
+ });
858
+ var ownIds = {};
859
+ (layer.unguided_f1.candidates || []).forEach(function(x){ ownIds[x.candidate_id] = true; });
860
+ var added = ((layer.f1 || {}).candidates || [])
861
+ .filter(function(x){ return !ownIds[x.candidate_id]; })
862
+ .map(function(x){ return x.candidate_id; });
863
+ if(added.length) chipRow(uc.body, 'candidates added by guidance', added, layer.layer_idx, true);
864
+ parseError(uc.body, layer.unguided_f1.parse_error, 'unguided f1');
865
+ renderAssessment(uc.body, layer.unguided_f1.assessment);
866
+ (layer.unguided_f1.candidates || []).forEach(function(cand){
867
+ var d = el('div');
868
+ var lbl = el('div', 'lbl');
869
+ lbl.appendChild(cidChip(cand.candidate_id, -1, false));
870
+ lbl.appendChild(document.createTextNode(' '));
871
+ lbl.appendChild(badge(cand.candidate_type || '—', 'b-accent'));
872
+ d.appendChild(lbl);
873
+ textBlock(d, cand.direction, {prose:true});
874
+ uc.body.appendChild(d);
875
  });
876
+ rawBlock(uc.body, 'unguided F1 raw output', layer.unguided_f1.raw_output);
877
+ c.body.appendChild(uc.root);
878
+ }
879
 
880
+ var chain = chainRows(layer);
881
+ if(chain.length){
882
+ var cc = card('F1 → F2 → F3 → F4 per candidate', null, {
883
+ collapsible:true, open:true, cls:'nested',
884
+ badges:[badge(chain.length + ' candidate ids', 'b-plain')]
885
+ });
886
+ chain.forEach(function(row){ renderCandidate(cc.body, layer, row); });
887
+ c.body.appendChild(cc.root);
888
+ }
889
+ if(layer.f2 && (layer.f2.raw_output || layer.f2.parse_error)){
890
+ var f2c = card('F2 — raw verdict output', 'f2', {collapsible:true, open:false, cls:'nested'});
891
+ parseError(f2c.body, layer.f2.parse_error, 'f2');
892
+ metaRow(f2c.body, [['model', modelNote(layer.f2.model)]]);
893
+ textBlock(f2c.body, layer.f2.raw_output, {cot:true, hideEmpty:true});
894
+ c.body.appendChild(f2c.root);
895
+ }
896
+ if(layer.f4){
897
+ var f4c = card('F4 — review pass output', 'f4', {
898
+ collapsible:true, open:false, cls:'nested',
899
+ badges:(layer.f4.escalated ? [badge('escalated', 'b-warn')] : [])
900
+ });
901
+ parseError(f4c.body, layer.f4.parse_error, 'f4');
902
+ metaRow(f4c.body, [['model', modelNote(layer.f4.model)]]);
903
+ rawBlock(f4c.body, 'F4 raw output', layer.f4.raw_output);
904
+ if(layer.f4.initial_raw_output && layer.f4.initial_raw_output !== layer.f4.raw_output){
905
+ rawBlock(f4c.body, 'F4 initial raw output (before escalation)', layer.f4.initial_raw_output);
906
  }
907
+ c.body.appendChild(f4c.root);
908
+ }
909
+ renderF5(c.body, layer.f5, layer.layer_idx);
910
+ (mrByLayer[layer.layer_idx] || []).forEach(function(mr){ renderMR(c.body, mr); });
911
+ main.appendChild(c.root);
912
+ }
913
+ function countErrors(node){
914
+ var n = 0;
915
+ if(node && typeof node === 'object'){
916
+ if(Array.isArray(node)) node.forEach(function(x){ n += countErrors(x); });
917
+ else Object.keys(node).forEach(function(k){
918
+ if(k === 'parse_error'){ if(node[k]) n++; }
919
+ else n += countErrors(node[k]);
920
+ });
921
+ }
922
+ return n;
923
+ }
924
+ function renderAnswer(main, fa){
925
+ if(!fa) return;
926
+ var c = card('Final answer', 'answer', {
927
+ collapsible:true, open:true,
928
+ badges:[badge(fa.terminated ? 'terminated' : 'not terminated', fa.terminated ? 'b-good' : 'b-warn')]
929
+ });
930
+ parseError(c.body, fa.parse_error, 'final answer');
931
+ metaRow(c.body, [['model', modelNote(fa.model)]]);
932
+ textBlock(c.body, fa.answer, {label:'answer (durable)'});
933
+ textBlock(c.body, fa.frontier, {label:'frontier the answer was written from', hideEmpty:true});
934
+ rawBlock(c.body, 'final answer raw output', fa.raw_output);
935
+ main.appendChild(c.root);
936
+ }
937
+ function renderJudge(main, j){
938
+ if(!j) return;
939
+ var cls = (j.normalized === null || j.normalized === undefined) ? 'b-muted'
940
+ : (j.normalized >= 0.75 ? 'b-good' : (j.normalized >= 0.4 ? 'b-warn' : 'b-bad'));
941
+ var text = (j.score === null || j.score === undefined)
942
+ ? 'no score'
943
+ : num(j.score) + (j.score_max ? '/' + j.score_max : '') +
944
+ (j.normalized === null || j.normalized === undefined ? '' : ' (' + j.normalized.toFixed(2) + ')');
945
+ var c = card('Final judge', 'judge', {
946
+ collapsible:true, open:true,
947
+ badges:[badge(text, cls), badge('scale ' + (j.score_scale || '?'), 'b-plain')]
948
+ });
949
+ parseError(c.body, j.parse_error, 'judge');
950
+ metaRow(c.body, [['raw score', num(j.score)], ['scale', j.score_scale],
951
+ ['normalized', j.normalized === null || j.normalized === undefined ? '—' : j.normalized.toFixed(3)],
952
+ ['model', modelNote(j.model)]]);
953
+ textBlock(c.body, j.explanation, {label:'explanation', prose:true});
954
+ rawBlock(c.body, 'judge raw output', j.raw_output);
955
+ main.appendChild(c.root);
956
+ }
957
+ function renderTermination(main, t){
958
+ if(!t) return;
959
+ var c = card('Termination target', 'term', {
960
+ collapsible:true, open:true,
961
+ badges:t.parse_error ? [badge('parse_error', 'b-bad')] : []
962
  });
963
+ parseError(c.body, t.parse_error, 'termination target');
964
+ metaRow(c.body, [['model', modelNote(t.model)],
965
+ ['assistant_target chars', (t.assistant_target || '').length.toLocaleString()]]);
966
+ textBlock(c.body, t.assistant_target, {label:'assistant target (training text)', cot:true});
967
+ textBlock(c.body, t.cot, {label:'cot', cot:true, hideEmpty:true});
968
+ textBlock(c.body, t.rationale, {label:'rationale', cot:true, hideEmpty:true});
969
+ main.appendChild(c.root);
970
+ }
971
+ function renderFiltered(main, row){
972
+ var head = card('F0-rejected problem · ' + row.problem_id, null, {
973
+ badges:[badge('filtered out', 'b-bad')].concat(row.source ? [badge(row.source, 'b-plain')] : [])
974
+ });
975
+ metaRow(head.body, [['reason', row.reason || '—'],
976
+ ['validity', row.f0 ? row.f0.validity : '—']]);
977
+ textBlock(head.body, row.problem, {label:'Problem statement', prose:true});
978
+ textBlock(head.body, row.rubric, {label:'Rubric', prose:true, hideEmpty:true});
979
+ main.appendChild(head.root);
980
+ renderF0(main, row.f0);
981
+ }
982
 
983
+ // ---------------------------------------------------------------- selection
984
+ function findTrajectory(uid){
985
+ for(var i = 0; i < DATA.trajectories.length; i++){
986
+ if(DATA.trajectories[i].uid === uid) return DATA.trajectories[i];
987
+ }
988
+ return null;
989
+ }
990
+ function findFiltered(uid){
991
+ for(var i = 0; i < DATA.filtered.length; i++){
992
+ if(DATA.filtered[i].uid === uid) return DATA.filtered[i];
993
  }
994
+ return null;
995
+ }
996
+ function select(uid){
997
+ state.uid = uid;
998
+ blocks = [];
999
+ explorationIndex = {};
1000
+ var main = $('#main');
1001
+ main.textContent = '';
1002
+ var entry = findTrajectory(uid);
1003
+ if(entry){
1004
+ var d = entry.data;
1005
+ (d.layers || []).forEach(function(layer){
1006
+ (((layer.f4 || {}).reviews) || []).forEach(function(r){
1007
+ if(r.summary && r.summary.exploration_id){
1008
+ explorationIndex[r.summary.exploration_id] = anchorFor(layer.layer_idx, r.candidate_id);
1009
+ }
1010
+ });
1011
+ });
1012
+ renderProblem(main, d.problem, d.outcome, entry.summary, entry);
1013
+ renderF0(main, d.f0);
1014
+ var mrByLayer = {};
1015
+ (d.mr_targets || []).forEach(function(mr){
1016
+ (mrByLayer[mr.layer_idx] = mrByLayer[mr.layer_idx] || []).push(mr);
1017
+ });
1018
+ var seen = {};
1019
+ (d.layers || []).forEach(function(layer){
1020
+ seen[layer.layer_idx] = true;
1021
+ renderLayer(main, layer, mrByLayer);
1022
+ });
1023
+ if(!(d.layers || []).length){
1024
+ main.appendChild(el('div', 'empty',
1025
+ 'This trajectory has no layers' +
1026
+ (d.outcome.excluded_reason ? ' (excluded_reason: ' + d.outcome.excluded_reason + ')' : '') + '.'));
1027
  }
1028
+ var orphans = (d.mr_targets || []).filter(function(mr){ return !seen[mr.layer_idx]; });
1029
+ if(orphans.length){
1030
+ var oc = card('MR targets without a matching layer', 'mr', {collapsible:true, open:true});
1031
+ orphans.forEach(function(mr){ renderMR(oc.body, mr); });
1032
+ main.appendChild(oc.root);
1033
+ }
1034
+ renderAnswer(main, d.final_answer);
1035
+ renderJudge(main, d.final_judge);
1036
+ renderTermination(main, d.termination_target);
1037
+ } else {
1038
+ var f = findFiltered(uid);
1039
+ if(f) renderFiltered(main, f);
1040
  }
1041
+ applyStageFilter();
1042
+ refreshAll();
1043
+ renderSidebar();
1044
+ window.scrollTo(0, 0);
1045
  }
1046
 
1047
+ // ---------------------------------------------------------------- controls
1048
+ function applyStageFilter(){
1049
+ document.querySelectorAll('#main [data-stage]').forEach(function(n){
1050
+ n.classList.toggle('hidden', !state.stages.has(n.dataset.stage));
1051
+ });
1052
+ document.querySelectorAll('#main .cand').forEach(function(c){
1053
+ c.classList.toggle('hidden', !c.querySelector('[data-stage]:not(.hidden)'));
1054
+ });
1055
+ document.querySelectorAll('#main .layer').forEach(function(l){
1056
+ l.classList.toggle('hidden', !l.querySelector('[data-stage]:not(.hidden)'));
1057
+ });
1058
+ }
1059
+ function applyMatchFilter(){
1060
+ var on = state.filterMatches && state.query;
1061
+ document.querySelectorAll('#main .card').forEach(function(c){
1062
+ if(!on){ c.classList.remove('nomatch'); c.style.display = ''; return; }
1063
+ c.style.display = c.querySelector('mark') ? '' : 'none';
1064
+ });
1065
+ }
1066
+ function jumpMark(delta){
1067
+ if(!markNodes.length) return;
1068
+ if(markPos >= 0 && markNodes[markPos]) markNodes[markPos].classList.remove('here');
1069
+ markPos = (markPos + delta + markNodes.length) % markNodes.length;
1070
+ var m = markNodes[markPos];
1071
+ var d = m.closest('details');
1072
+ while(d){ d.open = true; d = d.parentElement ? d.parentElement.closest('details') : null; }
1073
+ m.classList.add('here');
1074
+ m.scrollIntoView({block:'center'});
1075
+ }
1076
+ function buildStageChips(){
1077
+ var host = $('#stage-chips');
1078
+ STAGES.forEach(function(s){
1079
+ var b = el('button', 'ctl tiny on', s[1]);
1080
+ b.addEventListener('click', function(){
1081
+ if(state.stages.has(s[0])) state.stages.delete(s[0]); else state.stages.add(s[0]);
1082
+ b.classList.toggle('on', state.stages.has(s[0]));
1083
+ applyStageFilter();
1084
+ });
1085
+ b.dataset.stage = s[0];
1086
+ host.appendChild(b);
1087
+ });
1088
+ }
1089
+ function setAllStages(on){
1090
+ state.stages = on ? new Set(STAGES.map(function(s){ return s[0]; })) : new Set();
1091
+ document.querySelectorAll('#stage-chips .ctl').forEach(function(b){ b.classList.toggle('on', on); });
1092
+ applyStageFilter();
1093
+ }
1094
+ function setTheme(theme){
1095
+ document.documentElement.setAttribute('data-theme', theme);
1096
+ $('#theme').textContent = theme === 'dark' ? 'Light theme' : 'Dark theme';
1097
+ try { localStorage.setItem('mtv4-theme', theme); } catch(e) {}
1098
+ }
1099
+ function init(){
1100
+ $('#run-name').textContent = DATA.run_name || DATA.run_dir;
1101
+ var st = DATA.stats;
1102
+ $('#stats').textContent =
1103
+ st.trajectories + ' trajectories · ' + st.terminated + ' terminated · ' + st.guided + ' guided · ' +
1104
+ st.minimum_waived + ' minimum waived · ' +
1105
+ st.excluded + ' excluded · ' + st.superseded + ' superseded · ' + st.filtered + ' F0-filtered · ' +
1106
+ st.parse_errors + ' parse errors · mean score ' +
1107
+ (st.mean_normalized === null || st.mean_normalized === undefined ? '—' : st.mean_normalized);
1108
+ $('#gen').textContent = DATA.run_dir + ' · generated ' + DATA.generated_at;
1109
+ buildStageChips();
1110
+ $('#stages-all').addEventListener('click', function(){ setAllStages(true); });
1111
+ $('#stages-none').addEventListener('click', function(){ setAllStages(false); });
1112
+ $('#cot').addEventListener('click', function(){
1113
+ state.hideCot = !state.hideCot;
1114
+ this.classList.toggle('on', state.hideCot);
1115
+ this.textContent = state.hideCot ? 'CoT hidden' : 'CoT shown';
1116
+ refreshAll();
1117
+ });
1118
+ $('#raw').addEventListener('click', function(){
1119
+ state.showRaw = !state.showRaw;
1120
+ this.classList.toggle('on', state.showRaw);
1121
+ this.textContent = state.showRaw ? 'Raw shown' : 'Raw hidden';
1122
+ document.querySelectorAll('#main .raw-wrap').forEach(function(d){ d.open = state.showRaw; });
1123
+ });
1124
+ $('#expand-all').addEventListener('click', function(){
1125
+ document.querySelectorAll('#main details').forEach(function(d){ d.open = true; });
1126
+ });
1127
+ $('#collapse-layers').addEventListener('click', function(){
1128
+ document.querySelectorAll('#main details.layer').forEach(function(d){ d.open = false; });
1129
+ });
1130
+ var timer = null;
1131
+ $('#search').addEventListener('input', function(){
1132
+ var value = this.value;
1133
+ if(timer) clearTimeout(timer);
1134
+ timer = setTimeout(function(){ state.query = value; refreshAll(); }, 160);
1135
+ });
1136
+ $('#next').addEventListener('click', function(){ jumpMark(1); });
1137
+ $('#prev').addEventListener('click', function(){ jumpMark(-1); });
1138
+ $('#only-matches').addEventListener('click', function(){
1139
+ state.filterMatches = !state.filterMatches;
1140
+ this.classList.toggle('on', state.filterMatches);
1141
+ applyMatchFilter();
1142
+ });
1143
+ $('#traj-filter').addEventListener('input', renderSidebar);
1144
+ $('#theme').addEventListener('click', function(){
1145
+ setTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');
1146
+ });
1147
+ var saved = 'dark';
1148
+ try { saved = localStorage.getItem('mtv4-theme') || 'dark'; } catch(e) {}
1149
+ setTheme(saved);
1150
+ renderSidebar();
1151
+ var first = DATA.trajectories.length ? DATA.trajectories[0].uid
1152
+ : (DATA.filtered.length ? DATA.filtered[0].uid : null);
1153
+ if(first) select(first);
1154
+ else $('#main').appendChild(el('div', 'empty', 'This run directory contains no trajectories.'));
1155
+ }
1156
+ init();
1157
  </script>
1158
  </body>
1159
  </html>