FloorIsAwake commited on
Commit
fcba531
·
verified ·
1 Parent(s): 904b5c8

Update logbook: Repro - Networked Information Aggregation for Binary Classification

Browse files
.serve.log ADDED
File without changes
README.md CHANGED
@@ -1,10 +1,18 @@
1
  ---
2
- title: Repro Networked Information Aggregation Binary Classification
3
- emoji: 📈
4
- colorFrom: green
5
- colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
 
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
  ---
2
+ title: "Repro - Networked Information Aggregation for Binary Classification"
3
+ emoji: 🎯
4
+ colorFrom: yellow
5
+ colorTo: red
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - trackio
10
+ - trackio-logbook
11
+ - open-experiment
12
+ - icml2026-repro
13
+ - paper-mrtg4NmvAe
14
  ---
15
 
16
+ # Repro - Networked Information Aggregation for Binary Classification
17
+
18
+ An open experiment logbook, published with [Trackio](https://github.com/gradio-app/trackio).
bucket-icon.svg ADDED
index.html CHANGED
@@ -1,19 +1,54 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
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>Repro - Networked Information Aggregation for Binary Classification</title>
7
+ <link rel="stylesheet" href="./logbook.css" />
8
+ </head>
9
+ <body>
10
+ <div id="app">
11
+ <aside id="sidebar">
12
+ <div id="book-head">
13
+ <img id="book-wordmark" src="./trackio-wordmark-dark.png" alt="" />
14
+ <div id="book-title" class="sr-only">Logbook</div>
15
+ </div>
16
+ <nav id="tree"></nav>
17
+ <div id="sidebar-foot" hidden>
18
+ <button id="connect-btn" type="button">
19
+ <span class="ico">ⓘ</span> Collaborate with your agent
20
+ </button>
21
+ </div>
22
+ </aside>
23
+ <main id="content">
24
+ <div id="page"></div>
25
+ </main>
26
+ </div>
27
+
28
+ <div id="modal" hidden>
29
+ <div class="modal-backdrop"></div>
30
+ <div class="modal-card" role="dialog" aria-modal="true">
31
+ <div class="modal-head">
32
+ <div class="modal-title">
33
+ <img class="modal-logo" src="./trackio-logo.png" alt="" />
34
+ Collaborate with your agent
35
+ </div>
36
+ <div class="modal-actions">
37
+ <button id="copy-agent" class="btn">Copy for agent</button>
38
+ <button id="modal-close" class="btn icon" aria-label="Close">×</button>
39
+ </div>
40
+ </div>
41
+ <div class="modal-body">
42
+ <p class="modal-intro">
43
+ Point your coding agent at this logbook. It reads a compact,
44
+ token-efficient version — and if you've given it write access to this
45
+ Space, it can add findings that sync back automatically.
46
+ </p>
47
+ <ol id="connect-steps"></ol>
48
+ </div>
49
+ </div>
50
+ </div>
51
+
52
+ <script src="./logbook.js"></script>
53
+ </body>
54
  </html>
logbook.css ADDED
@@ -0,0 +1,1602 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --bg: #ffffff;
3
+ --paper: #fdfcf9;
4
+ --panel: #ffffff;
5
+ --ink: #1f2937;
6
+ --muted: #6b7280;
7
+ --line: #e5e7eb;
8
+ --accent: #f97316;
9
+ --accent-strong: #ea580c;
10
+ --accent-soft: #fff7ed;
11
+ --accent-line: rgba(249, 115, 22, 0.16);
12
+ --grid-line: rgba(31, 41, 55, 0.045);
13
+ --code-bg: #f3f4f6;
14
+ --radius: 12px;
15
+ --serif: ui-serif, "Iowan Old Style", "Palatino Linotype", Georgia, serif;
16
+ --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
17
+ sans-serif;
18
+ --mono: "SFMono-Regular", "Cascadia Mono", "JetBrains Mono", Menlo, Consolas,
19
+ ui-monospace, monospace;
20
+ }
21
+
22
+ * {
23
+ box-sizing: border-box;
24
+ }
25
+
26
+ html,
27
+ body {
28
+ margin: 0;
29
+ padding: 0;
30
+ }
31
+
32
+ html {
33
+ scroll-behavior: smooth;
34
+ }
35
+
36
+ body {
37
+ background: var(--bg);
38
+ color: var(--ink);
39
+ font-family: var(--sans);
40
+ font-size: 13px;
41
+ line-height: 1.65;
42
+ -webkit-font-smoothing: antialiased;
43
+ }
44
+
45
+ #app {
46
+ display: flex;
47
+ min-height: 100vh;
48
+ }
49
+
50
+ /* ---- sidebar (composition-book cover) ---- */
51
+ #sidebar {
52
+ width: 280px;
53
+ flex: 0 0 280px;
54
+ background: #17181c;
55
+ color: #e7e7ea;
56
+ position: sticky;
57
+ top: 0;
58
+ height: 100vh;
59
+ overflow-y: auto;
60
+ padding: 22px 16px;
61
+ display: flex;
62
+ flex-direction: column;
63
+ }
64
+
65
+ #book-head {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: 10px;
69
+ padding: 8px;
70
+ margin-bottom: 12px;
71
+ border-radius: 10px;
72
+ cursor: pointer;
73
+ transition: background 0.12s;
74
+ }
75
+ #book-head:hover {
76
+ background: rgba(255, 255, 255, 0.05);
77
+ }
78
+ #book-wordmark {
79
+ width: 154px;
80
+ height: auto;
81
+ object-fit: contain;
82
+ }
83
+ .sr-only {
84
+ position: absolute;
85
+ width: 1px;
86
+ height: 1px;
87
+ padding: 0;
88
+ margin: -1px;
89
+ overflow: hidden;
90
+ clip: rect(0, 0, 0, 0);
91
+ white-space: nowrap;
92
+ border: 0;
93
+ }
94
+
95
+ #tree {
96
+ flex: 1;
97
+ padding-top: 8px;
98
+ }
99
+
100
+ #tree a {
101
+ display: block;
102
+ padding: 6px 10px;
103
+ border-radius: 8px;
104
+ color: #c3c4cb;
105
+ text-decoration: none;
106
+ font-size: 14px;
107
+ transition: background 0.12s, color 0.12s;
108
+ }
109
+
110
+ #tree a:hover {
111
+ background: rgba(255, 255, 255, 0.06);
112
+ color: #ffffff;
113
+ }
114
+
115
+ #tree a.active {
116
+ background: rgba(249, 115, 22, 0.16);
117
+ color: #fdba74;
118
+ font-weight: 600;
119
+ }
120
+
121
+ #tree a .tree-mark {
122
+ color: #6b6d76;
123
+ }
124
+
125
+ #tree a:hover .tree-mark,
126
+ #tree a.active .tree-mark {
127
+ color: inherit;
128
+ opacity: 0.6;
129
+ }
130
+
131
+ #tree .depth-1 {
132
+ padding-left: 22px;
133
+ }
134
+ #tree .depth-2 {
135
+ padding-left: 34px;
136
+ }
137
+ #tree .depth-3 {
138
+ padding-left: 46px;
139
+ }
140
+
141
+
142
+ /* ---- content ---- */
143
+ #content {
144
+ flex: 1;
145
+ min-width: 0;
146
+ padding: 48px 40px 120px;
147
+ background-color: var(--paper);
148
+ background-image:
149
+ linear-gradient(var(--grid-line) 1px, transparent 1px),
150
+ linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
151
+ background-size: 26px 26px;
152
+ background-position: center top;
153
+ }
154
+
155
+ #page {
156
+ width: 100%;
157
+ min-width: 0;
158
+ max-width: 1052px;
159
+ margin: 0 auto;
160
+ }
161
+
162
+ .page-section {
163
+ scroll-margin-top: 40px;
164
+ padding: 0 0 35px;
165
+ margin: 0 0 32px;
166
+ }
167
+
168
+ .page-section:last-child {
169
+ margin-bottom: 0;
170
+ }
171
+
172
+ .page-layout {
173
+ display: grid;
174
+ grid-template-columns: minmax(0, 760px) 248px;
175
+ gap: 44px;
176
+ align-items: start;
177
+ }
178
+
179
+ .page-body {
180
+ min-width: 0;
181
+ }
182
+
183
+ .resource-anchor {
184
+ display: block;
185
+ height: 0;
186
+ overflow: hidden;
187
+ }
188
+
189
+ /* ---- pinned notes ---- */
190
+ .pinned-notes {
191
+ margin: 30px 0 0;
192
+ }
193
+ .pinned-notes-list .cell {
194
+ margin: 0;
195
+ border-color: rgba(249, 115, 22, 0.55);
196
+ }
197
+ .pinned-notes-list .cell + .cell {
198
+ margin-top: 12px;
199
+ }
200
+ .cell.pinned-source {
201
+ border-color: rgba(249, 115, 22, 0.55);
202
+ }
203
+ .book-intro.has-pinned-notes {
204
+ border-bottom: none;
205
+ padding-bottom: 22px;
206
+ margin-bottom: 30px;
207
+ }
208
+ .book-intro.book-intro-tight {
209
+ border-bottom: none;
210
+ padding-bottom: 4px;
211
+ margin-bottom: 20px;
212
+ }
213
+
214
+ #page h1 {
215
+ font-family: var(--serif);
216
+ font-size: 34px;
217
+ line-height: 1.15;
218
+ letter-spacing: -0.02em;
219
+ margin: 0 0 8px;
220
+ overflow-wrap: anywhere;
221
+ }
222
+
223
+ #page .page-section:not(.book-intro) h1 {
224
+ font-size: 26px;
225
+ }
226
+
227
+ #page h2 {
228
+ font-family: var(--serif);
229
+ font-size: 24px;
230
+ margin: 36px 0 10px;
231
+ }
232
+
233
+ #page h3 {
234
+ font-size: 17px;
235
+ font-weight: 700;
236
+ margin: 26px 0 2px;
237
+ letter-spacing: -0.01em;
238
+ }
239
+
240
+ #page h3::before {
241
+ content: "";
242
+ display: inline-block;
243
+ width: 7px;
244
+ height: 7px;
245
+ border-radius: 2px;
246
+ background: var(--accent);
247
+ margin-right: 10px;
248
+ vertical-align: middle;
249
+ transform: translateY(-1px);
250
+ }
251
+
252
+ #page p {
253
+ margin: 10px 0;
254
+ }
255
+
256
+ #page blockquote {
257
+ margin: 14px 0;
258
+ padding: 2px 16px;
259
+ border-left: 3px solid #fdba74;
260
+ color: var(--muted);
261
+ }
262
+
263
+ #page hr {
264
+ display: none;
265
+ }
266
+
267
+ #page code {
268
+ font-family: var(--mono);
269
+ font-size: 0.86em;
270
+ background: var(--code-bg);
271
+ padding: 2px 6px;
272
+ border-radius: 6px;
273
+ }
274
+
275
+ #page pre {
276
+ max-width: 100%;
277
+ background: var(--code-bg);
278
+ border: 1px solid var(--line);
279
+ border-radius: var(--radius);
280
+ padding: 14px 16px;
281
+ overflow-x: auto;
282
+ }
283
+ #page pre code {
284
+ background: none;
285
+ padding: 0;
286
+ font-size: 11.5px;
287
+ }
288
+
289
+ /* ---- code blocks + collapsible accordion ---- */
290
+ #page pre.hl {
291
+ background: #17181c;
292
+ border: none;
293
+ color: #e7e7ea;
294
+ font-size: 13px;
295
+ line-height: 1.58;
296
+ }
297
+ #page pre.hl code {
298
+ color: inherit;
299
+ font-family: var(--mono);
300
+ }
301
+ .code-accordion {
302
+ border: 1px solid rgba(249, 115, 22, 0.2);
303
+ border-radius: 8px;
304
+ overflow: hidden;
305
+ margin: 12px 0;
306
+ background: #17181c;
307
+ }
308
+ .code-accordion summary {
309
+ list-style: none;
310
+ cursor: pointer;
311
+ display: flex;
312
+ align-items: center;
313
+ gap: 9px;
314
+ padding: 9px 12px;
315
+ font-family: var(--mono);
316
+ font-size: 11.5px;
317
+ font-weight: 700;
318
+ color: #e7e7ea;
319
+ background: #1e2027;
320
+ user-select: none;
321
+ overflow-wrap: anywhere;
322
+ }
323
+ .code-accordion summary::-webkit-details-marker {
324
+ display: none;
325
+ }
326
+ .code-accordion summary::after {
327
+ content: "▸";
328
+ margin-left: auto;
329
+ color: var(--accent);
330
+ transition: transform 0.12s;
331
+ transform: rotate(180deg);
332
+ }
333
+ .code-accordion[open] summary::after {
334
+ transform: rotate(90deg);
335
+ }
336
+ .code-accordion .code-ico {
337
+ color: var(--accent);
338
+ font-weight: 700;
339
+ }
340
+ .code-accordion pre.hl {
341
+ margin: 0;
342
+ border-radius: 0;
343
+ border: none;
344
+ border-top: 1px solid rgba(249, 115, 22, 0.16);
345
+ }
346
+ .tok-comment {
347
+ color: #7a7d87;
348
+ font-style: italic;
349
+ }
350
+ .tok-string {
351
+ color: #a5d6a7;
352
+ }
353
+ .tok-keyword {
354
+ color: #fdba74;
355
+ }
356
+ .tok-number {
357
+ color: #7fd0e0;
358
+ }
359
+
360
+ #page a {
361
+ color: var(--accent);
362
+ }
363
+
364
+ #page ul {
365
+ padding-left: 20px;
366
+ }
367
+
368
+ .ts {
369
+ font-family: var(--mono);
370
+ font-size: 12px;
371
+ color: var(--muted);
372
+ background: none;
373
+ padding: 0;
374
+ }
375
+
376
+ /* ---- notebook-style cells ---- */
377
+ .cell {
378
+ max-width: 100%;
379
+ border: 1px solid var(--line);
380
+ border-radius: 10px;
381
+ background: rgba(255, 255, 255, 0.86);
382
+ margin: 18px 0;
383
+ overflow: hidden;
384
+ box-shadow: 0 2px 10px rgba(31, 41, 55, 0.035);
385
+ }
386
+ .cell-head {
387
+ display: flex;
388
+ justify-content: space-between;
389
+ gap: 16px;
390
+ align-items: center;
391
+ padding: 14px 18px;
392
+ background: rgba(255, 255, 255, 0.92);
393
+ border-bottom: 1px solid var(--line);
394
+ }
395
+ .cell-head.no-title {
396
+ justify-content: flex-end;
397
+ padding-top: 10px;
398
+ padding-bottom: 10px;
399
+ }
400
+ .cell-title {
401
+ flex: 1;
402
+ min-width: 0;
403
+ font-size: 13px;
404
+ font-weight: 650;
405
+ color: var(--ink);
406
+ line-height: 1.35;
407
+ overflow-wrap: anywhere;
408
+ }
409
+ .cell-meta {
410
+ flex: 0 0 auto;
411
+ display: flex;
412
+ align-items: center;
413
+ gap: 10px;
414
+ font-family: var(--sans);
415
+ font-size: 13px;
416
+ color: var(--muted);
417
+ }
418
+ .cell-open {
419
+ flex: 0 0 auto;
420
+ font-family: var(--mono);
421
+ font-size: 12px;
422
+ color: var(--accent);
423
+ text-decoration: none;
424
+ }
425
+ .cell-open:hover {
426
+ color: var(--accent-strong);
427
+ }
428
+ .cell-body {
429
+ min-width: 0;
430
+ padding: 14px 18px 18px;
431
+ }
432
+ .cell.dashboard .cell-body {
433
+ padding: 0;
434
+ }
435
+ #page .cell-body h1,
436
+ #page .cell-body h2 {
437
+ font-family: var(--sans);
438
+ font-size: 17px;
439
+ font-weight: 700;
440
+ letter-spacing: -0.01em;
441
+ line-height: 1.35;
442
+ margin: 22px 0 6px;
443
+ }
444
+ #page .cell-body > :first-child {
445
+ margin-top: 0;
446
+ }
447
+ #page .cell-body > :last-child {
448
+ margin-bottom: 0;
449
+ }
450
+ .cell.code .cell-head {
451
+ background: #fbfbfc;
452
+ }
453
+ .figure-fit {
454
+ position: relative;
455
+ overflow: hidden;
456
+ min-height: 160px;
457
+ border: 1px solid var(--line);
458
+ border-radius: 8px;
459
+ background: #fff;
460
+ }
461
+ .figure-fit[hidden] {
462
+ display: none;
463
+ }
464
+ .figure-fit:fullscreen,
465
+ .figure-fit:-webkit-full-screen {
466
+ width: 100%;
467
+ height: 100%;
468
+ border: none;
469
+ border-radius: 0;
470
+ }
471
+ .figure-frame {
472
+ display: block;
473
+ width: 100%;
474
+ min-height: 160px;
475
+ border: none;
476
+ background: #fff;
477
+ }
478
+ .figure-frame[hidden],
479
+ .figure-raw[hidden] {
480
+ display: none;
481
+ }
482
+ .fig-switch {
483
+ position: relative;
484
+ display: inline-flex;
485
+ flex: 0 0 auto;
486
+ border: 1px solid var(--line);
487
+ border-radius: 999px;
488
+ background: var(--code-bg);
489
+ padding: 2px;
490
+ }
491
+ .fig-switch button {
492
+ position: relative;
493
+ z-index: 1;
494
+ flex: 1;
495
+ min-width: 62px;
496
+ border: none;
497
+ background: none;
498
+ font-family: var(--sans);
499
+ font-size: 12px;
500
+ font-weight: 600;
501
+ color: var(--muted);
502
+ padding: 3px 12px;
503
+ border-radius: 999px;
504
+ cursor: pointer;
505
+ transition: color 0.15s;
506
+ }
507
+ .fig-switch button.active {
508
+ color: var(--accent-strong);
509
+ }
510
+ .fig-switch-thumb {
511
+ position: absolute;
512
+ top: 2px;
513
+ bottom: 2px;
514
+ left: 2px;
515
+ width: calc(50% - 2px);
516
+ border-radius: 999px;
517
+ background: var(--panel);
518
+ border: 1px solid rgba(249, 115, 22, 0.35);
519
+ box-shadow: 0 1px 4px rgba(31, 41, 55, 0.08);
520
+ transition: transform 0.18s ease;
521
+ }
522
+ .fig-switch.raw .fig-switch-thumb {
523
+ transform: translateX(100%);
524
+ }
525
+ #page .figure-raw pre {
526
+ margin: 0;
527
+ max-height: 420px;
528
+ overflow: auto;
529
+ font-family: var(--mono);
530
+ font-size: 13px;
531
+ line-height: 1.55;
532
+ background: var(--code-bg);
533
+ border: 1px solid var(--line);
534
+ border-radius: 8px;
535
+ padding: 12px 14px;
536
+ }
537
+ /* ---- figure fullscreen ---- */
538
+ .cell-fullscreen {
539
+ position: relative;
540
+ display: inline-flex;
541
+ flex: 0 0 auto;
542
+ }
543
+ .cell-fullscreen-btn {
544
+ display: inline-flex;
545
+ align-items: center;
546
+ justify-content: center;
547
+ width: 26px;
548
+ height: 26px;
549
+ padding: 0;
550
+ border: 1px solid var(--line);
551
+ border-radius: 999px;
552
+ background: var(--code-bg);
553
+ color: var(--muted);
554
+ cursor: pointer;
555
+ transition: color 0.15s, border-color 0.15s, background 0.15s;
556
+ }
557
+ .cell-fullscreen-btn:hover {
558
+ color: var(--accent-strong);
559
+ border-color: rgba(249, 115, 22, 0.35);
560
+ background: var(--accent-soft);
561
+ }
562
+ .cell-fullscreen-btn svg {
563
+ width: 14px;
564
+ height: 14px;
565
+ }
566
+ /* ---- copyable snippets ---- */
567
+ .snippet {
568
+ position: relative;
569
+ }
570
+ .copy-snippet {
571
+ position: absolute;
572
+ top: 7px;
573
+ right: 8px;
574
+ width: 24px;
575
+ height: 24px;
576
+ border: none;
577
+ border-radius: 6px;
578
+ background: rgba(255, 255, 255, 0.08);
579
+ color: #9a9da8;
580
+ font-size: 12px;
581
+ line-height: 1;
582
+ cursor: pointer;
583
+ opacity: 0;
584
+ transition: opacity 0.12s, color 0.12s, background 0.12s;
585
+ }
586
+ .snippet:hover .copy-snippet,
587
+ .jp-out:hover .copy-snippet,
588
+ .figure-raw:hover .copy-snippet,
589
+ .code-accordion summary:hover .copy-snippet {
590
+ opacity: 1;
591
+ }
592
+ .copy-snippet:hover {
593
+ color: #ffffff;
594
+ background: rgba(255, 255, 255, 0.16);
595
+ }
596
+ .copy-snippet.copied {
597
+ color: #52d08a;
598
+ opacity: 1;
599
+ }
600
+ .code-accordion .code-name {
601
+ user-select: text;
602
+ cursor: text;
603
+ }
604
+ .jp-out,
605
+ .figure-raw {
606
+ position: relative;
607
+ }
608
+ .jp-out .copy-snippet,
609
+ .figure-raw .copy-snippet {
610
+ background: var(--code-bg);
611
+ color: var(--muted);
612
+ border: 1px solid var(--line);
613
+ }
614
+ .jp-out .copy-snippet:hover,
615
+ .figure-raw .copy-snippet:hover {
616
+ color: var(--accent-strong);
617
+ background: var(--panel);
618
+ }
619
+
620
+ /* ---- jupyter-style code cells ---- */
621
+ .jp {
622
+ border: 1px solid var(--line);
623
+ border-radius: 10px;
624
+ overflow: hidden;
625
+ margin: 12px 0;
626
+ background: var(--panel);
627
+ }
628
+ .jp-gutter {
629
+ flex: 0 0 46px;
630
+ padding: 13px 0 0 13px;
631
+ font-family: var(--mono);
632
+ font-size: 10.5px;
633
+ letter-spacing: 0.07em;
634
+ text-transform: uppercase;
635
+ font-weight: 600;
636
+ user-select: none;
637
+ }
638
+ .jp-in {
639
+ display: flex;
640
+ background: #17181c;
641
+ }
642
+ .jp-in .jp-gutter {
643
+ color: #6f727d;
644
+ }
645
+ .jp-in-body {
646
+ flex: 1;
647
+ min-width: 0;
648
+ }
649
+ #page .jp-in-body pre.hl {
650
+ margin: 0;
651
+ border: none;
652
+ border-radius: 0;
653
+ background: none;
654
+ padding: 12px 16px 12px 0;
655
+ }
656
+ .jp-in-body .code-accordion {
657
+ margin: 0;
658
+ border: none;
659
+ border-top: 1px solid rgba(255, 255, 255, 0.09);
660
+ border-radius: 0;
661
+ background: none;
662
+ }
663
+ .jp-in-body .code-accordion summary {
664
+ background: none;
665
+ padding: 9px 16px 9px 0;
666
+ }
667
+ .jp-in-body .code-accordion pre.hl {
668
+ border-top: 1px solid rgba(255, 255, 255, 0.09);
669
+ }
670
+ .jp-meta {
671
+ padding: 5px 14px;
672
+ font-family: var(--mono);
673
+ font-size: 11.5px;
674
+ color: var(--muted);
675
+ background: #fbfbfc;
676
+ border-top: 1px solid var(--line);
677
+ }
678
+ .jp-out {
679
+ display: flex;
680
+ border-top: 1px solid var(--line);
681
+ background: var(--panel);
682
+ }
683
+ .jp-out .jp-gutter {
684
+ color: var(--accent-strong);
685
+ }
686
+ .jp-out-body {
687
+ flex: 1;
688
+ min-width: 0;
689
+ }
690
+ #page .jp-out-pre {
691
+ min-width: 0;
692
+ margin: 0;
693
+ border: none;
694
+ border-radius: 0;
695
+ background: none;
696
+ color: var(--ink);
697
+ font-family: var(--mono);
698
+ font-size: 13px;
699
+ line-height: 1.55;
700
+ padding: 12px 16px 12px 0;
701
+ white-space: pre;
702
+ overflow-x: auto;
703
+ overflow-y: auto;
704
+ max-height: 26em;
705
+ }
706
+ .jp-artifacts {
707
+ display: flex;
708
+ flex-direction: column;
709
+ }
710
+ .jp-out-body .jp-out-pre + .jp-artifacts {
711
+ border-top: 1px solid var(--line);
712
+ }
713
+ .out-artifact {
714
+ display: flex;
715
+ align-items: baseline;
716
+ gap: 8px;
717
+ padding: 9px 16px 9px 0;
718
+ text-decoration: none;
719
+ color: inherit;
720
+ }
721
+ .out-artifact + .out-artifact {
722
+ border-top: 1px solid var(--line);
723
+ }
724
+ a.out-artifact:hover .out-artifact-name {
725
+ color: var(--accent-strong);
726
+ }
727
+ .out-artifact-ico {
728
+ flex: 0 0 auto;
729
+ font-size: 13px;
730
+ }
731
+ .out-artifact-name {
732
+ font-family: var(--mono);
733
+ font-size: 12.5px;
734
+ font-weight: 600;
735
+ color: var(--ink);
736
+ overflow: hidden;
737
+ text-overflow: ellipsis;
738
+ white-space: nowrap;
739
+ }
740
+ .out-artifact-meta {
741
+ flex: 0 0 auto;
742
+ margin-left: auto;
743
+ padding-left: 12px;
744
+ font-size: 12px;
745
+ color: var(--muted);
746
+ white-space: nowrap;
747
+ }
748
+ .out-artifact-state.open {
749
+ color: var(--accent);
750
+ font-weight: 600;
751
+ }
752
+ .trackio-embed {
753
+ border: 1px solid var(--line);
754
+ border-radius: var(--radius);
755
+ overflow: hidden;
756
+ background: var(--panel);
757
+ }
758
+ .trackio-cell-meta {
759
+ display: flex;
760
+ gap: 6px;
761
+ flex-wrap: wrap;
762
+ justify-content: flex-end;
763
+ }
764
+
765
+ /* ---- unfurl cards ---- */
766
+ .unfurl {
767
+ display: block;
768
+ border: 1px solid var(--line);
769
+ border-radius: var(--radius);
770
+ background: var(--panel);
771
+ margin: 12px 0;
772
+ overflow: hidden;
773
+ text-decoration: none;
774
+ color: inherit;
775
+ transition: border-color 0.14s, box-shadow 0.14s;
776
+ }
777
+ .unfurl:hover {
778
+ border-color: #cfcbe6;
779
+ box-shadow: 0 4px 18px rgba(30, 20, 80, 0.06);
780
+ }
781
+
782
+ .unfurl-body {
783
+ padding: 13px 16px;
784
+ display: flex;
785
+ gap: 12px;
786
+ align-items: flex-start;
787
+ }
788
+
789
+ .unfurl-ico {
790
+ font-size: 20px;
791
+ line-height: 1.3;
792
+ flex: 0 0 auto;
793
+ }
794
+
795
+ .unfurl-main {
796
+ min-width: 0;
797
+ flex: 1;
798
+ }
799
+
800
+ .unfurl-kind {
801
+ font-family: var(--mono);
802
+ font-size: 10.5px;
803
+ text-transform: uppercase;
804
+ letter-spacing: 0.08em;
805
+ color: var(--accent);
806
+ font-weight: 600;
807
+ }
808
+
809
+ .unfurl-title {
810
+ font-weight: 650;
811
+ font-size: 15px;
812
+ margin: 1px 0 2px;
813
+ white-space: nowrap;
814
+ overflow: hidden;
815
+ text-overflow: ellipsis;
816
+ }
817
+
818
+ .unfurl-desc {
819
+ color: var(--muted);
820
+ font-size: 13.5px;
821
+ line-height: 1.45;
822
+ }
823
+
824
+ .unfurl-meta {
825
+ margin-top: 6px;
826
+ display: flex;
827
+ flex-wrap: wrap;
828
+ gap: 6px;
829
+ }
830
+
831
+ .chip {
832
+ font-size: 11.5px;
833
+ background: var(--code-bg);
834
+ border-radius: 999px;
835
+ padding: 2px 9px;
836
+ color: var(--muted);
837
+ font-family: var(--mono);
838
+ }
839
+
840
+ .unfurl-raw {
841
+ font-family: var(--mono);
842
+ font-size: 11px;
843
+ color: var(--muted);
844
+ border-top: 1px solid var(--line);
845
+ padding: 7px 16px;
846
+ white-space: nowrap;
847
+ overflow: hidden;
848
+ text-overflow: ellipsis;
849
+ }
850
+
851
+ .unfurl.embed {
852
+ padding: 0;
853
+ overflow: hidden;
854
+ }
855
+ .embed-head {
856
+ display: flex;
857
+ align-items: center;
858
+ gap: 10px;
859
+ padding: 10px 14px;
860
+ border-bottom: 1px solid var(--line);
861
+ }
862
+ .embed-head .unfurl-kind {
863
+ flex: 0 0 auto;
864
+ }
865
+ .embed-title {
866
+ flex: 1;
867
+ min-width: 0;
868
+ font-weight: 650;
869
+ font-size: 14px;
870
+ color: var(--ink);
871
+ text-decoration: none;
872
+ white-space: nowrap;
873
+ overflow: hidden;
874
+ text-overflow: ellipsis;
875
+ }
876
+ .embed-title:hover {
877
+ color: var(--accent);
878
+ }
879
+ .embed-open {
880
+ flex: 0 0 auto;
881
+ font-family: var(--mono);
882
+ font-size: 12px;
883
+ color: var(--accent);
884
+ text-decoration: none;
885
+ }
886
+ .embed-frame {
887
+ display: block;
888
+ width: 100%;
889
+ height: 560px;
890
+ border: 0;
891
+ background: var(--code-bg);
892
+ }
893
+
894
+ .dashboard-shell {
895
+ display: block;
896
+ }
897
+ .dashboard-shell .dashboard-frame {
898
+ display: block;
899
+ width: 100%;
900
+ height: 900px;
901
+ border: 0;
902
+ background: var(--code-bg);
903
+ }
904
+
905
+ .unfurl.image {
906
+ padding: 0;
907
+ }
908
+ .unfurl.image img {
909
+ display: block;
910
+ width: 100%;
911
+ height: auto;
912
+ max-height: 460px;
913
+ object-fit: contain;
914
+ background: var(--code-bg);
915
+ }
916
+
917
+ .artifact-chip {
918
+ border: 1px solid var(--line);
919
+ background: var(--panel);
920
+ border-radius: var(--radius);
921
+ padding: 10px 14px;
922
+ margin: 8px 0;
923
+ font-size: 14px;
924
+ }
925
+ .cell.dashboard .artifact-chip {
926
+ margin: 14px 18px 18px;
927
+ }
928
+ .artifact-chip code {
929
+ color: var(--accent);
930
+ }
931
+
932
+ /* ---- task board ---- */
933
+ .board-wrap {
934
+ overflow-x: auto;
935
+ border: 1px solid var(--line);
936
+ border-radius: var(--radius);
937
+ margin: 12px 0 20px;
938
+ background: var(--panel);
939
+ }
940
+ table.board {
941
+ border-collapse: collapse;
942
+ width: 100%;
943
+ font-size: 14px;
944
+ }
945
+ table.board th,
946
+ table.board td {
947
+ text-align: left;
948
+ padding: 9px 14px;
949
+ border-bottom: 1px solid var(--line);
950
+ vertical-align: top;
951
+ }
952
+ table.board thead th {
953
+ background: var(--accent-soft);
954
+ font-size: 12px;
955
+ text-transform: uppercase;
956
+ letter-spacing: 0.05em;
957
+ color: #9a4a12;
958
+ font-weight: 600;
959
+ border-bottom: 1px solid var(--line);
960
+ }
961
+ table.board tbody tr:last-child td {
962
+ border-bottom: none;
963
+ }
964
+ table.board .col-check {
965
+ text-align: center;
966
+ width: 92px;
967
+ white-space: nowrap;
968
+ }
969
+ table.board tr.section-row td {
970
+ background: var(--accent-soft);
971
+ text-align: center;
972
+ font-weight: 700;
973
+ font-size: 13px;
974
+ color: var(--accent-strong);
975
+ padding: 7px 14px;
976
+ letter-spacing: 0.02em;
977
+ }
978
+ .box {
979
+ display: inline-flex;
980
+ align-items: center;
981
+ justify-content: center;
982
+ width: 18px;
983
+ height: 18px;
984
+ border: 1.5px solid #cfcbe0;
985
+ border-radius: 5px;
986
+ font-size: 12px;
987
+ color: #fff;
988
+ line-height: 1;
989
+ }
990
+ .box.on {
991
+ background: var(--accent);
992
+ border-color: var(--accent);
993
+ }
994
+ .who-chip {
995
+ display: inline-block;
996
+ padding: 3px 12px;
997
+ border-radius: 999px;
998
+ font-size: 12.5px;
999
+ font-weight: 600;
1000
+ white-space: nowrap;
1001
+ }
1002
+ .who-chip.muted {
1003
+ background: var(--code-bg);
1004
+ color: var(--muted);
1005
+ font-weight: 500;
1006
+ }
1007
+
1008
+ /* ---- status badges + clickable rows ---- */
1009
+ table.board .col-status {
1010
+ width: 130px;
1011
+ white-space: nowrap;
1012
+ }
1013
+ .badge {
1014
+ display: inline-block;
1015
+ padding: 3px 11px;
1016
+ border-radius: 999px;
1017
+ font-size: 12px;
1018
+ font-weight: 600;
1019
+ letter-spacing: 0.01em;
1020
+ }
1021
+ .badge.gray {
1022
+ background: var(--code-bg);
1023
+ color: var(--muted);
1024
+ }
1025
+ .badge.amber {
1026
+ background: var(--accent-soft);
1027
+ color: #b45309;
1028
+ }
1029
+ .badge.green {
1030
+ background: #e6f7ee;
1031
+ color: #1a8a55;
1032
+ }
1033
+ .badge.red {
1034
+ background: #fde8ec;
1035
+ color: #c62a4b;
1036
+ }
1037
+ table.board tr.linked-row {
1038
+ cursor: pointer;
1039
+ }
1040
+ table.board tr.linked-row:hover td {
1041
+ background: var(--accent-soft);
1042
+ }
1043
+ table.board tr.linked-row a {
1044
+ color: var(--ink);
1045
+ font-weight: 600;
1046
+ text-decoration: none;
1047
+ }
1048
+ table.board tr.linked-row:hover a {
1049
+ color: var(--accent-strong);
1050
+ }
1051
+
1052
+ /* ---- agent read hint ---- */
1053
+ .agent-hint {
1054
+ display: flex;
1055
+ align-items: center;
1056
+ flex-wrap: wrap;
1057
+ gap: 8px;
1058
+ margin: 4px 0 22px;
1059
+ font-size: 12.5px;
1060
+ color: var(--muted);
1061
+ }
1062
+ #page .agent-hint code {
1063
+ background: var(--code-bg);
1064
+ padding: 2px 9px;
1065
+ border-radius: 6px;
1066
+ font-family: var(--mono);
1067
+ font-size: 12px;
1068
+ font-weight: 500;
1069
+ color: var(--ink);
1070
+ }
1071
+ .agent-hint .copy {
1072
+ flex: 0 0 auto;
1073
+ background: none;
1074
+ color: var(--muted);
1075
+ border: 1px solid var(--line);
1076
+ border-radius: 6px;
1077
+ width: 22px;
1078
+ height: 22px;
1079
+ font-size: 11px;
1080
+ line-height: 1;
1081
+ cursor: pointer;
1082
+ transition: color 0.12s, border-color 0.12s;
1083
+ }
1084
+ .agent-hint .copy:hover {
1085
+ color: var(--accent-strong);
1086
+ border-color: var(--accent);
1087
+ }
1088
+ .agent-hint .copy.copied {
1089
+ color: #1a8a55;
1090
+ border-color: #1a8a55;
1091
+ }
1092
+ .agent-hint-note {
1093
+ margin-left: auto;
1094
+ font-size: 12px;
1095
+ color: var(--muted);
1096
+ }
1097
+
1098
+ /* ---- logbook summary stats ---- */
1099
+ .logbook-stats {
1100
+ display: flex;
1101
+ flex-wrap: wrap;
1102
+ gap: 12px;
1103
+ margin: 0 0 28px;
1104
+ }
1105
+ .stat-tile {
1106
+ position: relative;
1107
+ display: inline-flex;
1108
+ align-items: center;
1109
+ gap: 11px;
1110
+ border: 1px solid var(--line);
1111
+ background: var(--panel);
1112
+ border-radius: var(--radius);
1113
+ padding: 12px 23px;
1114
+ font: inherit;
1115
+ text-align: left;
1116
+ cursor: pointer;
1117
+ transition: border-color 0.12s, box-shadow 0.12s;
1118
+ }
1119
+ .stat-tile:hover:not([disabled]) {
1120
+ border-color: rgba(249, 115, 22, 0.45);
1121
+ box-shadow: 0 3px 12px rgba(31, 41, 55, 0.06);
1122
+ }
1123
+ .stat-tile:focus-visible {
1124
+ outline: 2px solid var(--accent);
1125
+ outline-offset: 2px;
1126
+ }
1127
+ .stat-tile[disabled] {
1128
+ cursor: default;
1129
+ opacity: 0.7;
1130
+ }
1131
+ .stat-tile.open {
1132
+ border-color: rgba(249, 115, 22, 0.6);
1133
+ box-shadow: 0 3px 12px rgba(31, 41, 55, 0.08);
1134
+ }
1135
+ .stat-icon {
1136
+ width: 24px;
1137
+ height: 24px;
1138
+ flex: 0 0 24px;
1139
+ object-fit: contain;
1140
+ align-self: center;
1141
+ }
1142
+ .stat-text {
1143
+ display: flex;
1144
+ align-items: baseline;
1145
+ gap: 8px;
1146
+ white-space: nowrap;
1147
+ line-height: 1;
1148
+ }
1149
+ .stat-num {
1150
+ font-family: var(--mono);
1151
+ font-size: 20px;
1152
+ font-weight: 600;
1153
+ line-height: 1;
1154
+ color: var(--accent-strong);
1155
+ }
1156
+ .stat-label {
1157
+ font-size: 15px;
1158
+ line-height: 1;
1159
+ color: var(--muted);
1160
+ }
1161
+ .stat-caret {
1162
+ margin-left: 2px;
1163
+ font-size: 10px;
1164
+ color: var(--muted);
1165
+ align-self: center;
1166
+ transition: transform 0.12s;
1167
+ }
1168
+ .stat-tile.open .stat-caret {
1169
+ transform: rotate(180deg);
1170
+ }
1171
+ .stat-popover {
1172
+ position: absolute;
1173
+ top: 100%;
1174
+ left: 0;
1175
+ margin-top: 6px;
1176
+ min-width: 300px;
1177
+ max-width: min(460px, 92vw);
1178
+ max-height: 340px;
1179
+ overflow-y: auto;
1180
+ z-index: 20;
1181
+ background: var(--panel);
1182
+ border: 1px solid var(--line);
1183
+ border-radius: var(--radius);
1184
+ box-shadow: 0 8px 28px rgba(31, 41, 55, 0.12);
1185
+ padding: 6px;
1186
+ }
1187
+ .stat-popover[hidden] {
1188
+ display: none;
1189
+ }
1190
+ .stat-pop-head {
1191
+ padding: 6px 10px 8px;
1192
+ font-size: 11.5px;
1193
+ font-weight: 700;
1194
+ letter-spacing: 0.03em;
1195
+ text-transform: uppercase;
1196
+ color: var(--muted);
1197
+ }
1198
+ .stat-row {
1199
+ display: flex;
1200
+ align-items: flex-start;
1201
+ gap: 10px;
1202
+ padding: 9px 11px;
1203
+ border-radius: 9px;
1204
+ border: 1px solid transparent;
1205
+ text-decoration: none;
1206
+ color: inherit;
1207
+ cursor: pointer;
1208
+ }
1209
+ .stat-row:hover {
1210
+ border-color: rgba(249, 115, 22, 0.4);
1211
+ background: var(--accent-soft);
1212
+ }
1213
+ .stat-row-ico {
1214
+ font-size: 15px;
1215
+ line-height: 1.3;
1216
+ flex: 0 0 auto;
1217
+ }
1218
+ .stat-row-main {
1219
+ min-width: 0;
1220
+ flex: 1;
1221
+ }
1222
+ .stat-row-title {
1223
+ font-family: var(--mono);
1224
+ font-size: 12.5px;
1225
+ font-weight: 600;
1226
+ color: var(--ink);
1227
+ overflow: hidden;
1228
+ text-overflow: ellipsis;
1229
+ white-space: nowrap;
1230
+ }
1231
+ .stat-row-meta {
1232
+ margin-top: 2px;
1233
+ font-size: 12px;
1234
+ color: var(--muted);
1235
+ }
1236
+ .stat-row-state.open {
1237
+ color: var(--accent);
1238
+ font-weight: 600;
1239
+ border-radius: 5px;
1240
+ padding: 1px 5px;
1241
+ margin: -1px -2px;
1242
+ }
1243
+ .stat-row-state.open:hover {
1244
+ background: rgba(249, 115, 22, 0.14);
1245
+ text-decoration: underline;
1246
+ }
1247
+ .art-ico {
1248
+ width: 1em;
1249
+ height: 1em;
1250
+ object-fit: contain;
1251
+ vertical-align: -0.15em;
1252
+ }
1253
+
1254
+ /* ---- scroll-to-resource highlight ---- */
1255
+ .res-flash {
1256
+ animation: res-flash 1.5s ease;
1257
+ border-radius: 8px;
1258
+ }
1259
+ @keyframes res-flash {
1260
+ 0%,
1261
+ 25% {
1262
+ box-shadow: 0 0 0 3px var(--accent);
1263
+ }
1264
+ 100% {
1265
+ box-shadow: 0 0 0 3px rgba(249, 115, 22, 0);
1266
+ }
1267
+ }
1268
+
1269
+ /* ---- inline resource chips ---- */
1270
+ #page .res-chip {
1271
+ display: inline-flex;
1272
+ align-items: center;
1273
+ gap: 5px;
1274
+ max-width: 100%;
1275
+ padding: 0 9px 0 6px;
1276
+ margin: 0 1px;
1277
+ border: 1px solid var(--line);
1278
+ border-radius: 999px;
1279
+ background: var(--panel);
1280
+ font-family: var(--mono);
1281
+ font-size: 0.78em;
1282
+ font-weight: 600;
1283
+ color: var(--ink);
1284
+ text-decoration: none;
1285
+ white-space: nowrap;
1286
+ overflow: hidden;
1287
+ text-overflow: ellipsis;
1288
+ vertical-align: middle;
1289
+ line-height: 1.65;
1290
+ transform: translateY(-0.08em);
1291
+ transition: border-color 0.12s, background 0.12s, color 0.12s;
1292
+ }
1293
+ .res-chip-ico {
1294
+ font-size: 1.05em;
1295
+ line-height: 1;
1296
+ }
1297
+ #page .res-chip:hover,
1298
+ #page .res-chip.res-hl {
1299
+ border-color: var(--accent);
1300
+ background: var(--accent-soft);
1301
+ color: var(--accent-strong);
1302
+ }
1303
+ #page a.res-link.res-hl {
1304
+ background: var(--accent-soft);
1305
+ border-radius: 4px;
1306
+ }
1307
+ .rail-item.res-hl {
1308
+ border-color: var(--accent);
1309
+ background: var(--accent-soft);
1310
+ box-shadow: 0 3px 12px rgba(249, 115, 22, 0.14);
1311
+ }
1312
+ .rail-item.res-hl .rail-title {
1313
+ color: var(--accent-strong);
1314
+ }
1315
+ .rail-item.rail-local {
1316
+ cursor: default;
1317
+ }
1318
+ .artifact-chip.res-hl {
1319
+ border-color: var(--accent);
1320
+ background: var(--accent-soft);
1321
+ }
1322
+
1323
+ /* ---- contextual resources rail ---- */
1324
+ .context-rail {
1325
+ position: relative;
1326
+ width: 248px;
1327
+ }
1328
+ .context-rail[hidden] {
1329
+ display: none;
1330
+ }
1331
+ .rail-kind {
1332
+ display: flex;
1333
+ align-items: center;
1334
+ gap: 5px;
1335
+ font-family: var(--mono);
1336
+ font-size: 10px;
1337
+ text-transform: uppercase;
1338
+ letter-spacing: 0.08em;
1339
+ font-weight: 600;
1340
+ color: var(--accent);
1341
+ margin-bottom: 4px;
1342
+ }
1343
+ .rail-item {
1344
+ position: absolute;
1345
+ left: 0;
1346
+ right: 0;
1347
+ display: block;
1348
+ border: 1px solid var(--line);
1349
+ border-radius: 10px;
1350
+ background: var(--panel);
1351
+ padding: 9px 12px;
1352
+ margin-bottom: 8px;
1353
+ text-decoration: none;
1354
+ color: inherit;
1355
+ transition: border-color 0.14s, box-shadow 0.14s;
1356
+ }
1357
+ .rail-item:hover {
1358
+ border-color: rgba(249, 115, 22, 0.45);
1359
+ box-shadow: 0 3px 12px rgba(31, 41, 55, 0.06);
1360
+ }
1361
+ .rail-title {
1362
+ font-family: var(--mono);
1363
+ font-size: 12.5px;
1364
+ font-weight: 600;
1365
+ color: var(--ink);
1366
+ overflow-wrap: anywhere;
1367
+ line-height: 1.4;
1368
+ }
1369
+ .rail-item:hover .rail-title {
1370
+ color: var(--accent-strong);
1371
+ }
1372
+ .rail-meta {
1373
+ font-size: 11.5px;
1374
+ color: var(--muted);
1375
+ margin-top: 2px;
1376
+ }
1377
+
1378
+ @media (max-width: 1400px) {
1379
+ .page-layout {
1380
+ display: block;
1381
+ }
1382
+ .context-rail {
1383
+ width: 100%;
1384
+ margin-top: 28px;
1385
+ position: static;
1386
+ min-height: 0 !important;
1387
+ display: grid;
1388
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
1389
+ gap: 10px;
1390
+ }
1391
+ .context-rail[hidden] {
1392
+ display: none;
1393
+ }
1394
+ .context-rail .rail-item {
1395
+ position: static;
1396
+ margin-bottom: 0;
1397
+ }
1398
+ }
1399
+
1400
+ /* ---- connect footer + modal ---- */
1401
+ #sidebar-foot {
1402
+ margin-top: auto;
1403
+ padding-top: 14px;
1404
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
1405
+ }
1406
+
1407
+ #connect-btn {
1408
+ width: 100%;
1409
+ display: flex;
1410
+ align-items: center;
1411
+ gap: 8px;
1412
+ background: rgba(255, 255, 255, 0.05);
1413
+ color: #c3c4cb;
1414
+ border: 1px solid rgba(255, 255, 255, 0.12);
1415
+ border-radius: 9px;
1416
+ padding: 9px 12px;
1417
+ font-size: 13.5px;
1418
+ font-family: var(--sans);
1419
+ cursor: pointer;
1420
+ transition: background 0.12s, color 0.12s, border-color 0.12s;
1421
+ }
1422
+ #connect-btn:hover {
1423
+ background: rgba(249, 115, 22, 0.14);
1424
+ border-color: rgba(249, 115, 22, 0.4);
1425
+ color: #fdba74;
1426
+ }
1427
+ #connect-btn .ico {
1428
+ font-size: 15px;
1429
+ }
1430
+
1431
+ #modal[hidden] {
1432
+ display: none;
1433
+ }
1434
+ #modal {
1435
+ position: fixed;
1436
+ inset: 0;
1437
+ z-index: 100;
1438
+ display: flex;
1439
+ align-items: center;
1440
+ justify-content: center;
1441
+ padding: 24px;
1442
+ }
1443
+ .modal-backdrop {
1444
+ position: absolute;
1445
+ inset: 0;
1446
+ background: rgba(20, 18, 30, 0.5);
1447
+ backdrop-filter: blur(2px);
1448
+ }
1449
+ .modal-card {
1450
+ position: relative;
1451
+ background: var(--panel);
1452
+ border-radius: 16px;
1453
+ width: 100%;
1454
+ max-width: 620px;
1455
+ max-height: 85vh;
1456
+ overflow-y: auto;
1457
+ box-shadow: 0 24px 70px rgba(20, 15, 50, 0.28);
1458
+ }
1459
+ .modal-head {
1460
+ display: flex;
1461
+ align-items: center;
1462
+ justify-content: space-between;
1463
+ gap: 12px;
1464
+ padding: 18px 22px;
1465
+ border-bottom: 1px solid var(--line);
1466
+ position: sticky;
1467
+ top: 0;
1468
+ background: var(--panel);
1469
+ }
1470
+ .modal-title {
1471
+ display: flex;
1472
+ align-items: center;
1473
+ gap: 10px;
1474
+ font-family: var(--serif);
1475
+ font-size: 21px;
1476
+ letter-spacing: -0.01em;
1477
+ }
1478
+ .modal-logo {
1479
+ width: 26px;
1480
+ height: 26px;
1481
+ object-fit: contain;
1482
+ }
1483
+ .modal-actions {
1484
+ display: flex;
1485
+ align-items: center;
1486
+ gap: 8px;
1487
+ }
1488
+ .btn {
1489
+ font-family: var(--sans);
1490
+ font-size: 13.5px;
1491
+ font-weight: 600;
1492
+ border: 1px solid var(--line);
1493
+ background: var(--panel);
1494
+ color: var(--ink);
1495
+ border-radius: 9px;
1496
+ padding: 8px 13px;
1497
+ cursor: pointer;
1498
+ transition: background 0.12s, border-color 0.12s, color 0.12s;
1499
+ }
1500
+ .btn:hover {
1501
+ border-color: var(--accent);
1502
+ color: var(--accent-strong);
1503
+ }
1504
+ .btn.copied {
1505
+ border-color: #1a8a55;
1506
+ color: #1a8a55;
1507
+ }
1508
+ .btn.icon {
1509
+ font-size: 18px;
1510
+ line-height: 1;
1511
+ padding: 6px 11px;
1512
+ font-weight: 400;
1513
+ }
1514
+ .modal-body {
1515
+ padding: 20px 22px 26px;
1516
+ }
1517
+ .modal-intro {
1518
+ margin: 0 0 20px;
1519
+ color: var(--muted);
1520
+ line-height: 1.55;
1521
+ }
1522
+ #connect-steps {
1523
+ list-style: none;
1524
+ margin: 0;
1525
+ padding: 0;
1526
+ }
1527
+ #connect-steps li {
1528
+ margin-bottom: 18px;
1529
+ }
1530
+ .step-title {
1531
+ font-weight: 600;
1532
+ font-size: 14.5px;
1533
+ margin-bottom: 8px;
1534
+ }
1535
+ .codeblock {
1536
+ display: flex;
1537
+ align-items: center;
1538
+ gap: 8px;
1539
+ background: #17181c;
1540
+ border-radius: 10px;
1541
+ padding: 11px 12px 11px 15px;
1542
+ }
1543
+ .codeblock code {
1544
+ flex: 1;
1545
+ min-width: 0;
1546
+ overflow-x: auto;
1547
+ white-space: nowrap;
1548
+ font-family: var(--mono);
1549
+ font-size: 13px;
1550
+ color: #f0efff;
1551
+ background: none;
1552
+ padding: 0;
1553
+ }
1554
+ .codeblock .copy {
1555
+ flex: 0 0 auto;
1556
+ background: rgba(255, 255, 255, 0.08);
1557
+ color: #c3c4cb;
1558
+ border: 1px solid rgba(255, 255, 255, 0.14);
1559
+ border-radius: 7px;
1560
+ width: 30px;
1561
+ height: 30px;
1562
+ font-size: 14px;
1563
+ cursor: pointer;
1564
+ transition: background 0.12s, color 0.12s;
1565
+ }
1566
+ .codeblock .copy:hover {
1567
+ background: rgba(249, 115, 22, 0.2);
1568
+ color: #fdba74;
1569
+ }
1570
+ .codeblock .copy.copied {
1571
+ color: #52d08a;
1572
+ }
1573
+
1574
+ @media (max-width: 720px) {
1575
+ #app {
1576
+ flex-direction: column;
1577
+ }
1578
+ #sidebar {
1579
+ width: 100%;
1580
+ flex: none;
1581
+ height: auto;
1582
+ position: static;
1583
+ }
1584
+ #content {
1585
+ display: block;
1586
+ width: 100%;
1587
+ padding: 28px 20px 80px;
1588
+ overflow-x: hidden;
1589
+ }
1590
+ #page {
1591
+ width: 100%;
1592
+ max-width: 100%;
1593
+ }
1594
+ #page h1 {
1595
+ font-size: 30px;
1596
+ }
1597
+ .cell-head {
1598
+ align-items: flex-start;
1599
+ flex-direction: column;
1600
+ gap: 4px;
1601
+ }
1602
+ }
logbook.js ADDED
@@ -0,0 +1,2275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function () {
2
+ "use strict";
3
+
4
+ let MANIFEST = null;
5
+ const PAGE_CACHE = {};
6
+ const UNFURL_CACHE = {};
7
+ const LIVE_RELOAD_MS = 1500;
8
+ const FIGURE_FRAME_WINDOWS = new Set();
9
+ let FIGURE_NAVIGATION_READY = false;
10
+
11
+ function esc(s) {
12
+ return String(s)
13
+ .replace(/&/g, "&amp;")
14
+ .replace(/</g, "&lt;")
15
+ .replace(/>/g, "&gt;")
16
+ .replace(/"/g, "&quot;")
17
+ .replace(/'/g, "&#39;");
18
+ }
19
+
20
+ function flattenTree(node, depth, acc) {
21
+ acc.push({ node: node, depth: depth });
22
+ (node.children || []).forEach((c) => flattenTree(c, depth + 1, acc));
23
+ return acc;
24
+ }
25
+
26
+ function findNode(node, slug) {
27
+ if (node.slug === slug) return node;
28
+ for (const c of node.children || []) {
29
+ const hit = findNode(c, slug);
30
+ if (hit) return hit;
31
+ }
32
+ return null;
33
+ }
34
+
35
+ /* -------------------- minimal markdown -------------------- */
36
+
37
+ function inline(text) {
38
+ let t = esc(text);
39
+ t = t.replace(/`([^`]+)`/g, (_, c) => `<code>${c}</code>`);
40
+ t = t.replace(/\*\*([^*]+)\*\*/g, (_, c) => `<strong>${c}</strong>`);
41
+ t = t.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, txt, url) => {
42
+ const safe = esc(url);
43
+ const attrs = /^https?:/.test(url) ? ' target="_blank" rel="noopener"' : "";
44
+ const item = /^https?:/.test(url) ? classifyResource(url) : null;
45
+ const data = item
46
+ ? ` class="res-link" data-res-url="${esc(item.url)}"`
47
+ : "";
48
+ return `<a href="${safe}"${attrs}${data}>${txt}</a>`;
49
+ });
50
+ t = t.replace(/(^|[\s(])(https?:\/\/[^\s<>)"'`]+)/g, (m, pre, url) => {
51
+ let rest = "";
52
+ const cut = url.search(/&quot;|&#39;|&lt;|&gt;/);
53
+ if (cut !== -1) {
54
+ rest = url.slice(cut);
55
+ url = url.slice(0, cut);
56
+ }
57
+ const trailing = (url.match(/[.,;:!?`]+$/) || [""])[0];
58
+ const clean = trailing ? url.slice(0, -trailing.length) : url;
59
+ if (!clean) return m;
60
+ const item = classifyResource(clean);
61
+ if (item) return `${pre}${resChipHtml(item)}${trailing}${rest}`;
62
+ return `${pre}<a href="${clean}" target="_blank" rel="noopener">${clean}</a>${trailing}${rest}`;
63
+ });
64
+ return t;
65
+ }
66
+
67
+ function resChipHtml(item) {
68
+ return (
69
+ `<a class="res-chip" href="${esc(item.url)}" target="_blank" ` +
70
+ `rel="noopener" data-res-url="${esc(item.url)}">` +
71
+ `<span class="res-chip-ico">${RESOURCE_ICONS[item.kind]}</span>` +
72
+ `${esc(item.id)}</a>`
73
+ );
74
+ }
75
+
76
+ const URL_ONLY = /^(https?:\/\/[^\s]+)$/;
77
+ const DETECTED_URL =
78
+ /(https?:\/\/[^\s<>)\]"'`]+|trackio-local-dashboard:\/\/[^\s<>)\]"'`]+|trackio-artifact:\/\/[^\s<>)\]"'`]+|trackio-local-path:\/\/[^\s<>)\]"'`]+)/g;
79
+
80
+ function renderMarkdown(md, container) {
81
+ const cellRe = /(^|\n)---\n<!-- trackio-cell\n([\s\S]*?)\n-->\n([\s\S]*?)(?=\n---\n<!-- trackio-cell\n|\s*$)/g;
82
+ const tokens = [];
83
+ let pos = 0;
84
+ let found = false;
85
+ let match;
86
+ while ((match = cellRe.exec(md))) {
87
+ found = true;
88
+ tokens.push({
89
+ kind: "md",
90
+ text: md.slice(pos, match.index + match[1].length),
91
+ });
92
+ tokens.push({
93
+ kind: "cell",
94
+ meta: parseCellMeta(match[2]),
95
+ body: match[3],
96
+ });
97
+ pos = match.index + match[0].length;
98
+ }
99
+ tokens.push({ kind: "md", text: found ? md.slice(pos) : md });
100
+
101
+ for (let i = 0; i < tokens.length; i++) {
102
+ const t = tokens[i];
103
+ if (t.kind === "md") {
104
+ renderMarkdownPlain(t.text, container);
105
+ continue;
106
+ }
107
+ if (t.consumed) continue;
108
+ if (t.meta.type === "code") {
109
+ const arts = [];
110
+ for (let j = i + 1; j < tokens.length; j++) {
111
+ const n = tokens[j];
112
+ if (n.kind === "md") {
113
+ if (n.text.trim() === "") continue;
114
+ break;
115
+ }
116
+ if (n.meta.type === "artifact") {
117
+ arts.push(n);
118
+ n.consumed = true;
119
+ continue;
120
+ }
121
+ break;
122
+ }
123
+ renderCell(t.meta, t.body, container, arts);
124
+ } else {
125
+ renderCell(t.meta, t.body, container);
126
+ }
127
+ }
128
+ }
129
+
130
+ function parseCellMeta(raw) {
131
+ try {
132
+ return JSON.parse(raw);
133
+ } catch (e) {
134
+ return { type: "markdown", title: "Note" };
135
+ }
136
+ }
137
+
138
+ function renderMarkdownPlain(md, container) {
139
+ const lines = md.replace(/<!--[\s\S]*?-->/g, "").split("\n");
140
+ let i = 0;
141
+ let para = [];
142
+
143
+ function flushPara() {
144
+ if (!para.length) return;
145
+ const joined = para.join(" ").trim();
146
+ para = [];
147
+ if (!joined) return;
148
+ if (/^trackio-artifact:\/\/\S+$/.test(joined)) return;
149
+ if (/^trackio-local-path:\/\/\S+$/.test(joined)) return;
150
+ if (joined.indexOf("📦 Artifact") !== -1) {
151
+ const div = document.createElement("div");
152
+ div.className = "artifact-chip";
153
+ div.innerHTML = ARTIFACT_ICON_IMG + inline(joined.replace(/📦\s*/, ""));
154
+ container.appendChild(div);
155
+ return;
156
+ }
157
+ if (URL_ONLY.test(joined) || IMG_PATH.test(joined)) {
158
+ const el = renderStandaloneUrl(joined);
159
+ if (el) container.appendChild(el);
160
+ return;
161
+ }
162
+ const p = document.createElement("p");
163
+ p.innerHTML = inline(joined);
164
+ container.appendChild(p);
165
+ }
166
+
167
+ while (i < lines.length) {
168
+ const line = lines[i];
169
+ const trimmed = line.trim();
170
+
171
+ if (trimmed === "") {
172
+ flushPara();
173
+ i++;
174
+ continue;
175
+ }
176
+ const fence = trimmed.match(/^(`{3,}|~{3,})(.*)$/);
177
+ if (fence) {
178
+ flushPara();
179
+ const marker = fence[1][0];
180
+ const closeRe = new RegExp("^" + marker + "{" + fence[1].length + ",}\\s*$");
181
+ const info = fence[2].trim();
182
+ const buf = [];
183
+ i++;
184
+ while (i < lines.length && !closeRe.test(lines[i].trim())) {
185
+ buf.push(lines[i]);
186
+ i++;
187
+ }
188
+ i++;
189
+ const lang = (info.split(/\s+/)[0] || "").toLowerCase();
190
+ const tm = info.match(/title=(\S+)/);
191
+ container.appendChild(
192
+ renderCode(buf.join("\n"), lang, tm ? tm[1] : null)
193
+ );
194
+ continue;
195
+ }
196
+ if (trimmed === "---") {
197
+ flushPara();
198
+ container.appendChild(document.createElement("hr"));
199
+ i++;
200
+ continue;
201
+ }
202
+ const h = trimmed.match(/^(#{1,4})\s+(.*)$/);
203
+ if (h) {
204
+ flushPara();
205
+ const el = document.createElement("h" + h[1].length);
206
+ el.innerHTML = inline(h[2]);
207
+ container.appendChild(el);
208
+ i++;
209
+ continue;
210
+ }
211
+ if (
212
+ trimmed.startsWith("|") &&
213
+ i + 1 < lines.length &&
214
+ /^\|?[\s:|-]*-{2,}[\s:|-]*\|?$/.test(lines[i + 1].trim())
215
+ ) {
216
+ flushPara();
217
+ const rows = [];
218
+ while (i < lines.length && lines[i].trim().startsWith("|")) {
219
+ rows.push(parseRow(lines[i].trim()));
220
+ i++;
221
+ }
222
+ renderTable(rows, container);
223
+ continue;
224
+ }
225
+ if (trimmed.startsWith("> ")) {
226
+ flushPara();
227
+ const bq = document.createElement("blockquote");
228
+ bq.innerHTML = inline(trimmed.slice(2));
229
+ container.appendChild(bq);
230
+ i++;
231
+ continue;
232
+ }
233
+ if (/^`[^`]+`$/.test(trimmed)) {
234
+ flushPara();
235
+ const el = document.createElement("div");
236
+ el.className = "ts";
237
+ el.textContent = trimmed.replace(/`/g, "");
238
+ container.appendChild(el);
239
+ i++;
240
+ continue;
241
+ }
242
+ if (trimmed.startsWith("- ")) {
243
+ flushPara();
244
+ const items = [];
245
+ while (i < lines.length && lines[i].trim().startsWith("- ")) {
246
+ items.push(lines[i].trim().slice(2).trim());
247
+ i++;
248
+ }
249
+ renderList(items, container);
250
+ continue;
251
+ }
252
+ para.push(trimmed);
253
+ i++;
254
+ }
255
+ flushPara();
256
+ }
257
+
258
+ function renderCell(meta, body, container, artifacts) {
259
+ const cell = document.createElement("section");
260
+ cell.className = `cell ${meta.type || "markdown"}`;
261
+ if (meta.id) cell.dataset.cellId = meta.id;
262
+ if (isPinned(meta)) cell.classList.add("pinned-source");
263
+
264
+ const head = document.createElement("div");
265
+ head.className = "cell-head";
266
+ const rawTitle = (meta.title || "").trim();
267
+ const title = rawTitle && rawTitle.toLowerCase() !== "untitled" ? esc(rawTitle) : "";
268
+ const when = meta.created_at ? `<span>${esc(formatTime(meta.created_at))}</span>` : "";
269
+ head.innerHTML =
270
+ (title ? `<div class="cell-title">${title}</div>` : "") +
271
+ `<div class="cell-meta">${when}</div>`;
272
+ if (!title) head.classList.add("no-title");
273
+ cell.appendChild(head);
274
+
275
+ const bodyEl = document.createElement("div");
276
+ bodyEl.className = "cell-body";
277
+ if (meta.type === "code") {
278
+ renderCodeCell(body, bodyEl, artifacts);
279
+ } else if (meta.type === "figure") {
280
+ cell.dataset.resUrl = `trackio-figure://${(meta.title || "Figure").trim()}`;
281
+ renderFigureCell(body, bodyEl, head);
282
+ } else if (meta.type === "artifact") {
283
+ renderMarkdownPlain(body, bodyEl);
284
+ const chip = bodyEl.querySelector(".artifact-chip");
285
+ const uri = body.match(
286
+ /(trackio-artifact:\/\/\S+|trackio-local-path:\/\/\S+|https:\/\/huggingface\.co\/buckets\/[^\s<)]+#\S+)/
287
+ );
288
+ if (chip && uri) chip.dataset.resUrl = uri[1];
289
+ } else if (meta.type === "dashboard") {
290
+ const sp = body.match(/https:\/\/huggingface\.co\/spaces\/[^\s<>)"'`]+/);
291
+ cell.dataset.resUrl = sp
292
+ ? sp[0]
293
+ : `trackio-local-dashboard://${(meta.dashboard_project || "").trim()}`;
294
+ renderDashboardCell(meta, body, bodyEl, head);
295
+ } else {
296
+ const cleaned = stripDuplicateTitle(body, meta.title);
297
+ renderMarkdownPlain(cleaned, bodyEl);
298
+ renderDetectedEmbeds(cleaned, bodyEl);
299
+ }
300
+ cell.appendChild(bodyEl);
301
+ container.appendChild(cell);
302
+ return cell;
303
+ }
304
+
305
+ function isPinned(meta) {
306
+ return Boolean(meta && (meta.pinned === true || meta.pinned === "true"));
307
+ }
308
+
309
+ function stripDuplicateTitle(body, title) {
310
+ if (!title) return body;
311
+ const m = body.match(/^\s*#{1,6}\s+([^\n]+)\n?/);
312
+ if (!m) return body;
313
+ const norm = (s) =>
314
+ s
315
+ .toLowerCase()
316
+ .replace(/[*_`#]/g, "")
317
+ .replace(/\s+/g, " ")
318
+ .trim();
319
+ return norm(m[1]) === norm(title) ? body.slice(m[0].length) : body;
320
+ }
321
+
322
+ function formatTime(iso) {
323
+ const d = new Date(iso);
324
+ if (Number.isNaN(d.getTime())) return iso;
325
+ return d.toLocaleString(undefined, {
326
+ month: "short",
327
+ day: "numeric",
328
+ hour: "2-digit",
329
+ minute: "2-digit",
330
+ });
331
+ }
332
+
333
+ function parseFences(text) {
334
+ const fenceRe = /(`{3,4}|~{3,4})([^\n]*)\n([\s\S]*?)\n\1/g;
335
+ const parts = [];
336
+ let pos = 0;
337
+ let match;
338
+ while ((match = fenceRe.exec(text))) {
339
+ if (match.index > pos) {
340
+ parts.push({ kind: "text", text: text.slice(pos, match.index) });
341
+ }
342
+ const info = match[2].trim();
343
+ const lang = (info.split(/\s+/)[0] || "").toLowerCase();
344
+ const titleMatch = info.match(/title=(\S+)/);
345
+ parts.push({
346
+ kind: lang === "result" || lang === "output" ? "output" : "code",
347
+ lang,
348
+ title: titleMatch ? titleMatch[1] : null,
349
+ text: match[3],
350
+ });
351
+ pos = match.index + match[0].length;
352
+ }
353
+ if (pos < text.length) parts.push({ kind: "text", text: text.slice(pos) });
354
+ return parts;
355
+ }
356
+
357
+ function fitFigureFrame(frame, wrap) {
358
+ let doc;
359
+ try {
360
+ doc = frame.contentDocument;
361
+ } catch (e) {
362
+ return;
363
+ }
364
+ if (!doc || !doc.body) return;
365
+ frame.style.transform = "none";
366
+ frame.style.width = "100%";
367
+ frame.style.height = "auto";
368
+ frame.style.position = "";
369
+ frame.style.left = "";
370
+ frame.style.top = "";
371
+ const avail = wrap.clientWidth;
372
+ const isFullscreen =
373
+ document.fullscreenElement === wrap ||
374
+ document.webkitFullscreenElement === wrap;
375
+ const availHeight = isFullscreen ? wrap.clientHeight : Infinity;
376
+ const cw = Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth, 1);
377
+ const ch = Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight, 1);
378
+ const scale = Math.min(avail / cw, availHeight / ch);
379
+ if (avail && scale < 1 - 1e-3) {
380
+ frame.style.width = `${cw}px`;
381
+ frame.style.height = `${ch}px`;
382
+ frame.style.transformOrigin = "top left";
383
+ frame.style.transform = `scale(${scale})`;
384
+ if (isFullscreen) {
385
+ frame.style.position = "absolute";
386
+ frame.style.left = `${Math.max(0, (avail - cw * scale) / 2)}px`;
387
+ frame.style.top = `${Math.max(0, (availHeight - ch * scale) / 2)}px`;
388
+ wrap.style.height = "100%";
389
+ } else {
390
+ wrap.style.height = `${Math.ceil(ch * scale)}px`;
391
+ }
392
+ } else {
393
+ frame.style.width = "100%";
394
+ frame.style.height = `${ch}px`;
395
+ wrap.style.height = isFullscreen ? "100%" : `${ch}px`;
396
+ }
397
+ }
398
+
399
+ function attachFigureFit(frame, wrap) {
400
+ const refit = () => fitFigureFrame(frame, wrap);
401
+ frame.addEventListener("load", refit);
402
+ if (window.ResizeObserver) {
403
+ const ro = new ResizeObserver(() => refit());
404
+ ro.observe(wrap);
405
+ }
406
+ }
407
+
408
+ function renderFigureCell(text, container, head) {
409
+ const parts = parseFences(text);
410
+ const htmlPart = parts.find((part) => part.lang === "html");
411
+ const rawPart = parts.find((part) => part.lang === "raw");
412
+ if (!htmlPart || !htmlPart.text.trim()) {
413
+ const empty = document.createElement("p");
414
+ empty.className = "muted";
415
+ empty.textContent = "No figure HTML.";
416
+ container.appendChild(empty);
417
+ return;
418
+ }
419
+ const frame = document.createElement("iframe");
420
+ frame.className = "figure-frame";
421
+ frame.sandbox = "allow-scripts allow-same-origin";
422
+ frame.loading = "lazy";
423
+ frame.srcdoc = htmlPart.text;
424
+ registerFigureNavigation(frame);
425
+ const figWrap = document.createElement("div");
426
+ figWrap.className = "figure-fit";
427
+ figWrap.appendChild(frame);
428
+ attachFigureFit(frame, figWrap);
429
+ if (head) {
430
+ const metaEl = head.querySelector(".cell-meta");
431
+ if (metaEl)
432
+ metaEl.insertBefore(buildFullscreenControl(figWrap, frame), metaEl.firstChild);
433
+ }
434
+ if (!rawPart || !rawPart.text.trim()) {
435
+ container.appendChild(figWrap);
436
+ return;
437
+ }
438
+ const sw = document.createElement("div");
439
+ sw.className = "fig-switch";
440
+ const thumb = document.createElement("span");
441
+ thumb.className = "fig-switch-thumb";
442
+ const figBtn = document.createElement("button");
443
+ figBtn.type = "button";
444
+ figBtn.className = "active";
445
+ figBtn.textContent = "Figure";
446
+ const rawBtn = document.createElement("button");
447
+ rawBtn.type = "button";
448
+ rawBtn.textContent = "Raw";
449
+ sw.appendChild(thumb);
450
+ sw.appendChild(figBtn);
451
+ sw.appendChild(rawBtn);
452
+ const rawView = document.createElement("div");
453
+ rawView.className = "figure-raw";
454
+ rawView.hidden = true;
455
+ const pre = document.createElement("pre");
456
+ const code = document.createElement("code");
457
+ code.textContent = rawPart.text;
458
+ pre.appendChild(code);
459
+ rawView.appendChild(pre);
460
+ rawView.appendChild(copySnippetBtn(rawPart.text));
461
+ const select = (showRaw) => {
462
+ sw.classList.toggle("raw", showRaw);
463
+ figBtn.classList.toggle("active", !showRaw);
464
+ rawBtn.classList.toggle("active", showRaw);
465
+ figWrap.hidden = showRaw;
466
+ rawView.hidden = !showRaw;
467
+ };
468
+ figBtn.addEventListener("click", () => select(false));
469
+ rawBtn.addEventListener("click", () => select(true));
470
+ if (head) {
471
+ head.insertBefore(sw, head.querySelector(".cell-meta"));
472
+ } else {
473
+ container.appendChild(sw);
474
+ }
475
+ container.appendChild(figWrap);
476
+ container.appendChild(rawView);
477
+ }
478
+
479
+ // Poster embeds can send `{ type: "trackio-logbook:navigate", target: "..." }`
480
+ // from their iframe. Only accept messages from figure frames we created, and
481
+ // only route to pages that are present in this logbook's manifest.
482
+ function registerFigureNavigation(frame) {
483
+ const registerFrameWindow = () => {
484
+ if (frame.contentWindow) FIGURE_FRAME_WINDOWS.add(frame.contentWindow);
485
+ };
486
+ // `srcdoc` replaces the initial about:blank document. Register after that
487
+ // navigation as well, so messages come from the live figure document.
488
+ frame.addEventListener("load", registerFrameWindow);
489
+ registerFrameWindow();
490
+ if (FIGURE_NAVIGATION_READY) return;
491
+ FIGURE_NAVIGATION_READY = true;
492
+ window.addEventListener("message", (event) => {
493
+ if (!FIGURE_FRAME_WINDOWS.has(event.source)) return;
494
+ const message = event.data;
495
+ if (!message || message.type !== "trackio-logbook:navigate") return;
496
+ const target = String(message.target || "").replace(/^#?\//, "");
497
+ if (!target || !MANIFEST || !findNode(MANIFEST.root, target)) return;
498
+ const hash = "#/" + target;
499
+ if (location.hash === hash) scrollToHash();
500
+ else location.hash = hash;
501
+ });
502
+ }
503
+
504
+ const FULLSCREEN_ICON =
505
+ '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" ' +
506
+ 'stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
507
+ '<path d="M8 3H3v5M16 3h5v5M21 16v5h-5M3 16v5h5"/>' +
508
+ '<path d="M3 8 8 3M16 3l5 5M21 16l-5 5M8 21l-5-5"/></svg>';
509
+
510
+ // Figures are rendered in same-origin iframes, so fullscreen the fitted
511
+ // wrapper rather than the iframe document. This uses the browser's native
512
+ // fullscreen UI and preserves the figure's existing responsive sizing.
513
+ function buildFullscreenControl(figWrap, frame) {
514
+ const wrap = document.createElement("span");
515
+ wrap.className = "cell-fullscreen";
516
+ const btn = document.createElement("button");
517
+ btn.type = "button";
518
+ btn.className = "cell-fullscreen-btn";
519
+ btn.setAttribute("aria-label", "Open figure in fullscreen");
520
+ btn.title = "Open figure in fullscreen";
521
+ btn.innerHTML = FULLSCREEN_ICON;
522
+ wrap.appendChild(btn);
523
+
524
+ btn.addEventListener("click", async () => {
525
+ const request = figWrap.requestFullscreen || figWrap.webkitRequestFullscreen;
526
+ if (!request) return;
527
+ try {
528
+ await request.call(figWrap);
529
+ } catch (_) {
530
+ // Fullscreen can be disabled by the embedding browser or policy.
531
+ }
532
+ });
533
+ document.addEventListener("fullscreenchange", () => {
534
+ if (document.fullscreenElement === figWrap) fitFigureFrame(frame, figWrap);
535
+ });
536
+ return wrap;
537
+ }
538
+
539
+ function extractUrls(text) {
540
+ const seen = new Set();
541
+ const urls = [];
542
+ let match;
543
+ while ((match = DETECTED_URL.exec(text))) {
544
+ const url = match[1].replace(/[.,;:!?'"`]+$/, "");
545
+ if (!seen.has(url)) {
546
+ seen.add(url);
547
+ urls.push(url);
548
+ }
549
+ }
550
+ DETECTED_URL.lastIndex = 0;
551
+ return urls;
552
+ }
553
+
554
+ const IMG_URL = /(\.(png|jpe?g|gif|svg|webp)(\?|$)|\/artifact_blob\/)/i;
555
+
556
+ function renderDetectedEmbeds(text, container) {
557
+ extractUrls(text).forEach((url) => {
558
+ if (url.startsWith("trackio-local-dashboard://")) {
559
+ const div = document.createElement("div");
560
+ div.className = "artifact-chip";
561
+ div.dataset.resUrl = url;
562
+ div.innerHTML =
563
+ "🎯 <strong>Local Trackio dashboard</strong> — publish the logbook to share it";
564
+ container.appendChild(div);
565
+ } else if (IMG_URL.test(url)) {
566
+ container.appendChild(renderImage(url));
567
+ } else if (/huggingface\.co\/spaces\//.test(url)) {
568
+ maybeEmbedTrackioSpace(url, container);
569
+ }
570
+ });
571
+ }
572
+
573
+ function renderStandaloneUrl(url) {
574
+ if (IMG_URL.test(url) || IMG_PATH.test(url)) return renderImage(url);
575
+ const item = classifyResource(url);
576
+ if (item) {
577
+ const marker = document.createElement("span");
578
+ marker.className = "resource-anchor";
579
+ marker.dataset.resUrl = item.url;
580
+ marker.setAttribute("aria-hidden", "true");
581
+ return marker;
582
+ }
583
+ const p = document.createElement("p");
584
+ p.innerHTML = inline(url);
585
+ return p;
586
+ }
587
+
588
+ function renderImage(url) {
589
+ const a = document.createElement("a");
590
+ a.className = "unfurl image";
591
+ a.href = url;
592
+ a.target = "_blank";
593
+ a.rel = "noopener";
594
+ const img = document.createElement("img");
595
+ img.loading = "lazy";
596
+ img.src = url;
597
+ img.alt = "artifact image";
598
+ a.appendChild(img);
599
+ return a;
600
+ }
601
+
602
+ function maybeEmbedTrackioSpace(url, container) {
603
+ const id = url.split("/spaces/")[1].split(/[?#]/)[0].replace(/\/$/, "");
604
+ const holder = document.createElement("div");
605
+ container.appendChild(holder);
606
+ getJSON(`https://huggingface.co/api/spaces/${id}`).then((d) => {
607
+ const tags = (d && d.tags) || [];
608
+ if (tags.some((t) => String(t).toLowerCase() === "trackio")) {
609
+ renderTrackioSpaceEmbed(holder, url, id);
610
+ } else {
611
+ holder.remove();
612
+ }
613
+ });
614
+ }
615
+
616
+ function jpGutter(label) {
617
+ const g = document.createElement("div");
618
+ g.className = "jp-gutter";
619
+ g.textContent = label;
620
+ return g;
621
+ }
622
+
623
+ function renderOutArtifact(info) {
624
+ const remote = !info.local && !!info.url;
625
+ const el = document.createElement(remote ? "a" : "div");
626
+ el.className = "out-artifact";
627
+ if (remote) {
628
+ el.href = info.url;
629
+ el.target = "_blank";
630
+ el.rel = "noopener";
631
+ }
632
+ el.dataset.resUrl = info.resUrl;
633
+ const parts = [info.type, info.size].filter(Boolean).map(esc);
634
+ const state = remote
635
+ ? `<span class="out-artifact-state open">Open ↗</span>`
636
+ : `<span class="out-artifact-state">publish to share</span>`;
637
+ const meta = parts.length ? `${parts.join(" · ")} · ${state}` : state;
638
+ el.innerHTML =
639
+ `<span class="out-artifact-ico">${ARTIFACT_ICON_IMG}</span>` +
640
+ `<span class="out-artifact-name">${esc(info.name)}</span>` +
641
+ `<span class="out-artifact-meta">${meta}</span>`;
642
+ return el;
643
+ }
644
+
645
+ function renderCodeCell(body, container, artifacts) {
646
+ const parts = parseFences(body);
647
+ const block = document.createElement("div");
648
+ block.className = "jp";
649
+ const input = document.createElement("div");
650
+ input.className = "jp-in";
651
+ const inputBody = document.createElement("div");
652
+ inputBody.className = "jp-in-body";
653
+ input.appendChild(jpGutter("In"));
654
+ input.appendChild(inputBody);
655
+ let metaEl = null;
656
+ let outputEl = null;
657
+ let outBody = null;
658
+ const ensureOut = () => {
659
+ if (outputEl) return;
660
+ outputEl = document.createElement("div");
661
+ outputEl.className = "jp-out";
662
+ outputEl.appendChild(jpGutter("Out"));
663
+ outBody = document.createElement("div");
664
+ outBody.className = "jp-out-body";
665
+ outputEl.appendChild(outBody);
666
+ };
667
+ const embedTexts = [];
668
+ parts.forEach((part) => {
669
+ if (part.kind === "text") {
670
+ const text = part.text.trim();
671
+ if (!text) return;
672
+ if (/^exit\s+\S+(\s|·)/.test(text)) {
673
+ metaEl = document.createElement("div");
674
+ metaEl.className = "jp-meta";
675
+ metaEl.textContent = text.replace(
676
+ /\s*·\s*[A-Z][a-z]{2} \d{1,2}, \d{4}.*$/,
677
+ ""
678
+ );
679
+ } else {
680
+ renderMarkdownPlain(text, container);
681
+ embedTexts.push(text);
682
+ }
683
+ return;
684
+ }
685
+ if (part.kind === "output") {
686
+ ensureOut();
687
+ const pre = document.createElement("pre");
688
+ pre.className = "jp-out-pre";
689
+ const c = document.createElement("code");
690
+ c.textContent = part.text;
691
+ pre.appendChild(c);
692
+ outBody.appendChild(pre);
693
+ outputEl.appendChild(copySnippetBtn(part.text));
694
+ embedTexts.push(part.text);
695
+ return;
696
+ }
697
+ inputBody.appendChild(renderCode(part.text, part.lang, part.title));
698
+ });
699
+ if (artifacts && artifacts.length) {
700
+ ensureOut();
701
+ const artWrap = document.createElement("div");
702
+ artWrap.className = "jp-artifacts";
703
+ artifacts.forEach((a) => {
704
+ artWrap.appendChild(
705
+ renderOutArtifact(artifactInfoFromCell(a.meta, a.body))
706
+ );
707
+ });
708
+ outBody.appendChild(artWrap);
709
+ }
710
+ if (inputBody.childNodes.length > 0) block.appendChild(input);
711
+ if (metaEl) block.appendChild(metaEl);
712
+ if (outputEl) block.appendChild(outputEl);
713
+ if (block.childNodes.length) container.appendChild(block);
714
+ embedTexts.forEach((text) => renderDetectedEmbeds(text, container));
715
+ }
716
+
717
+ function parseRow(line) {
718
+ let s = line.trim();
719
+ if (s.startsWith("|")) s = s.slice(1);
720
+ if (s.endsWith("|")) s = s.slice(0, -1);
721
+ return s.split(/(?<!\\)\|/).map((c) => c.replace(/\\\|/g, "|").trim());
722
+ }
723
+
724
+ const TRUTHY = ["x", "✓", "✔", "yes", "done", "true", "[x]"];
725
+ const CHIP_COLORS = [
726
+ ["#e7f0ff", "#2158d0"],
727
+ ["#fde8ec", "#c62a4b"],
728
+ ["#e6f7ee", "#1a8a55"],
729
+ ["#fdf0e0", "#b26a12"],
730
+ ["#efe9ff", "#5b3bd6"],
731
+ ["#e6f6f8", "#127b88"],
732
+ ];
733
+
734
+ function chipColor(name) {
735
+ let h = 0;
736
+ for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0;
737
+ return CHIP_COLORS[h % CHIP_COLORS.length];
738
+ }
739
+
740
+ const STATUS_MAP = {
741
+ "": ["Planned", "gray"],
742
+ planned: ["Planned", "gray"],
743
+ todo: ["Planned", "gray"],
744
+ "to do": ["Planned", "gray"],
745
+ backlog: ["Planned", "gray"],
746
+ "in progress": ["In progress", "amber"],
747
+ "in-progress": ["In progress", "amber"],
748
+ wip: ["In progress", "amber"],
749
+ running: ["In progress", "amber"],
750
+ active: ["In progress", "amber"],
751
+ done: ["Done", "green"],
752
+ complete: ["Done", "green"],
753
+ completed: ["Done", "green"],
754
+ blocked: ["Blocked", "red"],
755
+ failed: ["Failed", "red"],
756
+ abandoned: ["Abandoned", "gray"],
757
+ };
758
+
759
+ function statusBadge(val) {
760
+ const [label, tone] = STATUS_MAP[val.toLowerCase()] || [val || "—", "gray"];
761
+ return `<span class="badge ${tone}">${esc(label)}</span>`;
762
+ }
763
+
764
+ function renderTable(rows, container) {
765
+ if (rows.length < 2) return;
766
+ const header = rows[0];
767
+ const body = rows.slice(2);
768
+ const roles = header.map((h) => {
769
+ const t = h.toLowerCase();
770
+ if (t.includes("status") || t.includes("state")) return "status";
771
+ if (t.includes("progress") || t.includes("complete") || t.includes("done"))
772
+ return "check";
773
+ if (t === "who" || t.includes("assign") || t.includes("owner")) return "who";
774
+ return "text";
775
+ });
776
+ const table = document.createElement("table");
777
+ table.className = "board";
778
+ const thead = document.createElement("thead");
779
+ const htr = document.createElement("tr");
780
+ header.forEach((h, c) => {
781
+ const th = document.createElement("th");
782
+ th.textContent = h;
783
+ if (roles[c] === "check") th.className = "col-check";
784
+ htr.appendChild(th);
785
+ });
786
+ thead.appendChild(htr);
787
+ table.appendChild(thead);
788
+ const tbody = document.createElement("tbody");
789
+ body.forEach((cells) => {
790
+ const nonEmpty = cells.filter((x) => x !== "").length;
791
+ if (header.length > 1 && nonEmpty === 1 && cells[0]) {
792
+ const tr = document.createElement("tr");
793
+ tr.className = "section-row";
794
+ const td = document.createElement("td");
795
+ td.colSpan = header.length;
796
+ td.innerHTML = inline(cells[0]);
797
+ tr.appendChild(td);
798
+ tbody.appendChild(tr);
799
+ return;
800
+ }
801
+ const tr = document.createElement("tr");
802
+ header.forEach((_, c) => {
803
+ const td = document.createElement("td");
804
+ const val = (cells[c] || "").trim();
805
+ if (roles[c] === "status") {
806
+ td.className = "col-status";
807
+ td.innerHTML = statusBadge(val);
808
+ } else if (roles[c] === "check") {
809
+ td.className = "col-check";
810
+ const on = TRUTHY.indexOf(val.toLowerCase()) !== -1;
811
+ td.innerHTML = `<span class="box ${on ? "on" : ""}">${on ? "✓" : ""}</span>`;
812
+ } else if (roles[c] === "who") {
813
+ if (!val || /^to assign$/i.test(val)) {
814
+ td.innerHTML = `<span class="who-chip muted">${esc(val || "—")}</span>`;
815
+ } else {
816
+ const [bg, fg] = chipColor(val);
817
+ td.innerHTML = `<span class="who-chip" style="background:${bg};color:${fg}">${esc(val)}</span>`;
818
+ }
819
+ } else {
820
+ td.innerHTML = inline(val);
821
+ }
822
+ tr.appendChild(td);
823
+ });
824
+ const link = tr.querySelector('a[href^="#/"]');
825
+ if (link) {
826
+ tr.classList.add("linked-row");
827
+ tr.addEventListener("click", (e) => {
828
+ if (e.target.tagName !== "A") location.hash = link.getAttribute("href");
829
+ });
830
+ }
831
+ tbody.appendChild(tr);
832
+ });
833
+ table.appendChild(tbody);
834
+ const wrap = document.createElement("div");
835
+ wrap.className = "board-wrap";
836
+ wrap.appendChild(table);
837
+ container.appendChild(wrap);
838
+ }
839
+
840
+ const HL_RULES = {
841
+ python: [
842
+ ["comment", /#[^\n]*/],
843
+ ["string", /'''[\s\S]*?'''|"""[\s\S]*?"""|'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"/],
844
+ [
845
+ "keyword",
846
+ /\b(?:def|class|return|if|elif|else|for|while|import|from|as|with|try|except|finally|raise|in|not|and|or|is|None|True|False|lambda|yield|global|nonlocal|assert|pass|break|continue|async|await|print)\b/,
847
+ ],
848
+ ["number", /\b\d[\d_.eE+-]*\b/],
849
+ ],
850
+ bash: [
851
+ ["comment", /#[^\n]*/],
852
+ ["string", /'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"/],
853
+ ["keyword", /\b(?:if|then|else|fi|for|in|do|done|while|case|esac|function|export|source|echo|cd|return|local)\b/],
854
+ ["number", /(?<=\s)-{1,2}[a-zA-Z][\w-]*/],
855
+ ],
856
+ json: [
857
+ ["string", /"(?:\\.|[^"\\])*"/],
858
+ ["keyword", /\b(?:true|false|null)\b/],
859
+ ["number", /-?\b\d[\d.eE+-]*\b/],
860
+ ],
861
+ yaml: [
862
+ ["comment", /#[^\n]*/],
863
+ ["string", /'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"/],
864
+ ["keyword", /\b(?:true|false|null|yes|no)\b/],
865
+ ["number", /-?\b\d[\d.eE+-]*\b/],
866
+ ],
867
+ };
868
+ HL_RULES.javascript = HL_RULES.python;
869
+ HL_RULES.typescript = HL_RULES.python;
870
+ HL_RULES.sql = [
871
+ ["comment", /--[^\n]*/],
872
+ ["string", /'(?:\\.|[^'\\])*'/],
873
+ [
874
+ "keyword",
875
+ /\b(?:SELECT|FROM|WHERE|JOIN|LEFT|RIGHT|INNER|OUTER|ON|GROUP|BY|ORDER|LIMIT|INSERT|INTO|VALUES|UPDATE|SET|DELETE|CREATE|TABLE|AS|AND|OR|NOT|NULL|COUNT|DISTINCT|IN)\b/i,
876
+ ],
877
+ ["number", /\b\d[\d.]*\b/],
878
+ ];
879
+
880
+ function highlightCode(code, lang) {
881
+ const rules = HL_RULES[lang];
882
+ if (!rules) return esc(code);
883
+ const combined = new RegExp(rules.map((r) => "(" + r[1].source + ")").join("|"), "g");
884
+ let out = "";
885
+ let last = 0;
886
+ let m;
887
+ while ((m = combined.exec(code))) {
888
+ if (m[0] === "") {
889
+ combined.lastIndex++;
890
+ continue;
891
+ }
892
+ out += esc(code.slice(last, m.index));
893
+ let gi = 1;
894
+ while (gi < m.length && m[gi] === undefined) gi++;
895
+ out += `<span class="tok-${rules[gi - 1][0]}">${esc(m[0])}</span>`;
896
+ last = m.index + m[0].length;
897
+ }
898
+ out += esc(code.slice(last));
899
+ return out;
900
+ }
901
+
902
+ function copySnippetBtn(text) {
903
+ const btn = document.createElement("button");
904
+ btn.type = "button";
905
+ btn.className = "copy-snippet";
906
+ btn.title = "Copy";
907
+ btn.textContent = "⧉";
908
+ btn.addEventListener("click", (e) => {
909
+ e.preventDefault();
910
+ e.stopPropagation();
911
+ copyText(text, btn, "⧉");
912
+ });
913
+ return btn;
914
+ }
915
+
916
+ function renderCode(code, lang, title) {
917
+ const pre = document.createElement("pre");
918
+ pre.className = "hl";
919
+ const c = document.createElement("code");
920
+ c.innerHTML = highlightCode(code, lang);
921
+ pre.appendChild(c);
922
+ if (!title) {
923
+ const wrap = document.createElement("div");
924
+ wrap.className = "snippet";
925
+ wrap.appendChild(pre);
926
+ wrap.appendChild(copySnippetBtn(code));
927
+ return wrap;
928
+ }
929
+ const det = document.createElement("details");
930
+ det.className = "code-accordion";
931
+ det.dataset.resUrl = `trackio-script://${title}`;
932
+ const sum = document.createElement("summary");
933
+ sum.innerHTML =
934
+ `<span class="code-ico">&lt;/&gt;</span>` +
935
+ `<span class="code-name">${esc(title)}</span>`;
936
+ sum
937
+ .querySelector(".code-name")
938
+ .addEventListener("click", (e) => e.preventDefault());
939
+ det.appendChild(sum);
940
+ const wrap = document.createElement("div");
941
+ wrap.className = "snippet";
942
+ wrap.appendChild(pre);
943
+ wrap.appendChild(copySnippetBtn(code));
944
+ det.appendChild(wrap);
945
+ return det;
946
+ }
947
+
948
+ const IMG_PATH = /^[^\s]+\.(png|jpe?g|gif|svg|webp)$/i;
949
+
950
+ function renderList(items, container) {
951
+ let ul = null;
952
+ items.forEach((item) => {
953
+ if (URL_ONLY.test(item) || IMG_PATH.test(item)) {
954
+ const el = renderStandaloneUrl(item);
955
+ if (el) {
956
+ ul = null;
957
+ container.appendChild(el);
958
+ }
959
+ } else if (item.indexOf("📦 Artifact") !== -1) {
960
+ ul = null;
961
+ const div = document.createElement("div");
962
+ div.className = "artifact-chip";
963
+ div.innerHTML = inline(item.replace("📦", "🪣"));
964
+ container.appendChild(div);
965
+ } else if (item.indexOf("trackio-local-dashboard://") !== -1) {
966
+ ul = null;
967
+ const uri = item.match(/trackio-local-dashboard:\/\/\S+/)?.[0] || "";
968
+ const div = document.createElement("div");
969
+ div.className = "artifact-chip";
970
+ if (uri) div.dataset.resUrl = uri;
971
+ div.innerHTML =
972
+ "🎯 <strong>Local dashboard</strong> — publish the logbook to share it";
973
+ container.appendChild(div);
974
+ } else {
975
+ if (!ul) {
976
+ ul = document.createElement("ul");
977
+ container.appendChild(ul);
978
+ }
979
+ const li = document.createElement("li");
980
+ li.innerHTML = inline(item);
981
+ ul.appendChild(li);
982
+ }
983
+ });
984
+ }
985
+
986
+ /* -------------------- resources rail -------------------- */
987
+
988
+ function fmt(n) {
989
+ if (n == null) return null;
990
+ if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
991
+ if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
992
+ return String(n);
993
+ }
994
+
995
+ const RESOURCE_SECTIONS = [
996
+ ["dashboard", "Dashboards", "🎯"],
997
+ ["model", "Models", "🤗"],
998
+ ["dataset", "Datasets", "📊"],
999
+ ["space", "Spaces", "🚀"],
1000
+ ["artifact", "Artifacts", "🪣"],
1001
+ ["paper", "Papers", "📄"],
1002
+ ["repo", "Code", "🐙"],
1003
+ ["job", "Jobs", "⚙️"],
1004
+ ["bucket", "Buckets", "🪣"],
1005
+ ];
1006
+
1007
+ const RESOURCE_ICONS = Object.fromEntries(
1008
+ RESOURCE_SECTIONS.map(([kind, , icon]) => [kind, icon])
1009
+ );
1010
+
1011
+ const ARTIFACT_ICON_IMG = `<img class="art-ico" src="./bucket-icon.svg" alt="" />`;
1012
+ const DASHBOARD_ICON_IMG = `<img class="art-ico" src="./trackio-logo-light.png" alt="" />`;
1013
+
1014
+ const RESOURCE_DESC = {
1015
+ dashboard: "Dashboard",
1016
+ model: "Model",
1017
+ dataset: "Dataset",
1018
+ space: "Space",
1019
+ artifact: "Artifact — in Bucket",
1020
+ paper: "Paper",
1021
+ repo: "Repository",
1022
+ job: "Job — status & logs",
1023
+ bucket: "Bucket — artifacts & data",
1024
+ };
1025
+
1026
+ const HF_NON_MODEL_PREFIX =
1027
+ /^(datasets|spaces|jobs|buckets|papers|blog|docs|api|posts|collections|organizations|settings|new|join|login|pricing|tasks|learn|chat|models)(\/|$)/;
1028
+
1029
+ function hfId(url, marker) {
1030
+ return url.split(marker)[1].split(/[?#]/)[0].replace(/\/$/, "");
1031
+ }
1032
+
1033
+ function classifyResource(url) {
1034
+ if (IMG_URL.test(url)) {
1035
+ return null;
1036
+ }
1037
+ let m;
1038
+ if (url.startsWith("trackio-local-dashboard://")) {
1039
+ return {
1040
+ kind: "dashboard",
1041
+ id: url.slice("trackio-local-dashboard://".length),
1042
+ url,
1043
+ local: true,
1044
+ };
1045
+ }
1046
+ if (url.startsWith("trackio-artifact://")) {
1047
+ return {
1048
+ kind: "artifact",
1049
+ id: url.slice("trackio-artifact://".length),
1050
+ url,
1051
+ local: true,
1052
+ };
1053
+ }
1054
+ if (url.startsWith("trackio-local-path://")) {
1055
+ return {
1056
+ kind: "artifact",
1057
+ id: url.slice("trackio-local-path://".length),
1058
+ url,
1059
+ local: true,
1060
+ };
1061
+ }
1062
+ if ((m = url.match(/huggingface\.co\/buckets\/[^#\s]+#(.+)/))) {
1063
+ return { kind: "artifact", id: decodeURIComponent(m[1]), url };
1064
+ }
1065
+ if (/huggingface\.co\/datasets\/[^/]+\/[^/]+/.test(url)) {
1066
+ return { kind: "dataset", id: hfId(url, "/datasets/"), url };
1067
+ }
1068
+ if (/huggingface\.co\/spaces\/[^/]+\/[^/]+/.test(url)) {
1069
+ return { kind: "space", id: hfId(url, "/spaces/"), url };
1070
+ }
1071
+ if (/huggingface\.co\/jobs\//.test(url)) {
1072
+ const parts = hfId(url, "/jobs/").split("/");
1073
+ const jid = parts[1] || "";
1074
+ return {
1075
+ kind: "job",
1076
+ id: parts[0] + (jid ? ` · ${jid.slice(0, 12)}${jid.length > 12 ? "…" : ""}` : ""),
1077
+ url,
1078
+ };
1079
+ }
1080
+ if (/huggingface\.co\/buckets\//.test(url)) {
1081
+ return { kind: "bucket", id: hfId(url, "/buckets/"), url };
1082
+ }
1083
+ if (/huggingface\.co\/papers\//.test(url)) {
1084
+ return { kind: "paper", id: `Paper ${hfId(url, "/papers/")}`, url };
1085
+ }
1086
+ if ((m = url.match(/arxiv\.org\/(?:abs|pdf)\/([^?#\s]+)/))) {
1087
+ return { kind: "paper", id: `arXiv:${m[1].replace(/\.pdf$/, "")}`, url };
1088
+ }
1089
+ if ((m = url.match(/github\.com\/([^/?#]+\/[^/?#]+)/))) {
1090
+ return { kind: "repo", id: m[1], url };
1091
+ }
1092
+ if ((m = url.match(/huggingface\.co\/([^?#]+)/))) {
1093
+ const rest = m[1].replace(/\/$/, "");
1094
+ if (/^[^/]+\/[^/]+$/.test(rest) && !HF_NON_MODEL_PREFIX.test(rest)) {
1095
+ return { kind: "model", id: rest, url };
1096
+ }
1097
+ }
1098
+ return null;
1099
+ }
1100
+
1101
+ async function fillRailMeta(item, el) {
1102
+ if (item.local) return;
1103
+ const meta = el.querySelector(".rail-meta");
1104
+ const set = (parts) => {
1105
+ const text = parts.filter(Boolean).join(" · ");
1106
+ if (text) meta.textContent = text;
1107
+ };
1108
+ if (item.kind === "model") {
1109
+ const d = await getJSON(`https://huggingface.co/api/models/${item.id}`);
1110
+ if (d) set([d.pipeline_tag, `↓ ${fmt(d.downloads)}`, `♥ ${fmt(d.likes)}`]);
1111
+ } else if (item.kind === "dataset") {
1112
+ const d = await getJSON(`https://huggingface.co/api/datasets/${item.id}`);
1113
+ if (d) set([`↓ ${fmt(d.downloads)}`, `♥ ${fmt(d.likes)}`]);
1114
+ } else if (item.kind === "space" || item.kind === "dashboard") {
1115
+ const d = await getJSON(`https://huggingface.co/api/spaces/${item.id}`);
1116
+ if (d) set([d.sdk, `♥ ${fmt(d.likes)}`]);
1117
+ } else if (item.kind === "repo") {
1118
+ const d = await getJSON(`https://api.github.com/repos/${item.id}`);
1119
+ if (d) set([`★ ${fmt(d.stargazers_count)}`, d.language]);
1120
+ } else if (item.kind === "paper") {
1121
+ const m = item.id.match(/^(?:arXiv:|Paper )(.+)$/);
1122
+ if (!m) return;
1123
+ const arxivId = m[1].replace(/v\d+$/, "");
1124
+ const d = await getJSON(`https://huggingface.co/api/papers/${arxivId}`);
1125
+ if (d && d.id) {
1126
+ if (el.href) el.href = `https://huggingface.co/papers/${d.id}`;
1127
+ const title =
1128
+ d.title && d.title.length > 70 ? `${d.title.slice(0, 69)}…` : d.title;
1129
+ set([title, d.upvotes ? `▲ ${fmt(d.upvotes)}` : null]);
1130
+ }
1131
+ }
1132
+ }
1133
+
1134
+ const BARE_ID_SKIP_DIRS = new Set([
1135
+ "scripts",
1136
+ "configs",
1137
+ "config",
1138
+ "results",
1139
+ "figures",
1140
+ "data",
1141
+ "datasets",
1142
+ "src",
1143
+ "tests",
1144
+ "test",
1145
+ "examples",
1146
+ "pages",
1147
+ "assets",
1148
+ "docs",
1149
+ "outputs",
1150
+ "output",
1151
+ "checkpoints",
1152
+ "models",
1153
+ "utils",
1154
+ "lib",
1155
+ "bin",
1156
+ "tmp",
1157
+ "node_modules",
1158
+ "dist",
1159
+ "build",
1160
+ ]);
1161
+ const FILE_EXT_RE =
1162
+ /\.(py|pyc|js|ts|jsx|tsx|json|jsonl|yaml|yml|csv|tsv|md|txt|sh|bash|html|css|png|jpe?g|svg|gif|webp|ipynb|toml|cfg|ini|lock|pdf|whl|gz|zip|tar|pt|pth|bin|safetensors|db|sqlite)$/i;
1163
+
1164
+ async function detectBareModelIds(text, groups) {
1165
+ const stripped = text.replace(DETECTED_URL, " ");
1166
+ DETECTED_URL.lastIndex = 0;
1167
+ const seen = new Set();
1168
+ const candidates = [];
1169
+ const re = /(^|[\s"'`(=[])([A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*)/g;
1170
+ let m;
1171
+ while ((m = re.exec(stripped)) && candidates.length < 15) {
1172
+ const id = m[2].replace(/[.:,]+$/, "");
1173
+ if (seen.has(id)) continue;
1174
+ seen.add(id);
1175
+ if (FILE_EXT_RE.test(id)) continue;
1176
+ if (BARE_ID_SKIP_DIRS.has(id.split("/")[0].toLowerCase())) continue;
1177
+ candidates.push(id);
1178
+ }
1179
+ const results = await Promise.all(
1180
+ candidates.map((id) => getJSON(`https://huggingface.co/api/models/${id}`))
1181
+ );
1182
+ let added = false;
1183
+ const confirmed = [];
1184
+ results.forEach((d, i) => {
1185
+ if (!d || !d.id) return;
1186
+ const id = candidates[i];
1187
+ confirmed.push(id);
1188
+ const url = `https://huggingface.co/${id}`;
1189
+ if (!groups.has("model")) groups.set("model", new Map());
1190
+ if (!groups.get("model").has(url)) {
1191
+ groups.get("model").set(url, { kind: "model", id, url });
1192
+ added = true;
1193
+ }
1194
+ });
1195
+ return { added, confirmed };
1196
+ }
1197
+
1198
+ function chipifyBareIds(ids, container) {
1199
+ if (!ids.length) return;
1200
+ const escaped = ids.map((id) => id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
1201
+ const pattern = new RegExp("(" + escaped.join("|") + ")");
1202
+ const splitter = new RegExp(pattern.source, "g");
1203
+ container
1204
+ .querySelectorAll(".cell.markdown .cell-body")
1205
+ .forEach((body) => {
1206
+ const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, {
1207
+ acceptNode(node) {
1208
+ if (!pattern.test(node.nodeValue)) return NodeFilter.FILTER_REJECT;
1209
+ for (
1210
+ let el = node.parentElement;
1211
+ el && el !== body;
1212
+ el = el.parentElement
1213
+ ) {
1214
+ if (["A", "CODE", "PRE", "BUTTON"].indexOf(el.tagName) !== -1) {
1215
+ return NodeFilter.FILTER_REJECT;
1216
+ }
1217
+ }
1218
+ return NodeFilter.FILTER_ACCEPT;
1219
+ },
1220
+ });
1221
+ const nodes = [];
1222
+ while (walker.nextNode()) nodes.push(walker.currentNode);
1223
+ nodes.forEach((node) => {
1224
+ const frag = document.createDocumentFragment();
1225
+ node.nodeValue.split(splitter).forEach((part) => {
1226
+ if (ids.indexOf(part) !== -1) {
1227
+ const holder = document.createElement("span");
1228
+ holder.innerHTML = resChipHtml({
1229
+ kind: "model",
1230
+ id: part,
1231
+ url: `https://huggingface.co/${part}`,
1232
+ });
1233
+ frag.appendChild(holder.firstChild);
1234
+ } else if (part) {
1235
+ frag.appendChild(document.createTextNode(part));
1236
+ }
1237
+ });
1238
+ node.parentNode.replaceChild(frag, node);
1239
+ });
1240
+ });
1241
+ }
1242
+
1243
+ let RAIL_TOKEN = 0;
1244
+ const RAIL_EXCLUDE_KINDS = new Set(["paper", "repo", "artifact", "dashboard"]);
1245
+
1246
+ function railDashboardItem(it) {
1247
+ return {
1248
+ kind: "dashboard",
1249
+ id: it.id,
1250
+ url: it.local ? it.resUrl : it.url || it.resUrl,
1251
+ local: it.local,
1252
+ railLabel: "Dashboard",
1253
+ };
1254
+ }
1255
+
1256
+ function promoteTrackioSpacesInRail(groups, dashResUrls, body, rail, token) {
1257
+ const spaceGroup = groups.get("space");
1258
+ if (!spaceGroup || !spaceGroup.size) return;
1259
+ spaceGroup.forEach((item, url) => {
1260
+ getJSON(`https://huggingface.co/api/spaces/${item.id}`)
1261
+ .then((d) => {
1262
+ if (rail.dataset.renderToken !== token) return;
1263
+ const tags = (d && d.tags) || [];
1264
+ if (!tags.some((t) => String(t).toLowerCase() === "trackio")) return;
1265
+ if (dashResUrls.has(url)) return;
1266
+ spaceGroup.delete(url);
1267
+ if (!spaceGroup.size) groups.delete("space");
1268
+ if (!groups.has("dashboard")) groups.set("dashboard", new Map());
1269
+ groups.get("dashboard").set(url, {
1270
+ kind: "dashboard",
1271
+ id: item.id,
1272
+ url: item.url,
1273
+ local: false,
1274
+ railLabel: "Dashboard",
1275
+ });
1276
+ dashResUrls.add(url);
1277
+ paintRail(groups, body, rail);
1278
+ })
1279
+ .catch(() => {});
1280
+ });
1281
+ }
1282
+
1283
+ function renderRail(md, body, rail) {
1284
+ const token = String(++RAIL_TOKEN);
1285
+ rail.dataset.renderToken = token;
1286
+ const scanText = md.replace(
1287
+ /(`{3,4}|~{3,4})(html|raw)[^\n]*\n[\s\S]*?\n\1/g,
1288
+ " "
1289
+ );
1290
+ const groups = new Map();
1291
+ const dashMap = new Map();
1292
+ const dashResUrls = new Set();
1293
+ cellDashboardItems(md).forEach((it) => {
1294
+ if (dashMap.has(it.resUrl)) return;
1295
+ dashMap.set(it.resUrl, railDashboardItem(it));
1296
+ dashResUrls.add(it.resUrl);
1297
+ });
1298
+ if (dashMap.size) groups.set("dashboard", dashMap);
1299
+ extractUrls(scanText).forEach((url) => {
1300
+ const item = classifyResource(url);
1301
+ if (!item) return;
1302
+ if (RAIL_EXCLUDE_KINDS.has(item.kind)) return;
1303
+ if (dashResUrls.has(url)) return;
1304
+ if (!groups.has(item.kind)) groups.set(item.kind, new Map());
1305
+ groups.get(item.kind).set(item.url, item);
1306
+ });
1307
+ const artMap = new Map();
1308
+ cellArtifactItems(md).forEach((it) => {
1309
+ if (artMap.has(it.resUrl)) return;
1310
+ const label = it.type
1311
+ ? it.type.charAt(0).toUpperCase() + it.type.slice(1)
1312
+ : "Artifact";
1313
+ artMap.set(it.resUrl, {
1314
+ kind: "artifact",
1315
+ id: it.name,
1316
+ url: it.local ? it.resUrl : it.url || it.resUrl,
1317
+ local: it.local,
1318
+ railLabel: label,
1319
+ size: it.size,
1320
+ });
1321
+ });
1322
+ if (artMap.size) groups.set("artifact", artMap);
1323
+ paintRail(groups, body, rail);
1324
+ promoteTrackioSpacesInRail(groups, dashResUrls, body, rail, token);
1325
+ detectBareModelIds(scanText, groups)
1326
+ .then((result) => {
1327
+ if (rail.dataset.renderToken !== token) return;
1328
+ chipifyBareIds(result.confirmed, body);
1329
+ if (result.added) paintRail(groups, body, rail);
1330
+ })
1331
+ .catch(() => {});
1332
+ }
1333
+
1334
+ function paintRail(groups, body, rail) {
1335
+ rail.innerHTML = "";
1336
+ RESOURCE_SECTIONS.forEach(([kind, label, icon]) => {
1337
+ const group = groups.get(kind);
1338
+ if (!group || !group.size) return;
1339
+ group.forEach((item) => {
1340
+ const el = document.createElement(item.local ? "div" : "a");
1341
+ el.className = item.local ? "rail-item rail-local" : "rail-item";
1342
+ if (!item.local) {
1343
+ el.href = item.url;
1344
+ el.target = "_blank";
1345
+ el.rel = "noopener";
1346
+ }
1347
+ el.dataset.resUrl = item.url;
1348
+ let desc;
1349
+ if (kind === "artifact") {
1350
+ const state = item.local ? "publish to share" : "Open ↗";
1351
+ desc = item.size ? `${item.size} · ${state}` : state;
1352
+ } else if (kind === "dashboard") {
1353
+ desc = item.local ? "publish to share" : "Open ↗";
1354
+ } else {
1355
+ desc = item.local ? "publish to share" : RESOURCE_DESC[kind];
1356
+ }
1357
+ const kindLabel = item.railLabel || label.replace(/s$/, "");
1358
+ const iconHtml =
1359
+ kind === "artifact"
1360
+ ? ARTIFACT_ICON_IMG
1361
+ : kind === "dashboard"
1362
+ ? DASHBOARD_ICON_IMG
1363
+ : `<span>${icon}</span>`;
1364
+ el.innerHTML =
1365
+ `<div class="rail-kind">${iconHtml}${esc(kindLabel)}</div>` +
1366
+ `<div class="rail-title">${esc(item.id)}</div>` +
1367
+ `<div class="rail-meta">${esc(desc)}</div>`;
1368
+ rail.appendChild(el);
1369
+ fillRailMeta(item, el)
1370
+ .catch(() => {})
1371
+ .finally(() => scheduleRailPosition(body, rail));
1372
+ });
1373
+ });
1374
+ rail.hidden = !rail.childElementCount;
1375
+ scheduleRailPosition(body, rail);
1376
+ }
1377
+
1378
+ function resourceAnchor(body, url) {
1379
+ return body.querySelector(`[data-res-url="${CSS.escape(url)}"]`);
1380
+ }
1381
+
1382
+ function positionRail(body, rail) {
1383
+ if (rail.hidden || !rail.isConnected) return;
1384
+ const bodyRect = body.getBoundingClientRect();
1385
+ const items = Array.from(rail.querySelectorAll(".rail-item")).map((el, index) => {
1386
+ const anchor = resourceAnchor(body, el.dataset.resUrl);
1387
+ return {
1388
+ el,
1389
+ index,
1390
+ desired: anchor
1391
+ ? Math.max(0, anchor.getBoundingClientRect().top - bodyRect.top)
1392
+ : 0,
1393
+ };
1394
+ });
1395
+ items.sort((a, b) => a.desired - b.desired || a.index - b.index);
1396
+ let cursor = 0;
1397
+ items.forEach(({ el, desired }) => {
1398
+ const top = Math.max(desired, cursor);
1399
+ el.style.top = `${top}px`;
1400
+ cursor = top + el.offsetHeight + 10;
1401
+ });
1402
+ rail.style.minHeight = `${Math.max(body.offsetHeight, cursor)}px`;
1403
+ }
1404
+
1405
+ function scheduleRailPosition(body, rail) {
1406
+ cancelAnimationFrame(Number(rail.dataset.positionFrame || 0));
1407
+ rail.dataset.positionFrame = String(
1408
+ requestAnimationFrame(() => positionRail(body, rail))
1409
+ );
1410
+ }
1411
+
1412
+ function dashboardSubdomainFromUrl(url) {
1413
+ return spaceIdFromUrl(url).toLowerCase().replace(/[^a-z0-9-]/g, "-");
1414
+ }
1415
+
1416
+ function dashboardOpenLink(head, url) {
1417
+ if (!head || !url) return;
1418
+ const meta = head.querySelector(".cell-meta");
1419
+ if (!meta) return;
1420
+ let link = meta.querySelector(".cell-open");
1421
+ if (!link) {
1422
+ link = document.createElement("a");
1423
+ link.className = "cell-open";
1424
+ link.target = "_blank";
1425
+ link.rel = "noopener";
1426
+ meta.insertBefore(link, meta.firstChild);
1427
+ }
1428
+ link.href = url;
1429
+ link.textContent = "Open ↗";
1430
+ }
1431
+
1432
+ function dashboardFrame(src) {
1433
+ const iframe = document.createElement("iframe");
1434
+ iframe.className = "dashboard-frame";
1435
+ iframe.src = src;
1436
+ iframe.loading = "lazy";
1437
+ iframe.allow = "clipboard-read; clipboard-write; fullscreen";
1438
+ return iframe;
1439
+ }
1440
+
1441
+ function renderDashboardCell(meta, body, container, head) {
1442
+ const project = meta.dashboard_project || "";
1443
+ const holder = document.createElement("div");
1444
+ holder.className = "dashboard-shell";
1445
+ container.appendChild(holder);
1446
+ const space = body.match(/https:\/\/huggingface\.co\/spaces\/[^\s<>)"'`]+/);
1447
+ if (space) {
1448
+ const url = space[0];
1449
+ dashboardOpenLink(head, url);
1450
+ holder.appendChild(
1451
+ dashboardFrame(
1452
+ `https://${dashboardSubdomainFromUrl(url)}.hf.space/?sidebar=hidden&hide_empty_tabs=true`
1453
+ )
1454
+ );
1455
+ return;
1456
+ }
1457
+ if (!isLocalPreview()) {
1458
+ holder.className = "artifact-chip";
1459
+ holder.dataset.resUrl = `trackio-local-dashboard://${project}`;
1460
+ holder.innerHTML =
1461
+ "🎯 <strong>Local Trackio dashboard</strong> — publish the logbook to share it";
1462
+ return;
1463
+ }
1464
+ const open = "/dashboard/?project=" + encodeURIComponent(project);
1465
+ dashboardOpenLink(head, open);
1466
+ holder.appendChild(
1467
+ dashboardFrame(open + "&sidebar=hidden&hide_empty_tabs=true"),
1468
+ );
1469
+ }
1470
+
1471
+ const CACHE_PREFIX = "trackio-logbook:";
1472
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
1473
+ const CACHE_MISS_TTL_MS = 60 * 60 * 1000;
1474
+
1475
+ function cacheGet(url) {
1476
+ try {
1477
+ const raw = localStorage.getItem(CACHE_PREFIX + url);
1478
+ if (!raw) return undefined;
1479
+ const entry = JSON.parse(raw);
1480
+ const ttl = entry.d === null ? CACHE_MISS_TTL_MS : CACHE_TTL_MS;
1481
+ if (Date.now() - entry.t > ttl) {
1482
+ localStorage.removeItem(CACHE_PREFIX + url);
1483
+ return undefined;
1484
+ }
1485
+ return entry.d;
1486
+ } catch (e) {
1487
+ return undefined;
1488
+ }
1489
+ }
1490
+
1491
+ function cacheSet(url, data) {
1492
+ try {
1493
+ localStorage.setItem(
1494
+ CACHE_PREFIX + url,
1495
+ JSON.stringify({ t: Date.now(), d: data })
1496
+ );
1497
+ } catch (e) {}
1498
+ }
1499
+
1500
+ async function getJSON(url) {
1501
+ if (UNFURL_CACHE[url] !== undefined) return UNFURL_CACHE[url];
1502
+ const cached = cacheGet(url);
1503
+ if (cached !== undefined) {
1504
+ UNFURL_CACHE[url] = cached;
1505
+ return cached;
1506
+ }
1507
+ try {
1508
+ const r = await fetch(url);
1509
+ if (!r.ok) throw new Error(r.status);
1510
+ const j = await r.json();
1511
+ UNFURL_CACHE[url] = j;
1512
+ cacheSet(url, j);
1513
+ return j;
1514
+ } catch (e) {
1515
+ UNFURL_CACHE[url] = null;
1516
+ cacheSet(url, null);
1517
+ return null;
1518
+ }
1519
+ }
1520
+
1521
+ /* -------------------- routing / render -------------------- */
1522
+
1523
+ function buildTree() {
1524
+ const tree = document.getElementById("tree");
1525
+ tree.innerHTML = "";
1526
+ const nodes = [];
1527
+ (MANIFEST.root.children || []).forEach((c) => flattenTree(c, 0, nodes));
1528
+ nodes.forEach(({ node, depth }) => {
1529
+ const a = document.createElement("a");
1530
+ a.href = "#/" + node.slug;
1531
+ a.className = "depth-" + depth;
1532
+ a.dataset.slug = node.slug;
1533
+ const mark = document.createElement("span");
1534
+ mark.className = "tree-mark";
1535
+ mark.textContent = "§";
1536
+ a.appendChild(mark);
1537
+ a.appendChild(document.createTextNode(" " + node.title));
1538
+ tree.appendChild(a);
1539
+ });
1540
+ }
1541
+
1542
+ function highlight(slug) {
1543
+ document
1544
+ .querySelectorAll("#tree a")
1545
+ .forEach((a) => a.classList.toggle("active", a.dataset.slug === slug));
1546
+ document
1547
+ .getElementById("book-head")
1548
+ .classList.toggle("active", slug === MANIFEST.root.slug);
1549
+ }
1550
+
1551
+ function clearPageCache() {
1552
+ Object.keys(PAGE_CACHE).forEach((key) => {
1553
+ delete PAGE_CACHE[key];
1554
+ });
1555
+ }
1556
+
1557
+ function isLocalPreview() {
1558
+ return ["localhost", "127.0.0.1", "::1"].includes(location.hostname);
1559
+ }
1560
+
1561
+ async function fetchManifest() {
1562
+ const suffix = isLocalPreview() ? `?t=${Date.now()}` : "";
1563
+ return await (await fetch("./logbook.json" + suffix, { cache: "no-store" })).json();
1564
+ }
1565
+
1566
+ async function fetchPage(node) {
1567
+ if (PAGE_CACHE[node.file]) return PAGE_CACHE[node.file];
1568
+ try {
1569
+ const suffix = isLocalPreview()
1570
+ ? `?rev=${encodeURIComponent(MANIFEST.revision || "")}`
1571
+ : "";
1572
+ const r = await fetch("./" + node.file + suffix, { cache: "no-store" });
1573
+ PAGE_CACHE[node.file] = await r.text();
1574
+ } catch (e) {
1575
+ PAGE_CACHE[node.file] = "# " + node.title + "\n\n_Could not load section._";
1576
+ }
1577
+ return PAGE_CACHE[node.file];
1578
+ }
1579
+
1580
+ function allNodes() {
1581
+ const nodes = [];
1582
+ flattenTree(MANIFEST.root, 0, nodes);
1583
+ return nodes.map(({ node }) => node);
1584
+ }
1585
+
1586
+ function collectPinnedCells(markdown, nodes) {
1587
+ const cells = [];
1588
+ markdown.forEach((text, index) => {
1589
+ const cellRe = /(^|\n)---\n<!-- trackio-cell\n([\s\S]*?)\n-->\n([\s\S]*?)(?=\n---\n<!-- trackio-cell\n|\s*$)/g;
1590
+ let match;
1591
+ let cellIndex = 0;
1592
+ while ((match = cellRe.exec(text))) {
1593
+ const meta = parseCellMeta(match[2]);
1594
+ if (isPinned(meta)) {
1595
+ cells.push({
1596
+ meta,
1597
+ body: match[3],
1598
+ node: nodes[index],
1599
+ index: cells.length,
1600
+ order: meta.pinned_at || meta.created_at || "",
1601
+ cellIndex,
1602
+ });
1603
+ }
1604
+ cellIndex++;
1605
+ }
1606
+ });
1607
+ return cells.sort(
1608
+ (a, b) =>
1609
+ a.order.localeCompare(b.order) ||
1610
+ a.index - b.index ||
1611
+ a.cellIndex - b.cellIndex
1612
+ );
1613
+ }
1614
+
1615
+ function renderPinnedNotes(cells, container) {
1616
+ if (!cells.length) return;
1617
+ const deck = document.createElement("section");
1618
+ deck.className = "pinned-notes";
1619
+ const list = document.createElement("div");
1620
+ list.className = "pinned-notes-list";
1621
+ cells.forEach(({ meta, body }) => {
1622
+ const cell = renderCell(meta, body, list);
1623
+ cell.classList.add("pinned-copy");
1624
+ });
1625
+ deck.appendChild(list);
1626
+ const anchor =
1627
+ container.querySelector(".logbook-stats") ||
1628
+ container.querySelector(".agent-hint");
1629
+ container.insertBefore(deck, anchor ? anchor.nextSibling : container.firstChild);
1630
+ container.closest(".book-intro").classList.add("has-pinned-notes");
1631
+ }
1632
+
1633
+ function removeIndexProse(body) {
1634
+ const h1 = Array.from(body.children).find((el) => el.tagName === "H1");
1635
+ if (!h1) return;
1636
+ let current = h1.nextElementSibling;
1637
+ while (current && current.tagName !== "H2") {
1638
+ const next = current.nextElementSibling;
1639
+ current.remove();
1640
+ current = next;
1641
+ }
1642
+ }
1643
+
1644
+ function removePageDirectory(body) {
1645
+ const heading = Array.from(body.children).find(
1646
+ (el) => el.tagName === "H2" && el.textContent.trim().toLowerCase() === "pages"
1647
+ );
1648
+ if (!heading) return;
1649
+ let current = heading;
1650
+ while (current) {
1651
+ const next = current.nextElementSibling;
1652
+ current.remove();
1653
+ if (next && ["H1", "H2"].includes(next.tagName)) break;
1654
+ current = next;
1655
+ }
1656
+ }
1657
+
1658
+ const RAIL_OBSERVERS = [];
1659
+
1660
+ async function renderLogbook(opts = {}) {
1661
+ const scrollY = window.scrollY;
1662
+ const page = document.getElementById("page");
1663
+ RAIL_OBSERVERS.splice(0).forEach((observer) => observer.disconnect());
1664
+ page.innerHTML = "";
1665
+ const nodes = allNodes();
1666
+ const markdown = await Promise.all(nodes.map(fetchPage));
1667
+ const pinnedCells = collectPinnedCells(markdown, nodes);
1668
+ let bookIntroBody = null;
1669
+ nodes.forEach((node, index) => {
1670
+ const section = document.createElement("section");
1671
+ section.className = "page-section";
1672
+ section.id = "/" + node.slug;
1673
+ section.dataset.slug = node.slug;
1674
+
1675
+ const layout = document.createElement("div");
1676
+ layout.className = "page-layout";
1677
+ const body = document.createElement("div");
1678
+ body.className = "page-body";
1679
+ const rail = document.createElement("aside");
1680
+ rail.className = "context-rail";
1681
+ rail.setAttribute("aria-label", `Resources for ${node.title}`);
1682
+
1683
+ renderMarkdown(markdown[index], body);
1684
+ if (node.slug === MANIFEST.root.slug) {
1685
+ section.classList.add("book-intro");
1686
+ removeIndexProse(body);
1687
+ removePageDirectory(body);
1688
+ const hint = buildAgentHint();
1689
+ const h1 = body.querySelector("h1");
1690
+ if (h1 && h1.parentNode === body) {
1691
+ body.insertBefore(hint, h1.nextSibling);
1692
+ } else {
1693
+ body.prepend(hint);
1694
+ }
1695
+ hint.after(buildLogbookStats(markdown));
1696
+ bookIntroBody = body;
1697
+ }
1698
+ layout.appendChild(body);
1699
+ layout.appendChild(rail);
1700
+ section.appendChild(layout);
1701
+ page.appendChild(section);
1702
+ renderRail(markdown[index], body, rail);
1703
+ if (window.ResizeObserver) {
1704
+ const observer = new ResizeObserver(() => scheduleRailPosition(body, rail));
1705
+ observer.observe(body);
1706
+ observer.observe(rail);
1707
+ RAIL_OBSERVERS.push(observer);
1708
+ }
1709
+ });
1710
+ if (bookIntroBody) renderPinnedNotes(pinnedCells, bookIntroBody);
1711
+ if (bookIntroBody) {
1712
+ const section = bookIntroBody.closest(".book-intro");
1713
+ const hasExtra = Array.from(bookIntroBody.children).some(
1714
+ (el) =>
1715
+ el.tagName !== "H1" &&
1716
+ !el.classList.contains("agent-hint") &&
1717
+ !el.classList.contains("logbook-stats") &&
1718
+ !el.classList.contains("pinned-notes")
1719
+ );
1720
+ if (section && !section.classList.contains("has-pinned-notes") && !hasExtra) {
1721
+ section.classList.add("book-intro-tight");
1722
+ }
1723
+ }
1724
+ requestAnimationFrame(() => {
1725
+ if (opts.preserveScroll) {
1726
+ window.scrollTo(0, scrollY);
1727
+ } else {
1728
+ scrollToHash({ behavior: "auto" });
1729
+ }
1730
+ updateActiveSection();
1731
+ });
1732
+ }
1733
+
1734
+ function setupResourceHover() {
1735
+ document.addEventListener("mouseover", (e) => {
1736
+ const el = e.target.closest && e.target.closest("[data-res-url]");
1737
+ if (!el || el.classList.contains("rail-item")) return;
1738
+ const url = el.getAttribute("data-res-url");
1739
+ const section = el.closest(".page-section");
1740
+ const scope = section || document;
1741
+ scope.querySelectorAll(".context-rail [data-res-url]").forEach((n) => {
1742
+ n.classList.toggle("res-hl", n.getAttribute("data-res-url") === url);
1743
+ });
1744
+ });
1745
+ document.addEventListener("mouseout", (e) => {
1746
+ const el = e.target.closest && e.target.closest("[data-res-url]");
1747
+ if (!el || el.classList.contains("rail-item")) return;
1748
+ document.querySelectorAll(".context-rail .res-hl").forEach((n) => {
1749
+ n.classList.remove("res-hl");
1750
+ });
1751
+ });
1752
+ }
1753
+
1754
+ let STATS_TOKEN = 0;
1755
+ let STATS_LISTENERS = false;
1756
+
1757
+ function fmtBytes(n) {
1758
+ if (n == null || isNaN(n)) return null;
1759
+ if (n < 1000) return `${n} B`;
1760
+ const units = ["kB", "MB", "GB", "TB"];
1761
+ let v = n;
1762
+ let i = -1;
1763
+ do {
1764
+ v /= 1000;
1765
+ i++;
1766
+ } while (v >= 1000 && i < units.length - 1);
1767
+ return `${v.toFixed(v < 10 ? 1 : 0)} ${units[i]}`;
1768
+ }
1769
+
1770
+ function spaceIdFromUrl(url) {
1771
+ return url.split("/spaces/")[1].split(/[?#]/)[0].replace(/\/$/, "");
1772
+ }
1773
+
1774
+ const LB_CELL_RE = /(^|\n)---\n<!-- trackio-cell\n([\s\S]*?)\n-->\n([\s\S]*?)(?=\n---\n<!-- trackio-cell\n|\s*$)/g;
1775
+
1776
+ function cellDashboardItems(md) {
1777
+ const re = new RegExp(LB_CELL_RE.source, "g");
1778
+ const items = [];
1779
+ let m;
1780
+ while ((m = re.exec(md))) {
1781
+ const meta = parseCellMeta(m[2]);
1782
+ if (meta.type !== "dashboard") continue;
1783
+ const body = m[3];
1784
+ const project = meta.dashboard_project || "";
1785
+ const sp = body.match(/https:\/\/huggingface\.co\/spaces\/[^\s<>)"'`]+/);
1786
+ const local = !sp;
1787
+ const url = sp ? sp[0] : "";
1788
+ const resUrl = local ? `trackio-local-dashboard://${project}` : url;
1789
+ items.push({
1790
+ id: local ? project : spaceIdFromUrl(url),
1791
+ local,
1792
+ url,
1793
+ resUrl,
1794
+ });
1795
+ }
1796
+ return items;
1797
+ }
1798
+
1799
+ function artifactInfoFromCell(meta, body) {
1800
+ const name = meta.artifact || meta.path || "";
1801
+ let size = null;
1802
+ const sm = body.match(/·\s*([\d.]+\s*[kMGT]?B)\b/);
1803
+ if (sm) size = sm[1].trim();
1804
+ if (!size && meta.size != null) size = fmtBytes(meta.size);
1805
+ const bucket = body.match(/https:\/\/huggingface\.co\/buckets\/[^\s<>)"'`]+/);
1806
+ const artUri = body.match(/trackio-artifact:\/\/\S+/);
1807
+ const pathUri = body.match(/trackio-local-path:\/\/\S+/);
1808
+ const url = bucket ? bucket[0] : "";
1809
+ const local = !bucket;
1810
+ const resUrl =
1811
+ url || (artUri ? artUri[0] : pathUri ? pathUri[0] : `trackio-artifact://${name}`);
1812
+ return {
1813
+ name,
1814
+ type: meta.artifact_type || "",
1815
+ size,
1816
+ local,
1817
+ isPathRef: !!meta.path,
1818
+ url,
1819
+ resUrl,
1820
+ };
1821
+ }
1822
+
1823
+ function cellArtifactItems(md) {
1824
+ const re = new RegExp(LB_CELL_RE.source, "g");
1825
+ const items = [];
1826
+ let m;
1827
+ while ((m = re.exec(md))) {
1828
+ const meta = parseCellMeta(m[2]);
1829
+ const body = m[3];
1830
+ const order = meta.created_at || "";
1831
+ if (meta.type === "artifact") {
1832
+ const info = artifactInfoFromCell(meta, body);
1833
+ if (info.name) items.push({ ...info, order });
1834
+ }
1835
+ }
1836
+ return items;
1837
+ }
1838
+
1839
+ function collectLogbookResources(markdownList) {
1840
+ const re = new RegExp(LB_CELL_RE.source, "g");
1841
+ const dashboards = new Map();
1842
+ markdownList.forEach((md) => {
1843
+ let m;
1844
+ while ((m = re.exec(md))) {
1845
+ const meta = parseCellMeta(m[2]);
1846
+ const body = m[3];
1847
+ if (meta.type !== "dashboard") continue;
1848
+ const project = meta.dashboard_project || "";
1849
+ const space = body.match(/https:\/\/huggingface\.co\/spaces\/[^\s<>)"'`]+/);
1850
+ const local = !space;
1851
+ const url = space ? space[0] : "";
1852
+ const key = local ? `local:${project}` : `space:${spaceIdFromUrl(url)}`;
1853
+ const resUrl = local ? `trackio-local-dashboard://${project}` : url;
1854
+ if (!dashboards.has(key))
1855
+ dashboards.set(key, { project, local, url, resUrl });
1856
+ }
1857
+ });
1858
+ const artifacts = new Map();
1859
+ markdownList.forEach((md) => {
1860
+ cellArtifactItems(md).forEach((it) => {
1861
+ const key = `${it.type}:${it.name}`;
1862
+ const prev = artifacts.get(key);
1863
+ if (!prev || it.order >= prev.order) artifacts.set(key, it);
1864
+ });
1865
+ });
1866
+ return {
1867
+ dashboards: Array.from(dashboards.values()).sort((a, b) =>
1868
+ a.project.localeCompare(b.project)
1869
+ ),
1870
+ artifacts: Array.from(artifacts.values()).sort((a, b) =>
1871
+ a.name.localeCompare(b.name)
1872
+ ),
1873
+ };
1874
+ }
1875
+
1876
+ function closeStatPopovers() {
1877
+ document
1878
+ .querySelectorAll(".stat-popover")
1879
+ .forEach((p) => (p.hidden = true));
1880
+ document
1881
+ .querySelectorAll(".stat-tile.open")
1882
+ .forEach((t) => t.classList.remove("open"));
1883
+ }
1884
+
1885
+ function ensureStatListeners() {
1886
+ if (STATS_LISTENERS) return;
1887
+ STATS_LISTENERS = true;
1888
+ document.addEventListener("click", closeStatPopovers);
1889
+ document.addEventListener("keydown", (e) => {
1890
+ if (e.key === "Escape") closeStatPopovers();
1891
+ });
1892
+ }
1893
+
1894
+ function stateHtml(remote, url) {
1895
+ return remote
1896
+ ? `<a class="stat-row-state open" href="${esc(url)}" target="_blank" rel="noopener" title="Open in a new tab">Open ↗</a>`
1897
+ : `<span class="stat-row-state">publish to share</span>`;
1898
+ }
1899
+
1900
+ function scrollToResource(resUrl) {
1901
+ closeStatPopovers();
1902
+ if (!resUrl) return;
1903
+ const el = document.querySelector(
1904
+ `#page .page-body [data-res-url="${CSS.escape(resUrl)}"]:not(.stat-row)`
1905
+ );
1906
+ if (!el) return;
1907
+ el.scrollIntoView({ behavior: "smooth", block: "center" });
1908
+ el.classList.add("res-flash");
1909
+ setTimeout(() => el.classList.remove("res-flash"), 1500);
1910
+ }
1911
+
1912
+ function dashRowHtml(d) {
1913
+ const inner =
1914
+ `<span class="stat-row-ico">${DASHBOARD_ICON_IMG}</span>` +
1915
+ `<div class="stat-row-main"><div class="stat-row-title">${esc(d.project)}</div>` +
1916
+ `<div class="stat-row-meta">${stateHtml(!d.local, d.url)}</div></div>`;
1917
+ return `<div class="stat-row" data-res-url="${esc(d.resUrl)}" title="Jump to it in the logbook">${inner}</div>`;
1918
+ }
1919
+
1920
+ function artRowHtml(a) {
1921
+ const remote = !a.local && !!a.url;
1922
+ const parts = [a.type, a.size].filter(Boolean).map(esc);
1923
+ const meta = parts.length
1924
+ ? `${parts.join(" · ")} · ${stateHtml(remote, a.url)}`
1925
+ : stateHtml(remote, a.url);
1926
+ const inner =
1927
+ `<span class="stat-row-ico">${ARTIFACT_ICON_IMG}</span>` +
1928
+ `<div class="stat-row-main"><div class="stat-row-title">${esc(a.name)}</div>` +
1929
+ `<div class="stat-row-meta">${meta}</div></div>`;
1930
+ return `<div class="stat-row" data-res-url="${esc(a.resUrl)}" title="Jump to it in the logbook">${inner}</div>`;
1931
+ }
1932
+
1933
+ function statTile(icon, alt, singular, plural, head, rowFn) {
1934
+ const tile = document.createElement("button");
1935
+ tile.type = "button";
1936
+ tile.className = "stat-tile";
1937
+ const render = (items) => {
1938
+ const count = items.length;
1939
+ const label = count === 1 ? singular : plural;
1940
+ const caret = count > 0 ? `<span class="stat-caret">▾</span>` : "";
1941
+ tile.innerHTML =
1942
+ `<img class="stat-icon" src="${icon}" alt="${esc(alt)}" />` +
1943
+ `<div class="stat-text"><div class="stat-num">${count}</div>` +
1944
+ `<div class="stat-label">${esc(label)}</div></div>` +
1945
+ caret;
1946
+ tile.disabled = count === 0;
1947
+ if (count > 0) {
1948
+ const pop = document.createElement("div");
1949
+ pop.className = "stat-popover";
1950
+ pop.hidden = true;
1951
+ pop.innerHTML =
1952
+ `<div class="stat-pop-head">${esc(head)}</div>` +
1953
+ items.map(rowFn).join("");
1954
+ pop.addEventListener("click", (e) => {
1955
+ if (e.target.closest("a.stat-row-state")) {
1956
+ e.stopPropagation();
1957
+ return;
1958
+ }
1959
+ e.stopPropagation();
1960
+ const row = e.target.closest(".stat-row");
1961
+ if (row) scrollToResource(row.dataset.resUrl);
1962
+ });
1963
+ tile.appendChild(pop);
1964
+ }
1965
+ };
1966
+ tile.addEventListener("click", (e) => {
1967
+ if (tile.disabled) return;
1968
+ e.stopPropagation();
1969
+ const pop = tile.querySelector(".stat-popover");
1970
+ if (!pop) return;
1971
+ const isOpen = !pop.hidden;
1972
+ closeStatPopovers();
1973
+ if (!isOpen) {
1974
+ pop.hidden = false;
1975
+ tile.classList.add("open");
1976
+ }
1977
+ });
1978
+ return { tile, render };
1979
+ }
1980
+
1981
+ function buildLogbookStats(markdownList) {
1982
+ const token = ++STATS_TOKEN;
1983
+ ensureStatListeners();
1984
+ const { dashboards, artifacts } = collectLogbookResources(markdownList);
1985
+
1986
+ const el = document.createElement("div");
1987
+ el.className = "logbook-stats";
1988
+ const dash = statTile(
1989
+ "./trackio-logo-light.png",
1990
+ "Trackio",
1991
+ "Trackio Dashboard",
1992
+ "Trackio Dashboards",
1993
+ "Dashboards created in this logbook",
1994
+ dashRowHtml
1995
+ );
1996
+ const art = statTile(
1997
+ "./bucket-icon.svg",
1998
+ "Bucket",
1999
+ "Artifact",
2000
+ "Artifacts",
2001
+ "Artifacts created in this logbook",
2002
+ artRowHtml
2003
+ );
2004
+ dash.render(dashboards);
2005
+ art.render(artifacts);
2006
+ el.appendChild(dash.tile);
2007
+ el.appendChild(art.tile);
2008
+
2009
+ const scanText = markdownList
2010
+ .map((md) =>
2011
+ md.replace(/(`{3,4}|~{3,4})(html|raw)[^\n]*\n[\s\S]*?\n\1/g, " ")
2012
+ )
2013
+ .join("\n");
2014
+ const seen = new Set(
2015
+ dashboards.map((d) =>
2016
+ d.local ? `local:${d.project}` : `space:${spaceIdFromUrl(d.url)}`
2017
+ )
2018
+ );
2019
+ const remoteSpaces = new Map();
2020
+ extractUrls(scanText).forEach((url) => {
2021
+ const item = classifyResource(url);
2022
+ if (item && item.kind === "space" && !item.local) {
2023
+ remoteSpaces.set(item.url, item);
2024
+ }
2025
+ });
2026
+ remoteSpaces.forEach((s) => {
2027
+ const key = `space:${s.id}`;
2028
+ if (seen.has(key)) return;
2029
+ getJSON(`https://huggingface.co/api/spaces/${s.id}`)
2030
+ .then((d) => {
2031
+ if (STATS_TOKEN !== token) return;
2032
+ const tags = (d && d.tags) || [];
2033
+ if (
2034
+ !seen.has(key) &&
2035
+ tags.some((t) => String(t).toLowerCase() === "trackio")
2036
+ ) {
2037
+ seen.add(key);
2038
+ dashboards.push({
2039
+ project: s.id,
2040
+ local: false,
2041
+ url: s.url,
2042
+ resUrl: s.url,
2043
+ });
2044
+ dashboards.sort((a, b) => a.project.localeCompare(b.project));
2045
+ dash.render(dashboards);
2046
+ }
2047
+ })
2048
+ .catch(() => {});
2049
+ });
2050
+ return el;
2051
+ }
2052
+
2053
+ function buildAgentHint() {
2054
+ const onSpaces =
2055
+ /\.hf\.space$/.test(location.hostname) ||
2056
+ /(^|\.)huggingface\.co$/.test(location.hostname);
2057
+ let source = "";
2058
+ if (onSpaces && MANIFEST.space_id) {
2059
+ source = ` ${MANIFEST.space_id}`;
2060
+ } else if (/^https?:$/.test(location.protocol)) {
2061
+ source = ` ${location.origin}/`;
2062
+ }
2063
+ const command = `trackio logbook read${source}`;
2064
+ const tokens = MANIFEST.agent_view_tokens;
2065
+ const div = document.createElement("div");
2066
+ div.className = "agent-hint";
2067
+ const label = document.createElement("span");
2068
+ label.className = "agent-hint-label";
2069
+ label.textContent = "Read from the CLI:";
2070
+ const code = document.createElement("code");
2071
+ code.textContent = command;
2072
+ const copy = document.createElement("button");
2073
+ copy.className = "copy";
2074
+ copy.type = "button";
2075
+ copy.title = "Copy";
2076
+ copy.textContent = "⧉";
2077
+ copy.addEventListener("click", () => copyText(command, copy, "⧉"));
2078
+ const note = document.createElement("span");
2079
+ note.className = "agent-hint-note";
2080
+ note.textContent =
2081
+ "compact view for agents" + (tokens ? ` · ~${fmt(tokens)} tokens` : "");
2082
+ div.appendChild(label);
2083
+ div.appendChild(code);
2084
+ div.appendChild(copy);
2085
+ div.appendChild(note);
2086
+ return div;
2087
+ }
2088
+
2089
+ function currentSlug() {
2090
+ const slug = (location.hash || "").replace(/^#\//, "") || MANIFEST.root.slug;
2091
+ return findNode(MANIFEST.root, slug) ? slug : MANIFEST.root.slug;
2092
+ }
2093
+
2094
+ function scrollToHash(opts = {}) {
2095
+ const slug = currentSlug();
2096
+ if (!location.hash) {
2097
+ window.scrollTo({ top: 0, behavior: opts.behavior || "auto" });
2098
+ highlight(slug);
2099
+ return;
2100
+ }
2101
+ const section = document.getElementById("/" + slug);
2102
+ if (section) section.scrollIntoView({ behavior: opts.behavior || "smooth" });
2103
+ highlight(slug);
2104
+ }
2105
+
2106
+ function navigateToLogbookSlug(target) {
2107
+ const slug = String(target || "").replace(/^#?\//, "").trim();
2108
+ if (!slug || !findNode(MANIFEST.root, slug)) return;
2109
+ const hash = "#/" + slug;
2110
+ if (location.hash === hash) {
2111
+ scrollToHash({ behavior: "smooth" });
2112
+ } else {
2113
+ location.hash = hash;
2114
+ }
2115
+ }
2116
+
2117
+ function setupFigureNavigation() {
2118
+ window.addEventListener("message", (event) => {
2119
+ const data = event.data;
2120
+ if (!data || data.type !== "trackio-logbook:navigate") return;
2121
+ // Only accept messages from one of this logbook's sandboxed figure
2122
+ // iframes, rather than from an arbitrary same-origin page.
2123
+ const isFigureFrame = Array.from(
2124
+ document.querySelectorAll("iframe.figure-frame")
2125
+ ).some((frame) => frame.contentWindow === event.source);
2126
+ if (!isFigureFrame) return;
2127
+ navigateToLogbookSlug(data.target);
2128
+ });
2129
+ }
2130
+
2131
+ let SCROLL_FRAME = 0;
2132
+ function updateActiveSection() {
2133
+ cancelAnimationFrame(SCROLL_FRAME);
2134
+ SCROLL_FRAME = requestAnimationFrame(() => {
2135
+ const sections = Array.from(document.querySelectorAll(".page-section"));
2136
+ if (!sections.length) return;
2137
+ const marker = Math.min(window.innerHeight * 0.28, 180);
2138
+ let active = sections[0];
2139
+ sections.forEach((section) => {
2140
+ if (section.getBoundingClientRect().top <= marker) active = section;
2141
+ });
2142
+ if (
2143
+ window.innerHeight + window.scrollY >=
2144
+ document.documentElement.scrollHeight - 2
2145
+ ) {
2146
+ active = sections[sections.length - 1];
2147
+ }
2148
+ highlight(active.dataset.slug);
2149
+ });
2150
+ }
2151
+
2152
+ function startLiveReload() {
2153
+ if (!isLocalPreview()) return;
2154
+ setInterval(async () => {
2155
+ try {
2156
+ const next = await fetchManifest();
2157
+ if (!next || next.revision === MANIFEST.revision) return;
2158
+ MANIFEST = next;
2159
+ clearPageCache();
2160
+ document.title = MANIFEST.title + " · Trackio Logbook";
2161
+ document.getElementById("book-title").textContent = MANIFEST.title;
2162
+ document.getElementById("book-head").setAttribute("aria-label", MANIFEST.title);
2163
+ buildTree();
2164
+ renderLogbook({ preserveScroll: true });
2165
+ } catch (e) {}
2166
+ }, LIVE_RELOAD_MS);
2167
+ }
2168
+
2169
+ function setupConnect() {
2170
+ const space = MANIFEST.space_id;
2171
+ if (!space) return;
2172
+ const steps = [
2173
+ { t: "Install Trackio, if you don't have it yet.", c: "uv tool install trackio" },
2174
+ { t: "Add the Trackio skill for your agent, then reload it.", c: "trackio skills add" },
2175
+ { t: "Connect to this logbook.", c: `trackio logbook open ${space}` },
2176
+ ];
2177
+ const ol = document.getElementById("connect-steps");
2178
+ steps.forEach((s, i) => {
2179
+ const li = document.createElement("li");
2180
+ const title = document.createElement("div");
2181
+ title.className = "step-title";
2182
+ title.textContent = `${i + 1}. ${s.t}`;
2183
+ const block = document.createElement("div");
2184
+ block.className = "codeblock";
2185
+ const code = document.createElement("code");
2186
+ code.textContent = s.c;
2187
+ const copy = document.createElement("button");
2188
+ copy.className = "copy";
2189
+ copy.type = "button";
2190
+ copy.title = "Copy";
2191
+ copy.textContent = "⧉";
2192
+ copy.addEventListener("click", () => copyText(s.c, copy, "⧉"));
2193
+ block.appendChild(code);
2194
+ block.appendChild(copy);
2195
+ li.appendChild(title);
2196
+ li.appendChild(block);
2197
+ ol.appendChild(li);
2198
+ });
2199
+
2200
+ const agentPrompt =
2201
+ `Read and help maintain this Trackio experiment logbook ("${MANIFEST.title}").\n\n` +
2202
+ "1. If you don't have Trackio, install it: uv tool install trackio\n" +
2203
+ "2. Add the Trackio skill for your agent: trackio skills add (then reload)\n" +
2204
+ `3. Connect to this logbook: trackio logbook open ${space}\n\n` +
2205
+ "Start with `trackio logbook read`; use `trackio logbook read page \"...\"` " +
2206
+ "for a page-level view, then fetch relevant details with " +
2207
+ "`trackio logbook read cell cell_<id>`. If I've given you " +
2208
+ 'write access to the Space, add findings with `trackio logbook cell markdown "..." ' +
2209
+ '--page "..."` and they will sync back automatically.';
2210
+
2211
+ const foot = document.getElementById("sidebar-foot");
2212
+ foot.hidden = false;
2213
+ const modal = document.getElementById("modal");
2214
+ const open = () => (modal.hidden = false);
2215
+ const close = () => (modal.hidden = true);
2216
+ document.getElementById("connect-btn").addEventListener("click", open);
2217
+ document.getElementById("modal-close").addEventListener("click", close);
2218
+ modal.querySelector(".modal-backdrop").addEventListener("click", close);
2219
+ document.addEventListener("keydown", (e) => {
2220
+ if (e.key === "Escape") close();
2221
+ });
2222
+ const agentBtn = document.getElementById("copy-agent");
2223
+ agentBtn.addEventListener("click", () =>
2224
+ copyText(agentPrompt, agentBtn, "Copy for agent")
2225
+ );
2226
+ }
2227
+
2228
+ function copyText(text, btn, restore) {
2229
+ const done = () => {
2230
+ const prev = btn.textContent;
2231
+ btn.textContent = restore === "⧉" ? "✓" : "Copied!";
2232
+ btn.classList.add("copied");
2233
+ setTimeout(() => {
2234
+ btn.textContent = restore;
2235
+ btn.classList.remove("copied");
2236
+ }, 1400);
2237
+ void prev;
2238
+ };
2239
+ if (navigator.clipboard && navigator.clipboard.writeText) {
2240
+ navigator.clipboard.writeText(text).then(done, done);
2241
+ } else {
2242
+ const ta = document.createElement("textarea");
2243
+ ta.value = text;
2244
+ document.body.appendChild(ta);
2245
+ ta.select();
2246
+ try {
2247
+ document.execCommand("copy");
2248
+ } catch (e) {}
2249
+ document.body.removeChild(ta);
2250
+ done();
2251
+ }
2252
+ }
2253
+
2254
+ async function init() {
2255
+ MANIFEST = await fetchManifest();
2256
+ document.title = MANIFEST.title + " · Trackio Logbook";
2257
+ document.getElementById("book-title").textContent = MANIFEST.title;
2258
+ document.getElementById("book-head").setAttribute("aria-label", MANIFEST.title);
2259
+ document.getElementById("book-head").addEventListener("click", () => {
2260
+ const target = "#/" + MANIFEST.root.slug;
2261
+ if (location.hash === target) scrollToHash();
2262
+ else location.hash = target;
2263
+ });
2264
+ buildTree();
2265
+ setupConnect();
2266
+ setupResourceHover();
2267
+ setupFigureNavigation();
2268
+ window.addEventListener("hashchange", () => scrollToHash());
2269
+ window.addEventListener("scroll", updateActiveSection, { passive: true });
2270
+ await renderLogbook();
2271
+ startLiveReload();
2272
+ }
2273
+
2274
+ init();
2275
+ })();
logbook.json ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema_version": 1,
3
+ "title": "Repro - Networked Information Aggregation for Binary Classification",
4
+ "emoji": "🎯",
5
+ "space_id": "FloorIsAwake/repro-networked-information-aggregation-binary-classification",
6
+ "paper": {
7
+ "arxiv_id": "2605.01082",
8
+ "openreview_id": "mrtg4NmvAe",
9
+ "icml_paper_number": "30204"
10
+ },
11
+ "tags": [
12
+ "icml2026-repro",
13
+ "paper-mrtg4NmvAe"
14
+ ],
15
+ "updated_at": "2026-07-16T12:53:16+00:00",
16
+ "root": {
17
+ "slug": "index",
18
+ "title": "Repro - Networked Information Aggregation for Binary Classification",
19
+ "file": "pages/index.md",
20
+ "children": [
21
+ {
22
+ "slug": "sources-and-methodology",
23
+ "title": "Sources and methodology",
24
+ "file": "pages/sources-and-methodology/page.md",
25
+ "children": []
26
+ },
27
+ {
28
+ "slug": "claim-1-upper-bound-under-m-coverage",
29
+ "title": "Claim 1 - Upper bound under M-coverage",
30
+ "file": "pages/claim-1-upper-bound-under-m-coverage/page.md",
31
+ "children": []
32
+ },
33
+ {
34
+ "slug": "claim-2-lower-bound-k-over-d",
35
+ "title": "Claim 2 - Lower bound k over D",
36
+ "file": "pages/claim-2-lower-bound-k-over-d/page.md",
37
+ "children": []
38
+ },
39
+ {
40
+ "slug": "claim-3-bce-residual-orthogonality",
41
+ "title": "Claim 3 - BCE residual orthogonality",
42
+ "file": "pages/claim-3-bce-residual-orthogonality/page.md",
43
+ "children": []
44
+ },
45
+ {
46
+ "slug": "claim-4-bce-kl-decomposition",
47
+ "title": "Claim 4 - BCE KL decomposition",
48
+ "file": "pages/claim-4-bce-kl-decomposition/page.md",
49
+ "children": []
50
+ },
51
+ {
52
+ "slug": "claim-5-sequential-logit-protocol",
53
+ "title": "Claim 5 - Sequential logit protocol",
54
+ "file": "pages/claim-5-sequential-logit-protocol/page.md",
55
+ "children": []
56
+ },
57
+ {
58
+ "slug": "conclusion",
59
+ "title": "Conclusion",
60
+ "file": "pages/conclusion/page.md",
61
+ "children": []
62
+ }
63
+ ]
64
+ },
65
+ "agent_view_tokens": 4056,
66
+ "revision": "1784206396526226735"
67
+ }
pages/claim-1-upper-bound-under-m-coverage/page.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 1 - Upper bound under M-coverage
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_0c32632ab207", "created_at": "2026-07-16T12:29:16+00:00", "title": "Claim 1 audit and bound check"}
7
+ -->
8
+ **Verdict: supported in substance, but the challenge cites the wrong theorem number.** In the accepted arXiv source, Definition 3.7 is M-coverage and the bound is **Theorem 3.8** (PDF page 5):
9
+
10
+ `L(p_D)-L(p*) <= B_{p*} B_X M/sqrt(D)`.
11
+
12
+ The assumptions are a depth-D path in the DAG satisfying M-coverage, feature second moments bounded by `B_X^2`, and global-optimum logit coefficients with l1 norm at most `B_{p*}`. The block argument implicitly needs `D>=M`. On all 59 cyclic hard-instance checkpoints, measured excess was below the theorem bound; the largest excess/bound ratio was 0.00433798. This validates the inequality on the tested family but also shows the upper bound is very loose there; it is not a tightness experiment.
pages/claim-2-lower-bound-k-over-d/page.md ADDED
@@ -0,0 +1,587 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 2 - Lower bound k over D
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "code", "id": "cell_53969e776163", "created_at": "2026-07-16T12:25:50+00:00", "title": "Substantive deterministic population run plus Monte Carlo cross-check", "command": ["python", "repro.py", "--mode", "substantive", "--output", "outputs/substantive", "--device", "cpu", "--mc-samples", "500000"], "exit_code": 0, "duration_s": 12.088}
7
+ -->
8
+ ````bash
9
+ $ python repro.py --mode substantive --output outputs/substantive --device cpu --mc-samples 500000
10
+ ````
11
+
12
+ exit 0 · 12.1s
13
+
14
+
15
+ ````python title=repro.py
16
+ #!/usr/bin/env python3
17
+ """Deterministic and Monte Carlo checks for arXiv:2605.01082.
18
+
19
+ The population sequential protocol is simulated exactly in coefficient space.
20
+ For the paper's Gaussian hard instance, every logit is a linear form a^T Z,
21
+ where Z ~ N(0, I). Population BCE therefore depends only on ||a|| and a_k.
22
+ Each agent minimizes BCE over the span of its local-feature logit and its
23
+ parent's logit, exactly matching Section 2's logit-passing protocol.
24
+ """
25
+ from __future__ import annotations
26
+
27
+ import argparse
28
+ import csv
29
+ import json
30
+ import math
31
+ import os
32
+ import platform
33
+ import subprocess
34
+ import time
35
+ from dataclasses import asdict, dataclass
36
+ from pathlib import Path
37
+ from typing import Any
38
+
39
+ import numpy as np
40
+ from scipy.optimize import minimize
41
+ from scipy.special import expit, roots_hermitenorm
42
+
43
+ try:
44
+ import torch
45
+ except ImportError: # Monte Carlo is optional for the deterministic smoke test.
46
+ torch = None
47
+
48
+
49
+ @dataclass
50
+ class AgentRecord:
51
+ k: int
52
+ agent: int
53
+ depth: int
54
+ pass_index: int
55
+ feature: int
56
+ loss: float
57
+ excess: float
58
+ local_weight: float
59
+ parent_weight: float
60
+ stationarity_max_abs: float
61
+ off_relevant_l2: float
62
+
63
+
64
+ def softplus(x: np.ndarray) -> np.ndarray:
65
+ return np.logaddexp(0.0, x)
66
+
67
+
68
+ class GaussianBCE:
69
+ """Gauss-Hermite population integrals for centered Gaussian logits."""
70
+
71
+ def __init__(self, order: int = 100):
72
+ nodes, weights = roots_hermitenorm(order)
73
+ self.nodes = nodes.astype(np.float64)
74
+ self.weights = (weights / math.sqrt(2.0 * math.pi)).astype(np.float64)
75
+ self.corr = float(np.sum(self.weights * self.nodes * expit(self.nodes)))
76
+
77
+ def loss(self, a: np.ndarray) -> float:
78
+ std = float(np.linalg.norm(a))
79
+ expected_softplus = float(np.sum(self.weights * softplus(std * self.nodes)))
80
+ return expected_softplus - self.corr * float(a[-1])
81
+
82
+ def optimize_span(self, local: np.ndarray, parent: np.ndarray) -> tuple[np.ndarray, np.ndarray, float]:
83
+ basis = np.column_stack([local, parent])
84
+
85
+ def objective(w: np.ndarray) -> float:
86
+ return self.loss(basis @ w)
87
+
88
+ def gradient(w: np.ndarray) -> np.ndarray:
89
+ a = basis @ w
90
+ std = float(np.linalg.norm(a))
91
+ if std < 1e-14:
92
+ grad_a = -self.corr * np.eye(1, len(a), len(a) - 1, dtype=np.float64).ravel()
93
+ else:
94
+ radial = float(np.sum(self.weights * expit(std * self.nodes) * self.nodes)) / std
95
+ grad_a = radial * a
96
+ grad_a[-1] -= self.corr
97
+ return basis.T @ grad_a
98
+
99
+ result = minimize(objective, np.array([0.0, 1.0]), jac=gradient, method="BFGS", options={"gtol": 1e-12, "maxiter": 200})
100
+ # BFGS can report precision loss after already reaching machine-level stationarity.
101
+ grad = gradient(result.x)
102
+ if not np.isfinite(result.fun) or float(np.max(np.abs(grad))) > 2e-8:
103
+ raise RuntimeError(f"span optimization failed: {result.message}; gradient={grad}")
104
+ return basis @ result.x, result.x, float(np.max(np.abs(grad)))
105
+
106
+ def joint_expectations(self, a: np.ndarray, b: np.ndarray, order: int = 60) -> dict[str, float]:
107
+ """KL(a||b) and probability MSE for two jointly Gaussian logits."""
108
+ nodes, weights = roots_hermitenorm(order)
109
+ weights = weights / math.sqrt(2.0 * math.pi)
110
+ cov = np.array([[a @ a, a @ b], [a @ b, b @ b]], dtype=np.float64)
111
+ vals, vecs = np.linalg.eigh(cov)
112
+ transform = vecs @ np.diag(np.sqrt(np.clip(vals, 0.0, None)))
113
+ g1, g2 = np.meshgrid(nodes, nodes, indexing="ij")
114
+ z1 = transform[0, 0] * g1 + transform[0, 1] * g2
115
+ z2 = transform[1, 0] * g1 + transform[1, 1] * g2
116
+ ww = weights[:, None] * weights[None, :]
117
+ p, q = expit(z1), expit(z2)
118
+ kl_point = p * (z1 - z2) + softplus(z2) - softplus(z1)
119
+ mse_point = (p - q) ** 2
120
+ return {
121
+ "kl": float(np.sum(ww * kl_point)),
122
+ "probability_mse": float(np.sum(ww * mse_point)),
123
+ }
124
+
125
+
126
+ def feature_vector(k: int, feature_index: int) -> np.ndarray:
127
+ """Return x_i as coefficients over independent Z_1,...,Z_k (1-indexed i)."""
128
+ b = np.zeros(k, dtype=np.float64)
129
+ if feature_index == 1:
130
+ b[0] = 1.0
131
+ else:
132
+ b[feature_index - 1] = 1.0
133
+ b[feature_index - 2] = -1.0
134
+ return b
135
+
136
+
137
+ def simulate_path(k: int, passes: int, gbce: GaussianBCE) -> tuple[list[AgentRecord], list[np.ndarray]]:
138
+ parent = np.zeros(k, dtype=np.float64)
139
+ global_a = np.zeros(k, dtype=np.float64)
140
+ global_a[-1] = 1.0
141
+ optimum = gbce.loss(global_a)
142
+ records: list[AgentRecord] = []
143
+ coefficients: list[np.ndarray] = []
144
+ for depth in range(1, k * passes + 1):
145
+ feature = ((depth - 1) % k) + 1
146
+ pass_index = (depth - 1) // k + 1
147
+ local = feature_vector(k, feature)
148
+ new_a, w, stationarity = gbce.optimize_span(local, parent)
149
+ relevant_start = max(0, k - pass_index - 1) # Last p x-features span Z_{k-p},...,Z_k.
150
+ off = float(np.linalg.norm(new_a[:relevant_start])) if feature == k else float("nan")
151
+ loss = gbce.loss(new_a)
152
+ records.append(AgentRecord(k, depth, depth, pass_index, feature, loss, loss - optimum, float(w[0]), float(w[1]), stationarity, off))
153
+ coefficients.append(new_a.copy())
154
+ parent = new_a
155
+ return records, coefficients
156
+
157
+
158
+ def linear_fit(x: np.ndarray, y: np.ndarray) -> dict[str, float]:
159
+ slope, intercept = np.polyfit(x, y, 1)
160
+ pred = slope * x + intercept
161
+ ss_res = float(np.sum((y - pred) ** 2))
162
+ ss_tot = float(np.sum((y - np.mean(y)) ** 2))
163
+ return {"slope": float(slope), "intercept": float(intercept), "r2": 1.0 - ss_res / ss_tot}
164
+
165
+
166
+ def monte_carlo_lower(p_values: list[int], samples: int, seeds: list[int], device: str) -> list[dict[str, Any]]:
167
+ if torch is None:
168
+ return []
169
+ dtype = torch.float64
170
+ rows: list[dict[str, Any]] = []
171
+ for seed in seeds:
172
+ gen = torch.Generator(device=device)
173
+ gen.manual_seed(seed)
174
+ z = torch.randn(samples, generator=gen, device=device, dtype=dtype)
175
+ xi = torch.randn(samples, generator=gen, device=device, dtype=dtype)
176
+ target = torch.sigmoid(z)
177
+ optimum = torch.mean(torch.nn.functional.softplus(z) - target * z)
178
+ for p in p_values:
179
+ s = z + xi / math.sqrt(p)
180
+ c = torch.tensor(p / (p + 1.0), device=device, dtype=dtype)
181
+ for _ in range(20):
182
+ pred = torch.sigmoid(c * s)
183
+ grad = torch.mean(s * (pred - target))
184
+ hess = torch.mean(s * s * pred * (1.0 - pred))
185
+ step = grad / hess
186
+ c = c - step
187
+ if float(torch.abs(step).cpu()) < 1e-12:
188
+ break
189
+ loss = torch.mean(torch.nn.functional.softplus(c * s) - target * c * s)
190
+ rows.append({
191
+ "seed": seed,
192
+ "p": p,
193
+ "samples": samples,
194
+ "device": device,
195
+ "c": float(c.cpu()),
196
+ "excess": float((loss - optimum).cpu()),
197
+ })
198
+ del z, xi, target
199
+ return rows
200
+
201
+
202
+ def idealized_lower_curve(gbce: GaussianBCE, p_values: list[int]) -> list[dict[str, float]]:
203
+ """Optimize c in c(Z + xi/sqrt(p)), the form used in Theorem 4.5."""
204
+ rows: list[dict[str, float]] = []
205
+ optimum_a = np.array([1.0])
206
+ optimum = gbce.loss(optimum_a)
207
+ for p in p_values:
208
+ # In coefficient space over independent (xi, Z), the candidate is
209
+ # [c/sqrt(p), c], while the true logit is Z.
210
+ def objective(c_array: np.ndarray) -> float:
211
+ c = float(c_array[0])
212
+ return gbce.loss(np.array([c / math.sqrt(p), c]))
213
+
214
+ result = minimize(objective, np.array([p / (p + 1.0)]), method="BFGS", options={"gtol": 1e-13, "maxiter": 100})
215
+ c = float(result.x[0])
216
+ excess = float(result.fun - optimum)
217
+ rows.append({"p": p, "c": c, "excess": excess, "p_excess": p * excess})
218
+ return rows
219
+
220
+
221
+ def write_csv(path: Path, rows: list[dict[str, Any]]) -> None:
222
+ path.parent.mkdir(parents=True, exist_ok=True)
223
+ if not rows:
224
+ path.write_text("", encoding="utf-8")
225
+ return
226
+ fields = list(rows[0])
227
+ with path.open("w", newline="", encoding="utf-8") as f:
228
+ writer = csv.DictWriter(f, fieldnames=fields)
229
+ writer.writeheader()
230
+ writer.writerows(rows)
231
+
232
+
233
+ def svg_lower_bound(path: Path, rows: list[dict[str, Any]]) -> None:
234
+ points = [(r["p"], r["excess"]) for r in rows if r["k"] == max(x["k"] for x in rows)]
235
+ width, height, margin = 800, 500, 70
236
+ xs = np.log10([p for p, _ in points]); ys = np.log10([e for _, e in points])
237
+ xmin, xmax = float(xs.min()), float(xs.max()); ymin, ymax = float(ys.min()), float(ys.max())
238
+ def sx(x: float) -> float: return margin + (x - xmin) / (xmax - xmin) * (width - 2 * margin)
239
+ def sy(y: float) -> float: return height - margin - (y - ymin) / (ymax - ymin) * (height - 2 * margin)
240
+ poly = " ".join(f"{sx(x):.1f},{sy(y):.1f}" for x, y in zip(xs, ys))
241
+ circles = "\n".join(f'<circle cx="{sx(x):.1f}" cy="{sy(y):.1f}" r="5" fill="#2563eb"/>' for x, y in zip(xs, ys))
242
+ content = f'''<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="0 0 {width} {height}">
243
+ <rect width="100%" height="100%" fill="white"/><line x1="{margin}" y1="{height-margin}" x2="{width-margin}" y2="{height-margin}" stroke="black"/><line x1="{margin}" y1="{margin}" x2="{margin}" y2="{height-margin}" stroke="black"/>
244
+ <polyline points="{poly}" fill="none" stroke="#2563eb" stroke-width="3"/>{circles}
245
+ <text x="{width/2}" y="35" text-anchor="middle" font-family="sans-serif" font-size="22">Hard-instance BCE excess vs passes (k={max(x['k'] for x in rows)})</text>
246
+ <text x="{width/2}" y="{height-18}" text-anchor="middle" font-family="sans-serif" font-size="17">log10(passes p)</text>
247
+ <text x="18" y="{height/2}" transform="rotate(-90 18 {height/2})" text-anchor="middle" font-family="sans-serif" font-size="17">log10(excess BCE)</text></svg>'''
248
+ path.write_text(content, encoding="utf-8")
249
+
250
+
251
+ def main() -> None:
252
+ parser = argparse.ArgumentParser()
253
+ parser.add_argument("--mode", choices=["smoke", "substantive"], default="smoke")
254
+ parser.add_argument("--output", type=Path, required=True)
255
+ parser.add_argument("--device", choices=["auto", "cpu", "cuda"], default="auto")
256
+ parser.add_argument("--mc-samples", type=int, default=None)
257
+ args = parser.parse_args()
258
+ started = time.time()
259
+ args.output.mkdir(parents=True, exist_ok=True)
260
+ order = 80 if args.mode == "smoke" else 140
261
+ ks = [4, 8] if args.mode == "smoke" else [4, 8, 12, 16, 24]
262
+ mc_samples = args.mc_samples or (50_000 if args.mode == "smoke" else 2_000_000)
263
+ if args.device == "auto":
264
+ device = "cuda" if torch is not None and torch.cuda.is_available() else "cpu"
265
+ else:
266
+ device = args.device
267
+ if device == "cuda" and (torch is None or not torch.cuda.is_available()):
268
+ raise RuntimeError("CUDA requested but unavailable")
269
+
270
+ gbce = GaussianBCE(order)
271
+ all_records: list[AgentRecord] = []
272
+ lower_rows: list[dict[str, Any]] = []
273
+ identity_rows: list[dict[str, Any]] = []
274
+ max_stationarity = 0.0
275
+ max_off_relevant = 0.0
276
+ for k in ks:
277
+ recs, coeffs = simulate_path(k, k - 1, gbce)
278
+ all_records.extend(recs)
279
+ global_a = np.zeros(k); global_a[-1] = 1.0
280
+ global_loss = gbce.loss(global_a)
281
+ for p in range(1, k):
282
+ idx = p * k - 1
283
+ rec, a = recs[idx], coeffs[idx]
284
+ lower_rows.append({"k": k, "p": p, "D": k * p, "loss": rec.loss, "global_loss": global_loss, "excess": rec.excess, "scaled_p_excess": p * rec.excess, "upper_bound": k * math.sqrt(2.0) * k / math.sqrt(k * p), "bound_ratio": rec.excess / (k * math.sqrt(2.0) * k / math.sqrt(k * p))})
285
+ max_off_relevant = max(max_off_relevant, rec.off_relevant_l2)
286
+ max_stationarity = max(max_stationarity, max(r.stationarity_max_abs for r in recs))
287
+
288
+ # Global decomposition and Pinsker check for the final predictor.
289
+ q = coeffs[-1]
290
+ joint = gbce.joint_expectations(global_a, q)
291
+ loss_gap = gbce.loss(q) - global_loss
292
+ identity_rows.append({"kind": "global", "k": k, "depth": k * (k - 1), "loss_gap": loss_gap, "kl": joint["kl"], "identity_abs_error": abs(loss_gap - joint["kl"]), "probability_mse": joint["probability_mse"], "pinsker_slack": joint["kl"] - 2.0 * joint["probability_mse"]})
293
+
294
+ # Operational decomposition for every transition: current local optimum vs parent.
295
+ zero = np.zeros(k)
296
+ for i, (rec, cur) in enumerate(zip(recs, coeffs)):
297
+ prev = zero if i == 0 else coeffs[i - 1]
298
+ joint_step = gbce.joint_expectations(cur, prev, order=40)
299
+ gap = gbce.loss(prev) - gbce.loss(cur)
300
+ identity_rows.append({"kind": "transition", "k": k, "depth": i + 1, "loss_gap": gap, "kl": joint_step["kl"], "identity_abs_error": abs(gap - joint_step["kl"]), "probability_mse": joint_step["probability_mse"], "pinsker_slack": joint_step["kl"] - 2.0 * joint_step["probability_mse"]})
301
+
302
+ slope_rows = []
303
+ for k in ks:
304
+ subset = [r for r in lower_rows if r["k"] == k]
305
+ fit = linear_fit(np.log([r["p"] for r in subset]), np.log([r["excess"] for r in subset]))
306
+ slope_rows.append({"k": k, "n_points": len(subset), **fit})
307
+
308
+ asymptotic_ps = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]
309
+ ideal_rows = idealized_lower_curve(gbce, asymptotic_ps)
310
+ ideal_fit_all = linear_fit(np.log([r["p"] for r in ideal_rows]), np.log([r["excess"] for r in ideal_rows]))
311
+ ideal_tail = [r for r in ideal_rows if r["p"] >= 128]
312
+ ideal_fit_tail = linear_fit(np.log([r["p"] for r in ideal_tail]), np.log([r["excess"] for r in ideal_tail]))
313
+
314
+ mc_ps = asymptotic_ps if args.mode == "substantive" else [1, 2, 4, 8, 16, 32, 64]
315
+ seeds = [11] if args.mode == "smoke" else [11, 23, 37]
316
+ mc_rows = monte_carlo_lower(mc_ps, mc_samples, seeds, device)
317
+ mc_agg = []
318
+ for p in mc_ps:
319
+ vals = [r["excess"] for r in mc_rows if r["p"] == p]
320
+ mc_agg.append({"p": p, "mean_excess": float(np.mean(vals)), "std_excess": float(np.std(vals, ddof=1)) if len(vals) > 1 else 0.0, "mean_p_excess": float(p * np.mean(vals))})
321
+ mc_fit = linear_fit(np.log([r["p"] for r in mc_agg]), np.log([r["mean_excess"] for r in mc_agg]))
322
+
323
+ agent_rows = [asdict(r) for r in all_records]
324
+ write_csv(args.output / "agent_trajectory.csv", agent_rows)
325
+ write_csv(args.output / "lower_bound.csv", lower_rows)
326
+ write_csv(args.output / "decomposition_pinsker.csv", identity_rows)
327
+ write_csv(args.output / "lower_slopes.csv", slope_rows)
328
+ write_csv(args.output / "idealized_lower_curve.csv", ideal_rows)
329
+ write_csv(args.output / "monte_carlo.csv", mc_rows)
330
+ write_csv(args.output / "monte_carlo_aggregate.csv", mc_agg)
331
+ svg_lower_bound(args.output / "lower_bound.svg", lower_rows)
332
+
333
+ summary = {
334
+ "paper": {"title": "Networked Information Aggregation for Binary Classification", "arxiv_id": "2605.01082", "openreview_id": "mrtg4NmvAe"},
335
+ "mode": args.mode,
336
+ "method": "deterministic population sequential-logit simulation plus independent Monte Carlo lower-bound check",
337
+ "quadrature_order": order,
338
+ "k_values": ks,
339
+ "mc": {"samples_per_seed": mc_samples, "seeds": seeds, "device": device, "fit_log_excess_vs_log_p": mc_fit},
340
+ "invariants": {
341
+ "max_agent_stationarity_abs": max_stationarity,
342
+ "max_end_of_pass_off_relevant_l2": max_off_relevant,
343
+ "max_loss_decomposition_abs_error": max(r["identity_abs_error"] for r in identity_rows),
344
+ "min_pinsker_slack": min(r["pinsker_slack"] for r in identity_rows),
345
+ },
346
+ "lower_bound_fits": slope_rows,
347
+ "idealized_lower_bound_fit": {"all_points": ideal_fit_all, "tail_p_ge_128": ideal_fit_tail, "tail_p_excess_range": [min(r["p_excess"] for r in ideal_tail), max(r["p_excess"] for r in ideal_tail)]},
348
+ "upper_bound_check": {"all_excess_below_theorem_bound": all(r["excess"] <= r["upper_bound"] + 1e-12 for r in lower_rows), "max_excess_to_bound_ratio": max(r["bound_ratio"] for r in lower_rows), "note": "The theorem bound is valid but loose on its own hard instance; this check does not establish worst-case tightness."},
349
+ "environment": {
350
+ "python": platform.python_version(),
351
+ "platform": platform.platform(),
352
+ "numpy": np.__version__,
353
+ "scipy": __import__("scipy").__version__,
354
+ "torch": getattr(torch, "__version__", None),
355
+ "cuda_available": bool(torch is not None and torch.cuda.is_available()),
356
+ "cuda_device": torch.cuda.get_device_name(0) if torch is not None and torch.cuda.is_available() else None,
357
+ },
358
+ "wall_seconds": time.time() - started,
359
+ }
360
+ (args.output / "summary.json").write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8")
361
+ print(json.dumps(summary, indent=2))
362
+
363
+
364
+ if __name__ == "__main__":
365
+ main()
366
+
367
+ ````
368
+
369
+
370
+ ````output
371
+ /home/floor/.hermes/hermes-agent/venv/lib/python3.11/site-packages/torch/cuda/__init__.py:188: UserWarning: CUDA initialization: The NVIDIA driver on your system is too old (found version 12020). Please update your GPU driver by downloading and installing a new version from the URL: http://www.nvidia.com/Download/index.aspx Alternatively, go to: https://pytorch.org to install a PyTorch version that has been compiled with your version of the CUDA driver. (Triggered internally at /__w/pytorch/pytorch/c10/cuda/CUDAFunctions.cpp:119.)
372
+ return torch._C._cuda_getDeviceCount() > 0
373
+ {
374
+ "paper": {
375
+ "title": "Networked Information Aggregation for Binary Classification",
376
+ "arxiv_id": "2605.01082",
377
+ "openreview_id": "mrtg4NmvAe"
378
+ },
379
+ "mode": "substantive",
380
+ "method": "deterministic population sequential-logit simulation plus independent Monte Carlo lower-bound check",
381
+ "quadrature_order": 140,
382
+ "k_values": [
383
+ 4,
384
+ 8,
385
+ 12,
386
+ 16,
387
+ 24
388
+ ],
389
+ "mc": {
390
+ "samples_per_seed": 500000,
391
+ "seeds": [
392
+ 11,
393
+ 23,
394
+ 37
395
+ ],
396
+ "device": "cpu",
397
+ "fit_log_excess_vs_log_p": {
398
+ "slope": -0.9350206812289982,
399
+ "intercept": -2.6686461943304782,
400
+ "r2": 0.9968147767574095
401
+ }
402
+ },
403
+ "invariants": {
404
+ "max_agent_stationarity_abs": 1.0819462638311794e-08,
405
+ "max_end_of_pass_off_relevant_l2": 0.0,
406
+ "max_loss_decomposition_abs_error": 6.465142761424847e-10,
407
+ "min_pinsker_slack": -7.200367990754695e-17
408
+ },
409
+ "lower_bound_fits": [
410
+ {
411
+ "k": 4,
412
+ "n_points": 3,
413
+ "slope": -0.5878402635155259,
414
+ "intercept": -3.009358647585332,
415
+ "r2": 0.9986958337218635
416
+ },
417
+ {
418
+ "k": 8,
419
+ "n_points": 7,
420
+ "slope": -0.6076749374430602,
421
+ "intercept": -3.0013797439977976,
422
+ "r2": 0.999417157745313
423
+ },
424
+ {
425
+ "k": 12,
426
+ "n_points": 11,
427
+ "slope": -0.6038111334261702,
428
+ "intercept": -3.0048149948891894,
429
+ "r2": 0.999637858355033
430
+ },
431
+ {
432
+ "k": 16,
433
+ "n_points": 15,
434
+ "slope": -0.5981752677434371,
435
+ "intercept": -3.011061460264441,
436
+ "r2": 0.9995887771225648
437
+ },
438
+ {
439
+ "k": 24,
440
+ "n_points": 23,
441
+ "slope": -0.588362084486772,
442
+ "intercept": -3.0245264202136557,
443
+ "r2": 0.9993707299478031
444
+ }
445
+ ],
446
+ "idealized_lower_bound_fit": {
447
+ "all_points": {
448
+ "slope": -0.9352501163874916,
449
+ "intercept": -2.6695775957507704,
450
+ "r2": 0.9968323922882505
451
+ },
452
+ "tail_p_ge_128": {
453
+ "slope": -0.9977674521988302,
454
+ "intercept": -2.287549895506178,
455
+ "r2": 0.9999989096931335
456
+ },
457
+ "tail_p_excess_range": [
458
+ 0.10242379279748093,
459
+ 0.10328253938041598
460
+ ]
461
+ },
462
+ "upper_bound_check": {
463
+ "all_excess_below_theorem_bound": true,
464
+ "max_excess_to_bound_ratio": 0.004337979502507641,
465
+ "note": "The theorem bound is valid but loose on its own hard instance; this check does not establish worst-case tightness."
466
+ },
467
+ "environment": {
468
+ "python": "3.11.15",
469
+ "platform": "Linux-6.17.0-23-generic-x86_64-with-glibc2.39",
470
+ "numpy": "2.4.3",
471
+ "scipy": "1.17.1",
472
+ "torch": "2.13.0+cu130",
473
+ "cuda_available": false,
474
+ "cuda_device": null
475
+ },
476
+ "wall_seconds": 8.304808855056763
477
+ }
478
+
479
+ ````
480
+
481
+
482
+ ---
483
+ <!-- trackio-cell
484
+ {"type": "artifact", "id": "cell_a01c54f103de", "created_at": "2026-07-16T12:25:50+00:00", "title": "Artifact: agent_trajectory.csv", "path": "outputs/substantive/agent_trajectory.csv", "size": 104850, "artifact_type": "dataset", "auto": true}
485
+ -->
486
+ **📦 Artifact** `outputs/substantive/agent_trajectory.csv` · dataset · 0.1 MB
487
+
488
+ https://huggingface.co/buckets/FloorIsAwake/repro-networked-information-aggregation-binary-classification-artifacts#logbook-files/outputs/substantive/agent_trajectory.csv
489
+
490
+
491
+ ---
492
+ <!-- trackio-cell
493
+ {"type": "artifact", "id": "cell_c86630e7f9bd", "created_at": "2026-07-16T12:25:50+00:00", "title": "Artifact: decomposition_pinsker.csv", "path": "outputs/substantive/decomposition_pinsker.csv", "size": 88038, "artifact_type": "dataset", "auto": true}
494
+ -->
495
+ **📦 Artifact** `outputs/substantive/decomposition_pinsker.csv` · dataset · 88.0 kB
496
+
497
+ https://huggingface.co/buckets/FloorIsAwake/repro-networked-information-aggregation-binary-classification-artifacts#logbook-files/outputs/substantive/decomposition_pinsker.csv
498
+
499
+
500
+ ---
501
+ <!-- trackio-cell
502
+ {"type": "artifact", "id": "cell_e18cec9d8c6d", "created_at": "2026-07-16T12:25:50+00:00", "title": "Artifact: lower_bound.csv", "path": "outputs/substantive/lower_bound.csv", "size": 7613, "artifact_type": "dataset", "auto": true}
503
+ -->
504
+ **📦 Artifact** `outputs/substantive/lower_bound.csv` · dataset · 7.6 kB
505
+
506
+ https://huggingface.co/buckets/FloorIsAwake/repro-networked-information-aggregation-binary-classification-artifacts#logbook-files/outputs/substantive/lower_bound.csv
507
+
508
+
509
+ ---
510
+ <!-- trackio-cell
511
+ {"type": "artifact", "id": "cell_74c82bd9f334", "created_at": "2026-07-16T12:25:50+00:00", "title": "Artifact: monte_carlo.csv", "path": "outputs/substantive/monte_carlo.csv", "size": 2334, "artifact_type": "dataset", "auto": true}
512
+ -->
513
+ **📦 Artifact** `outputs/substantive/monte_carlo.csv` · dataset · 2.3 kB
514
+
515
+ https://huggingface.co/buckets/FloorIsAwake/repro-networked-information-aggregation-binary-classification-artifacts#logbook-files/outputs/substantive/monte_carlo.csv
516
+
517
+
518
+ ---
519
+ <!-- trackio-cell
520
+ {"type": "artifact", "id": "cell_bf39ecb39c71", "created_at": "2026-07-16T12:25:50+00:00", "title": "Artifact: monte_carlo_aggregate.csv", "path": "outputs/substantive/monte_carlo_aggregate.csv", "size": 927, "artifact_type": "dataset", "auto": true}
521
+ -->
522
+ **📦 Artifact** `outputs/substantive/monte_carlo_aggregate.csv` · dataset · 927 B
523
+
524
+ https://huggingface.co/buckets/FloorIsAwake/repro-networked-information-aggregation-binary-classification-artifacts#logbook-files/outputs/substantive/monte_carlo_aggregate.csv
525
+
526
+
527
+ ---
528
+ <!-- trackio-cell
529
+ {"type": "artifact", "id": "cell_d5b912639de0", "created_at": "2026-07-16T12:25:50+00:00", "title": "Artifact: idealized_lower_curve.csv", "path": "outputs/substantive/idealized_lower_curve.csv", "size": 865, "artifact_type": "dataset", "auto": true}
530
+ -->
531
+ **📦 Artifact** `outputs/substantive/idealized_lower_curve.csv` · dataset · 865 B
532
+
533
+ https://huggingface.co/buckets/FloorIsAwake/repro-networked-information-aggregation-binary-classification-artifacts#logbook-files/outputs/substantive/idealized_lower_curve.csv
534
+
535
+
536
+ ---
537
+ <!-- trackio-cell
538
+ {"type": "artifact", "id": "cell_3f81f4cba2d2", "created_at": "2026-07-16T12:25:50+00:00", "title": "Artifact: lower_slopes.csv", "path": "outputs/substantive/lower_slopes.csv", "size": 352, "artifact_type": "dataset", "auto": true}
539
+ -->
540
+ **📦 Artifact** `outputs/substantive/lower_slopes.csv` · dataset · 352 B
541
+
542
+ https://huggingface.co/buckets/FloorIsAwake/repro-networked-information-aggregation-binary-classification-artifacts#logbook-files/outputs/substantive/lower_slopes.csv
543
+
544
+
545
+ ---
546
+ <!-- trackio-cell
547
+ {"type": "markdown", "id": "cell_e27c3e57a0ab", "created_at": "2026-07-16T12:29:16+00:00", "title": "Claim 2 audit and asymptotic reproduction"}
548
+ -->
549
+ **Verdict: the `Omega(k/D)` result is supported; the word “matching” is not.** Theorem 4.5 (PDF page 8) constructs `x_1=Z_1`, `x_i=Z_i-Z_{i-1}`, target probability `sigmoid(Z_k)`, and a cyclic one-feature-per-agent path. At the end of pass `p<=k-1`, `D=kp` and the theorem proves excess BCE `Omega(1/p)=Omega(k/D)`.
550
+
551
+ Population sequential simulation covered k in {4,8,12,16,24} and 992 total agent stages. The end-of-pass coefficient norm outside the theorem's relevant suffix was exactly 0. For the theorem's idealized estimator `c(Z_k+xi/sqrt(p))`, the deterministic tail fit over `p>=128` was slope -0.997767 with R² 0.9999989; `p*excess` remained in [0.102424, 0.103283]. The independent Monte Carlo tail slope was -0.997742 and differed from quadrature by at most 0.2406%.
552
+
553
+ The lower rate is scoped to the constructed cyclic path and `D=kp`, `p<=k-1`. It does **not** match the upper theorem's D exponent (`1/D` versus `1/sqrt(D)`); the paper itself calls it a “close lower bound.”
554
+
555
+
556
+ ---
557
+ <!-- trackio-cell
558
+ {"type": "figure", "id": "cell_b0c0282c8785", "created_at": "2026-07-16T12:29:17+00:00", "title": "Hard-instance excess BCE by pass"}
559
+ -->
560
+ ````html
561
+ <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MDAiIGhlaWdodD0iNTAwIiB2aWV3Qm94PSIwIDAgODAwIDUwMCI+CjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IndoaXRlIi8+PGxpbmUgeDE9IjcwIiB5MT0iNDMwIiB4Mj0iNzMwIiB5Mj0iNDMwIiBzdHJva2U9ImJsYWNrIi8+PGxpbmUgeDE9IjcwIiB5MT0iNzAiIHgyPSI3MCIgeTI9IjQzMCIgc3Ryb2tlPSJibGFjayIvPgo8cG9seWxpbmUgcG9pbnRzPSI3MC4wLDcwLjAgMjE1LjksMTQ2LjIgMzAxLjMsMTk3LjEgMzYxLjgsMjMyLjkgNDA4LjgsMjU5LjggNDQ3LjIsMjgxLjQgNDc5LjYsMjk5LjIgNTA3LjcsMzE0LjUgNTMyLjUsMzI3LjggNTU0LjcsMzM5LjYgNTc0LjcsMzUwLjIgNTkzLjEsMzU5LjggNjA5LjksMzY4LjYgNjI1LjUsMzc2LjcgNjQwLjAsMzg0LjIgNjUzLjYsMzkxLjIgNjY2LjQsMzk3LjcgNjc4LjQsNDAzLjkgNjg5LjgsNDA5LjcgNzAwLjYsNDE1LjEgNzEwLjksNDIwLjMgNzIwLjYsNDI1LjMgNzMwLjAsNDMwLjAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzI1NjNlYiIgc3Ryb2tlLXdpZHRoPSIzIi8+PGNpcmNsZSBjeD0iNzAuMCIgY3k9IjcwLjAiIHI9IjUiIGZpbGw9IiMyNTYzZWIiLz4KPGNpcmNsZSBjeD0iMjE1LjkiIGN5PSIxNDYuMiIgcj0iNSIgZmlsbD0iIzI1NjNlYiIvPgo8Y2lyY2xlIGN4PSIzMDEuMyIgY3k9IjE5Ny4xIiByPSI1IiBmaWxsPSIjMjU2M2ViIi8+CjxjaXJjbGUgY3g9IjM2MS44IiBjeT0iMjMyLjkiIHI9IjUiIGZpbGw9IiMyNTYzZWIiLz4KPGNpcmNsZSBjeD0iNDA4LjgiIGN5PSIyNTkuOCIgcj0iNSIgZmlsbD0iIzI1NjNlYiIvPgo8Y2lyY2xlIGN4PSI0NDcuMiIgY3k9IjI4MS40IiByPSI1IiBmaWxsPSIjMjU2M2ViIi8+CjxjaXJjbGUgY3g9IjQ3OS42IiBjeT0iMjk5LjIiIHI9IjUiIGZpbGw9IiMyNTYzZWIiLz4KPGNpcmNsZSBjeD0iNTA3LjciIGN5PSIzMTQuNSIgcj0iNSIgZmlsbD0iIzI1NjNlYiIvPgo8Y2lyY2xlIGN4PSI1MzIuNSIgY3k9IjMyNy44IiByPSI1IiBmaWxsPSIjMjU2M2ViIi8+CjxjaXJjbGUgY3g9IjU1NC43IiBjeT0iMzM5LjYiIHI9IjUiIGZpbGw9IiMyNTYzZWIiLz4KPGNpcmNsZSBjeD0iNTc0LjciIGN5PSIzNTAuMiIgcj0iNSIgZmlsbD0iIzI1NjNlYiIvPgo8Y2lyY2xlIGN4PSI1OTMuMSIgY3k9IjM1OS44IiByPSI1IiBmaWxsPSIjMjU2M2ViIi8+CjxjaXJjbGUgY3g9IjYwOS45IiBjeT0iMzY4LjYiIHI9IjUiIGZpbGw9IiMyNTYzZWIiLz4KPGNpcmNsZSBjeD0iNjI1LjUiIGN5PSIzNzYuNyIgcj0iNSIgZmlsbD0iIzI1NjNlYiIvPgo8Y2lyY2xlIGN4PSI2NDAuMCIgY3k9IjM4NC4yIiByPSI1IiBmaWxsPSIjMjU2M2ViIi8+CjxjaXJjbGUgY3g9IjY1My42IiBjeT0iMzkxLjIiIHI9IjUiIGZpbGw9IiMyNTYzZWIiLz4KPGNpcmNsZSBjeD0iNjY2LjQiIGN5PSIzOTcuNyIgcj0iNSIgZmlsbD0iIzI1NjNlYiIvPgo8Y2lyY2xlIGN4PSI2NzguNCIgY3k9IjQwMy45IiByPSI1IiBmaWxsPSIjMjU2M2ViIi8+CjxjaXJjbGUgY3g9IjY4OS44IiBjeT0iNDA5LjciIHI9IjUiIGZpbGw9IiMyNTYzZWIiLz4KPGNpcmNsZSBjeD0iNzAwLjYiIGN5PSI0MTUuMSIgcj0iNSIgZmlsbD0iIzI1NjNlYiIvPgo8Y2lyY2xlIGN4PSI3MTAuOSIgY3k9IjQyMC4zIiByPSI1IiBmaWxsPSIjMjU2M2ViIi8+CjxjaXJjbGUgY3g9IjcyMC42IiBjeT0iNDI1LjMiIHI9IjUiIGZpbGw9IiMyNTYzZWIiLz4KPGNpcmNsZSBjeD0iNzMwLjAiIGN5PSI0MzAuMCIgcj0iNSIgZmlsbD0iIzI1NjNlYiIvPgo8dGV4dCB4PSI0MDAuMCIgeT0iMzUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjIyIj5IYXJkLWluc3RhbmNlIEJDRSBleGNlc3MgdnMgcGFzc2VzIChrPTI0KTwvdGV4dD4KPHRleHQgeD0iNDAwLjAiIHk9IjQ4MiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTciPmxvZzEwKHBhc3NlcyBwKTwvdGV4dD4KPHRleHQgeD0iMTgiIHk9IjI1MC4wIiB0cmFuc2Zvcm09InJvdGF0ZSgtOTAgMTggMjUwLjApIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNyI+bG9nMTAoZXhjZXNzIEJDRSk8L3RleHQ+PC9zdmc+" alt="lower_bound" style="max-width:100%;height:auto;" />
562
+ ````
563
+
564
+ ````raw
565
+ p,c,excess,p_excess
566
+ 1,0.4520013086348428,0.049078635565942275,0.049078635565942275
567
+ 2,0.6223693200355696,0.03326183690122464,0.06652367380244928
568
+ 4,0.7670829656213174,0.020229940118988265,0.08091976047595306
569
+ 8,0.86812058257218,0.011343581712249029,0.09074865369799223
570
+ 16,0.9293802201156757,0.006038811854166548,0.09662098966666477
571
+ 32,0.9633901567599166,0.0031204134510443016,0.09985323043341765
572
+ 64,0.9813516978157595,0.0015867543421363983,0.10155227789672949
573
+ 128,0.9905875182183287,0.0008001858812303198,0.10242379279748093
574
+ 256,0.9952713392435402,0.0004018172493100991,0.10286521582338537
575
+ 512,0.9976300177343294,0.00020134251053682828,0.10308736539485608
576
+ 1024,0.9988134987680994,0.00010078008051639387,0.10319880244878732
577
+ 2048,0.999406422974884,5.041729096122438e-05,0.10325461188858753
578
+ 4096,0.9997031564296578,2.521546371592187e-05,0.10328253938041598
579
+
580
+ ````
581
+
582
+
583
+ ---
584
+ <!-- trackio-cell
585
+ {"type": "markdown", "id": "cell_a5d6ae8aa1c7", "created_at": "2026-07-16T12:50:17+00:00", "title": "Asymptotic regime and sample-count clarification"}
586
+ -->
587
+ Scope clarification for the asymptotic curve: the direct sequential simulations use k in {4,8,12,16,24}, totaling 992 agent stages and 59 end-of-pass checkpoints. The separate idealized curve evaluates the Theorem 4.5 estimator through p=4096; each plotted p is theorem-compatible by choosing a construction member with k>=p+1, so p<=k-1 and D=kp. The numerical observation is C/p scaling; the theorem itself proves only the lower bound Omega(1/p). Monte Carlo uses 500,000 samples per seed for seeds 11, 23, and 37, fitted after averaging seed-level excess.
pages/claim-3-bce-residual-orthogonality/page.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 3 - BCE residual orthogonality
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_a9f6d9c9da9e", "created_at": "2026-07-16T12:29:18+00:00", "title": "Claim 3 derivation and stationarity check"}
7
+ -->
8
+ **Verdict: supported.** Lemma 3.1 (PDF page 3) differentiates logistic BCE to obtain
9
+
10
+ `grad_theta L(theta) = E[x(sigmoid(theta^T x)-y)]`.
11
+
12
+ At a finite unconstrained optimum, first-order stationarity gives `E[x(p*(x)-y)]=0`. This is the BCE analogue of the squared-loss projection orthogonality used by prior networked-regression analyses. Across 992 population agent optimizations, the maximum absolute stationarity residual in each agent's available span was `1.08195e-8`.
13
+
14
+ Scope caveat: a finite optimum must exist; separable logistic data can have an unattained infimum.
pages/claim-4-bce-kl-decomposition/page.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 4 - BCE KL decomposition
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_9c58971f27c9", "created_at": "2026-07-16T12:29:19+00:00", "title": "Claim 4 decomposition and Pinsker check"}
7
+ -->
8
+ **Verdict: supported.** Lemma 3.3 (PDF page 4) states that, for the optimal logistic predictor `p*` on feature set S and any logistic predictor `q` on the same S,
9
+
10
+ `L(q)=L(p*)+E_x KL(Bern(p*(x)) || Bern(q(x)))`.
11
+
12
+ The orthogonality term from Lemma 3.1 cancels, leaving the Bernoulli log-partition Bregman divergence. Lemma 3.4 then gives `D(p||q)>=2 E[(p-q)^2]`. Across global and consecutive-agent checks, the maximum absolute loss-gap/KL identity error was `6.46514e-10`; minimum Pinsker slack was `-7.20e-17`, numerical zero at quadrature precision.
pages/claim-5-sequential-logit-protocol/page.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 5 - Sequential logit protocol
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_c15950a5d6d1", "created_at": "2026-07-16T12:29:20+00:00", "title": "Claim 5 protocol audit"}
7
+ -->
8
+ **Verdict: supported.** Section 2 (PDF page 3) defines a topologically ordered DAG. Agent i sees only local features `x_{S_i}` and parent **logits** `z_j`, then minimizes expected BCE for
9
+
10
+ `z_i(x)=w_i^T x_{S_i} + sum_{j in Pa(i)} v_ij z_j(x)`,
11
+ `p_i(x)=sigmoid(z_i(x))`.
12
+
13
+ The reproduction implements exactly this logit-span update on a path; it never substitutes probabilities for parent logits. Loss was non-increasing at every within-k trajectory transition, and the hard-instance relevant-information suffix invariant held exactly. The abstract loosely says “prediction columns,” but the formal protocol and proof use logits.
pages/conclusion/page.md ADDED
The diff for this file is too large to render. See raw diff
 
pages/index.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Repro - Networked Information Aggregation for Binary Classification
2
+
3
+ ## Pages
4
+
5
+ | Page |
6
+ | --- |
7
+ | [Sources and methodology](#/sources-and-methodology) |
8
+ | [Claim 1 - Upper bound under M-coverage](#/claim-1-upper-bound-under-m-coverage) |
9
+ | [Claim 2 - Lower bound k over D](#/claim-2-lower-bound-k-over-d) |
10
+ | [Claim 3 - BCE residual orthogonality](#/claim-3-bce-residual-orthogonality) |
11
+ | [Claim 4 - BCE KL decomposition](#/claim-4-bce-kl-decomposition) |
12
+ | [Claim 5 - Sequential logit protocol](#/claim-5-sequential-logit-protocol) |
13
+ | [Conclusion](#/conclusion) |
pages/sources-and-methodology/page.md ADDED
@@ -0,0 +1,421 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Sources and methodology
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "code", "id": "cell_290f4467d74e", "created_at": "2026-07-16T12:24:02+00:00", "title": "Local deterministic smoke test", "command": ["python", "repro.py", "--mode", "smoke", "--output", "outputs/smoke", "--device", "cpu"], "exit_code": 0, "duration_s": 7.498}
7
+ -->
8
+ ````bash
9
+ $ python repro.py --mode smoke --output outputs/smoke --device cpu
10
+ ````
11
+
12
+ exit 0 · 7.5s
13
+
14
+
15
+ ````python title=repro.py
16
+ #!/usr/bin/env python3
17
+ """Deterministic and Monte Carlo checks for arXiv:2605.01082.
18
+
19
+ The population sequential protocol is simulated exactly in coefficient space.
20
+ For the paper's Gaussian hard instance, every logit is a linear form a^T Z,
21
+ where Z ~ N(0, I). Population BCE therefore depends only on ||a|| and a_k.
22
+ Each agent minimizes BCE over the span of its local-feature logit and its
23
+ parent's logit, exactly matching Section 2's logit-passing protocol.
24
+ """
25
+ from __future__ import annotations
26
+
27
+ import argparse
28
+ import csv
29
+ import json
30
+ import math
31
+ import os
32
+ import platform
33
+ import subprocess
34
+ import time
35
+ from dataclasses import asdict, dataclass
36
+ from pathlib import Path
37
+ from typing import Any
38
+
39
+ import numpy as np
40
+ from scipy.optimize import minimize
41
+ from scipy.special import expit, roots_hermitenorm
42
+
43
+ try:
44
+ import torch
45
+ except ImportError: # Monte Carlo is optional for the deterministic smoke test.
46
+ torch = None
47
+
48
+
49
+ @dataclass
50
+ class AgentRecord:
51
+ k: int
52
+ agent: int
53
+ depth: int
54
+ pass_index: int
55
+ feature: int
56
+ loss: float
57
+ excess: float
58
+ local_weight: float
59
+ parent_weight: float
60
+ stationarity_max_abs: float
61
+ off_relevant_l2: float
62
+
63
+
64
+ def softplus(x: np.ndarray) -> np.ndarray:
65
+ return np.logaddexp(0.0, x)
66
+
67
+
68
+ class GaussianBCE:
69
+ """Gauss-Hermite population integrals for centered Gaussian logits."""
70
+
71
+ def __init__(self, order: int = 100):
72
+ nodes, weights = roots_hermitenorm(order)
73
+ self.nodes = nodes.astype(np.float64)
74
+ self.weights = (weights / math.sqrt(2.0 * math.pi)).astype(np.float64)
75
+ self.corr = float(np.sum(self.weights * self.nodes * expit(self.nodes)))
76
+
77
+ def loss(self, a: np.ndarray) -> float:
78
+ std = float(np.linalg.norm(a))
79
+ expected_softplus = float(np.sum(self.weights * softplus(std * self.nodes)))
80
+ return expected_softplus - self.corr * float(a[-1])
81
+
82
+ def optimize_span(self, local: np.ndarray, parent: np.ndarray) -> tuple[np.ndarray, np.ndarray, float]:
83
+ basis = np.column_stack([local, parent])
84
+
85
+ def objective(w: np.ndarray) -> float:
86
+ return self.loss(basis @ w)
87
+
88
+ def gradient(w: np.ndarray) -> np.ndarray:
89
+ a = basis @ w
90
+ std = float(np.linalg.norm(a))
91
+ if std < 1e-14:
92
+ grad_a = -self.corr * np.eye(1, len(a), len(a) - 1, dtype=np.float64).ravel()
93
+ else:
94
+ radial = float(np.sum(self.weights * expit(std * self.nodes) * self.nodes)) / std
95
+ grad_a = radial * a
96
+ grad_a[-1] -= self.corr
97
+ return basis.T @ grad_a
98
+
99
+ result = minimize(objective, np.array([0.0, 1.0]), jac=gradient, method="BFGS", options={"gtol": 1e-12, "maxiter": 200})
100
+ # BFGS can report precision loss after already reaching machine-level stationarity.
101
+ grad = gradient(result.x)
102
+ if not np.isfinite(result.fun) or float(np.max(np.abs(grad))) > 2e-8:
103
+ raise RuntimeError(f"span optimization failed: {result.message}; gradient={grad}")
104
+ return basis @ result.x, result.x, float(np.max(np.abs(grad)))
105
+
106
+ def joint_expectations(self, a: np.ndarray, b: np.ndarray, order: int = 60) -> dict[str, float]:
107
+ """KL(a||b) and probability MSE for two jointly Gaussian logits."""
108
+ nodes, weights = roots_hermitenorm(order)
109
+ weights = weights / math.sqrt(2.0 * math.pi)
110
+ cov = np.array([[a @ a, a @ b], [a @ b, b @ b]], dtype=np.float64)
111
+ vals, vecs = np.linalg.eigh(cov)
112
+ transform = vecs @ np.diag(np.sqrt(np.clip(vals, 0.0, None)))
113
+ g1, g2 = np.meshgrid(nodes, nodes, indexing="ij")
114
+ z1 = transform[0, 0] * g1 + transform[0, 1] * g2
115
+ z2 = transform[1, 0] * g1 + transform[1, 1] * g2
116
+ ww = weights[:, None] * weights[None, :]
117
+ p, q = expit(z1), expit(z2)
118
+ kl_point = p * (z1 - z2) + softplus(z2) - softplus(z1)
119
+ mse_point = (p - q) ** 2
120
+ return {
121
+ "kl": float(np.sum(ww * kl_point)),
122
+ "probability_mse": float(np.sum(ww * mse_point)),
123
+ }
124
+
125
+
126
+ def feature_vector(k: int, feature_index: int) -> np.ndarray:
127
+ """Return x_i as coefficients over independent Z_1,...,Z_k (1-indexed i)."""
128
+ b = np.zeros(k, dtype=np.float64)
129
+ if feature_index == 1:
130
+ b[0] = 1.0
131
+ else:
132
+ b[feature_index - 1] = 1.0
133
+ b[feature_index - 2] = -1.0
134
+ return b
135
+
136
+
137
+ def simulate_path(k: int, passes: int, gbce: GaussianBCE) -> tuple[list[AgentRecord], list[np.ndarray]]:
138
+ parent = np.zeros(k, dtype=np.float64)
139
+ global_a = np.zeros(k, dtype=np.float64)
140
+ global_a[-1] = 1.0
141
+ optimum = gbce.loss(global_a)
142
+ records: list[AgentRecord] = []
143
+ coefficients: list[np.ndarray] = []
144
+ for depth in range(1, k * passes + 1):
145
+ feature = ((depth - 1) % k) + 1
146
+ pass_index = (depth - 1) // k + 1
147
+ local = feature_vector(k, feature)
148
+ new_a, w, stationarity = gbce.optimize_span(local, parent)
149
+ relevant_start = max(0, k - pass_index - 1) # Last p x-features span Z_{k-p},...,Z_k.
150
+ off = float(np.linalg.norm(new_a[:relevant_start])) if feature == k else float("nan")
151
+ loss = gbce.loss(new_a)
152
+ records.append(AgentRecord(k, depth, depth, pass_index, feature, loss, loss - optimum, float(w[0]), float(w[1]), stationarity, off))
153
+ coefficients.append(new_a.copy())
154
+ parent = new_a
155
+ return records, coefficients
156
+
157
+
158
+ def linear_fit(x: np.ndarray, y: np.ndarray) -> dict[str, float]:
159
+ slope, intercept = np.polyfit(x, y, 1)
160
+ pred = slope * x + intercept
161
+ ss_res = float(np.sum((y - pred) ** 2))
162
+ ss_tot = float(np.sum((y - np.mean(y)) ** 2))
163
+ return {"slope": float(slope), "intercept": float(intercept), "r2": 1.0 - ss_res / ss_tot}
164
+
165
+
166
+ def monte_carlo_lower(p_values: list[int], samples: int, seeds: list[int], device: str) -> list[dict[str, Any]]:
167
+ if torch is None:
168
+ return []
169
+ dtype = torch.float64
170
+ rows: list[dict[str, Any]] = []
171
+ for seed in seeds:
172
+ gen = torch.Generator(device=device)
173
+ gen.manual_seed(seed)
174
+ z = torch.randn(samples, generator=gen, device=device, dtype=dtype)
175
+ xi = torch.randn(samples, generator=gen, device=device, dtype=dtype)
176
+ target = torch.sigmoid(z)
177
+ optimum = torch.mean(torch.nn.functional.softplus(z) - target * z)
178
+ for p in p_values:
179
+ s = z + xi / math.sqrt(p)
180
+ c = torch.tensor(p / (p + 1.0), device=device, dtype=dtype)
181
+ for _ in range(20):
182
+ pred = torch.sigmoid(c * s)
183
+ grad = torch.mean(s * (pred - target))
184
+ hess = torch.mean(s * s * pred * (1.0 - pred))
185
+ step = grad / hess
186
+ c = c - step
187
+ if float(torch.abs(step).cpu()) < 1e-12:
188
+ break
189
+ loss = torch.mean(torch.nn.functional.softplus(c * s) - target * c * s)
190
+ rows.append({
191
+ "seed": seed,
192
+ "p": p,
193
+ "samples": samples,
194
+ "device": device,
195
+ "c": float(c.cpu()),
196
+ "excess": float((loss - optimum).cpu()),
197
+ })
198
+ del z, xi, target
199
+ return rows
200
+
201
+
202
+ def write_csv(path: Path, rows: list[dict[str, Any]]) -> None:
203
+ path.parent.mkdir(parents=True, exist_ok=True)
204
+ if not rows:
205
+ path.write_text("", encoding="utf-8")
206
+ return
207
+ fields = list(rows[0])
208
+ with path.open("w", newline="", encoding="utf-8") as f:
209
+ writer = csv.DictWriter(f, fieldnames=fields)
210
+ writer.writeheader()
211
+ writer.writerows(rows)
212
+
213
+
214
+ def svg_lower_bound(path: Path, rows: list[dict[str, Any]]) -> None:
215
+ points = [(r["p"], r["excess"]) for r in rows if r["k"] == max(x["k"] for x in rows)]
216
+ width, height, margin = 800, 500, 70
217
+ xs = np.log10([p for p, _ in points]); ys = np.log10([e for _, e in points])
218
+ xmin, xmax = float(xs.min()), float(xs.max()); ymin, ymax = float(ys.min()), float(ys.max())
219
+ def sx(x: float) -> float: return margin + (x - xmin) / (xmax - xmin) * (width - 2 * margin)
220
+ def sy(y: float) -> float: return height - margin - (y - ymin) / (ymax - ymin) * (height - 2 * margin)
221
+ poly = " ".join(f"{sx(x):.1f},{sy(y):.1f}" for x, y in zip(xs, ys))
222
+ circles = "\n".join(f'<circle cx="{sx(x):.1f}" cy="{sy(y):.1f}" r="5" fill="#2563eb"/>' for x, y in zip(xs, ys))
223
+ content = f'''<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="0 0 {width} {height}">
224
+ <rect width="100%" height="100%" fill="white"/><line x1="{margin}" y1="{height-margin}" x2="{width-margin}" y2="{height-margin}" stroke="black"/><line x1="{margin}" y1="{margin}" x2="{margin}" y2="{height-margin}" stroke="black"/>
225
+ <polyline points="{poly}" fill="none" stroke="#2563eb" stroke-width="3"/>{circles}
226
+ <text x="{width/2}" y="35" text-anchor="middle" font-family="sans-serif" font-size="22">Hard-instance BCE excess vs passes (k={max(x['k'] for x in rows)})</text>
227
+ <text x="{width/2}" y="{height-18}" text-anchor="middle" font-family="sans-serif" font-size="17">log10(passes p)</text>
228
+ <text x="18" y="{height/2}" transform="rotate(-90 18 {height/2})" text-anchor="middle" font-family="sans-serif" font-size="17">log10(excess BCE)</text></svg>'''
229
+ path.write_text(content, encoding="utf-8")
230
+
231
+
232
+ def main() -> None:
233
+ parser = argparse.ArgumentParser()
234
+ parser.add_argument("--mode", choices=["smoke", "substantive"], default="smoke")
235
+ parser.add_argument("--output", type=Path, required=True)
236
+ parser.add_argument("--device", choices=["auto", "cpu", "cuda"], default="auto")
237
+ parser.add_argument("--mc-samples", type=int, default=None)
238
+ args = parser.parse_args()
239
+ started = time.time()
240
+ args.output.mkdir(parents=True, exist_ok=True)
241
+ order = 80 if args.mode == "smoke" else 140
242
+ ks = [4, 8] if args.mode == "smoke" else [4, 8, 12, 16, 24]
243
+ mc_samples = args.mc_samples or (50_000 if args.mode == "smoke" else 2_000_000)
244
+ if args.device == "auto":
245
+ device = "cuda" if torch is not None and torch.cuda.is_available() else "cpu"
246
+ else:
247
+ device = args.device
248
+ if device == "cuda" and (torch is None or not torch.cuda.is_available()):
249
+ raise RuntimeError("CUDA requested but unavailable")
250
+
251
+ gbce = GaussianBCE(order)
252
+ all_records: list[AgentRecord] = []
253
+ lower_rows: list[dict[str, Any]] = []
254
+ identity_rows: list[dict[str, Any]] = []
255
+ max_stationarity = 0.0
256
+ max_off_relevant = 0.0
257
+ for k in ks:
258
+ recs, coeffs = simulate_path(k, k - 1, gbce)
259
+ all_records.extend(recs)
260
+ global_a = np.zeros(k); global_a[-1] = 1.0
261
+ global_loss = gbce.loss(global_a)
262
+ for p in range(1, k):
263
+ idx = p * k - 1
264
+ rec, a = recs[idx], coeffs[idx]
265
+ lower_rows.append({"k": k, "p": p, "D": k * p, "loss": rec.loss, "global_loss": global_loss, "excess": rec.excess, "scaled_p_excess": p * rec.excess, "upper_bound": k * math.sqrt(2.0) * k / math.sqrt(k * p), "bound_ratio": rec.excess / (k * math.sqrt(2.0) * k / math.sqrt(k * p))})
266
+ max_off_relevant = max(max_off_relevant, rec.off_relevant_l2)
267
+ max_stationarity = max(max_stationarity, max(r.stationarity_max_abs for r in recs))
268
+
269
+ # Global decomposition and Pinsker check for the final predictor.
270
+ q = coeffs[-1]
271
+ joint = gbce.joint_expectations(global_a, q)
272
+ loss_gap = gbce.loss(q) - global_loss
273
+ identity_rows.append({"kind": "global", "k": k, "depth": k * (k - 1), "loss_gap": loss_gap, "kl": joint["kl"], "identity_abs_error": abs(loss_gap - joint["kl"]), "probability_mse": joint["probability_mse"], "pinsker_slack": joint["kl"] - 2.0 * joint["probability_mse"]})
274
+
275
+ # Operational decomposition for every transition: current local optimum vs parent.
276
+ zero = np.zeros(k)
277
+ for i, (rec, cur) in enumerate(zip(recs, coeffs)):
278
+ prev = zero if i == 0 else coeffs[i - 1]
279
+ joint_step = gbce.joint_expectations(cur, prev, order=40)
280
+ gap = gbce.loss(prev) - gbce.loss(cur)
281
+ identity_rows.append({"kind": "transition", "k": k, "depth": i + 1, "loss_gap": gap, "kl": joint_step["kl"], "identity_abs_error": abs(gap - joint_step["kl"]), "probability_mse": joint_step["probability_mse"], "pinsker_slack": joint_step["kl"] - 2.0 * joint_step["probability_mse"]})
282
+
283
+ slope_rows = []
284
+ for k in ks:
285
+ subset = [r for r in lower_rows if r["k"] == k]
286
+ fit = linear_fit(np.log([r["p"] for r in subset]), np.log([r["excess"] for r in subset]))
287
+ slope_rows.append({"k": k, "n_points": len(subset), **fit})
288
+
289
+ mc_ps = list(range(1, max(ks)))
290
+ seeds = [11] if args.mode == "smoke" else [11, 23, 37]
291
+ mc_rows = monte_carlo_lower(mc_ps, mc_samples, seeds, device)
292
+ mc_agg = []
293
+ for p in mc_ps:
294
+ vals = [r["excess"] for r in mc_rows if r["p"] == p]
295
+ mc_agg.append({"p": p, "mean_excess": float(np.mean(vals)), "std_excess": float(np.std(vals, ddof=1)) if len(vals) > 1 else 0.0, "mean_p_excess": float(p * np.mean(vals))})
296
+ mc_fit = linear_fit(np.log([r["p"] for r in mc_agg]), np.log([r["mean_excess"] for r in mc_agg]))
297
+
298
+ agent_rows = [asdict(r) for r in all_records]
299
+ write_csv(args.output / "agent_trajectory.csv", agent_rows)
300
+ write_csv(args.output / "lower_bound.csv", lower_rows)
301
+ write_csv(args.output / "decomposition_pinsker.csv", identity_rows)
302
+ write_csv(args.output / "lower_slopes.csv", slope_rows)
303
+ write_csv(args.output / "monte_carlo.csv", mc_rows)
304
+ write_csv(args.output / "monte_carlo_aggregate.csv", mc_agg)
305
+ svg_lower_bound(args.output / "lower_bound.svg", lower_rows)
306
+
307
+ summary = {
308
+ "paper": {"title": "Networked Information Aggregation for Binary Classification", "arxiv_id": "2605.01082", "openreview_id": "mrtg4NmvAe"},
309
+ "mode": args.mode,
310
+ "method": "deterministic population sequential-logit simulation plus independent Monte Carlo lower-bound check",
311
+ "quadrature_order": order,
312
+ "k_values": ks,
313
+ "mc": {"samples_per_seed": mc_samples, "seeds": seeds, "device": device, "fit_log_excess_vs_log_p": mc_fit},
314
+ "invariants": {
315
+ "max_agent_stationarity_abs": max_stationarity,
316
+ "max_end_of_pass_off_relevant_l2": max_off_relevant,
317
+ "max_loss_decomposition_abs_error": max(r["identity_abs_error"] for r in identity_rows),
318
+ "min_pinsker_slack": min(r["pinsker_slack"] for r in identity_rows),
319
+ },
320
+ "lower_bound_fits": slope_rows,
321
+ "upper_bound_check": {"all_excess_below_theorem_bound": all(r["excess"] <= r["upper_bound"] + 1e-12 for r in lower_rows), "max_excess_to_bound_ratio": max(r["bound_ratio"] for r in lower_rows), "note": "The theorem bound is valid but loose on its own hard instance; this check does not establish worst-case tightness."},
322
+ "environment": {
323
+ "python": platform.python_version(),
324
+ "platform": platform.platform(),
325
+ "numpy": np.__version__,
326
+ "scipy": __import__("scipy").__version__,
327
+ "torch": getattr(torch, "__version__", None),
328
+ "cuda_available": bool(torch is not None and torch.cuda.is_available()),
329
+ "cuda_device": torch.cuda.get_device_name(0) if torch is not None and torch.cuda.is_available() else None,
330
+ },
331
+ "wall_seconds": time.time() - started,
332
+ }
333
+ (args.output / "summary.json").write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8")
334
+ print(json.dumps(summary, indent=2))
335
+
336
+
337
+ if __name__ == "__main__":
338
+ main()
339
+
340
+ ````
341
+
342
+
343
+ ````output
344
+ /home/floor/.hermes/hermes-agent/venv/lib/python3.11/site-packages/torch/cuda/__init__.py:188: UserWarning: CUDA initialization: The NVIDIA driver on your system is too old (found version 12020). Please update your GPU driver by downloading and installing a new version from the URL: http://www.nvidia.com/Download/index.aspx Alternatively, go to: https://pytorch.org to install a PyTorch version that has been compiled with your version of the CUDA driver. (Triggered internally at /__w/pytorch/pytorch/c10/cuda/CUDAFunctions.cpp:119.)
345
+ return torch._C._cuda_getDeviceCount() > 0
346
+ {
347
+ "paper": {
348
+ "title": "Networked Information Aggregation for Binary Classification",
349
+ "arxiv_id": "2605.01082",
350
+ "openreview_id": "mrtg4NmvAe"
351
+ },
352
+ "mode": "smoke",
353
+ "method": "deterministic population sequential-logit simulation plus independent Monte Carlo lower-bound check",
354
+ "quadrature_order": 80,
355
+ "k_values": [
356
+ 4,
357
+ 8
358
+ ],
359
+ "mc": {
360
+ "samples_per_seed": 50000,
361
+ "seeds": [
362
+ 11
363
+ ],
364
+ "device": "cpu",
365
+ "fit_log_excess_vs_log_p": {
366
+ "slope": -0.6985447045410992,
367
+ "intercept": -2.957562559102943,
368
+ "r2": 0.9929117592488544
369
+ }
370
+ },
371
+ "invariants": {
372
+ "max_agent_stationarity_abs": 1.3366677834025786e-09,
373
+ "max_end_of_pass_off_relevant_l2": 0.0,
374
+ "max_loss_decomposition_abs_error": 8.914925395120399e-11,
375
+ "min_pinsker_slack": -3.207951782336416e-17
376
+ },
377
+ "lower_bound_fits": [
378
+ {
379
+ "k": 4,
380
+ "n_points": 3,
381
+ "slope": -0.5878402681112328,
382
+ "intercept": -3.0093586467265947,
383
+ "r2": 0.9986958332923913
384
+ },
385
+ {
386
+ "k": 8,
387
+ "n_points": 7,
388
+ "slope": -0.6076749395800219,
389
+ "intercept": -3.001379744483822,
390
+ "r2": 0.9994171577157169
391
+ }
392
+ ],
393
+ "upper_bound_check": {
394
+ "all_excess_below_theorem_bound": true,
395
+ "max_excess_to_bound_ratio": 0.0043379795025076315,
396
+ "note": "The theorem bound is valid but loose on its own hard instance; this check does not establish worst-case tightness."
397
+ },
398
+ "environment": {
399
+ "python": "3.11.15",
400
+ "platform": "Linux-6.17.0-23-generic-x86_64-with-glibc2.39",
401
+ "numpy": "2.4.3",
402
+ "scipy": "1.17.1",
403
+ "torch": "2.13.0+cu130",
404
+ "cuda_available": false,
405
+ "cuda_device": null
406
+ },
407
+ "wall_seconds": 2.741654396057129
408
+ }
409
+
410
+ ````
411
+
412
+
413
+ ---
414
+ <!-- trackio-cell
415
+ {"type": "markdown", "id": "cell_d80cc36c53a8", "created_at": "2026-07-16T12:29:15+00:00", "title": "Evidence sources and numerical method"}
416
+ -->
417
+ Primary sources: [arXiv abstract](https://arxiv.org/abs/2605.01082), [PDF](https://arxiv.org/pdf/2605.01082), and OpenReview ID [`mrtg4NmvAe`](https://openreview.net/forum?id=mrtg4NmvAe). The accepted arXiv v1 PDF and LaTeX source were audited directly; no official code repository was linked in the manuscript or found by title/arXiv search. OpenReview access was blocked by Cloudflare, so theorem text was checked against the accepted arXiv source.
418
+
419
+ The reproduction implements the paper's Gaussian hard instance in population coefficient space. Every parent logit is represented as a linear form over independent latent normals; every agent minimizes population BCE over the span of its local feature and parent logit. Order-140 Gauss-Hermite quadrature provides deterministic expectations, with a separate 3-seed, 500,000-sample-per-seed Monte Carlo cross-check.
420
+
421
+ A Hugging Face T4-small Job was attempted early. Creation failed before scheduling with HTTP 402 insufficient prepaid credit, so there is no Job ID or URL and no GPU result is claimed. The substantive local CPU run completed in 8.3048 s.
trackio-logo-light.png ADDED
trackio-logo.png ADDED
trackio-wordmark-dark.png ADDED