Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/verify_twodepth.mjs

52.8 KiB, 1 run

created by r2519314175:767, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1// verify_twodepth.mjs — the two-depth answer, and the streaming that is the whole
2// point of it.
3//
4// A model can now fold part of its answer away in the answer itself: a
5// `<details>` in the markdown, no tool involved. The tool it replaces, `say`, did
6// not stream at all — an unchanging spinner for one to three minutes on a hard
7// question, and then the whole reply at once. So THE STREAMING IS THE FEATURE.
8// A fold that renders beautifully and stops the text arriving is a regression,
9// however well it folds, and most of what is asserted below is about the stream
10// rather than about the fold.
11//
12// What is asserted, and why each one rather than the next thing:
13//
14// 1. THE KEY, AGAINST THE FIXTURE ON DISK. `dev/fixtures/fold_keys.json` is the
15// one name the Rust and JS halves must agree on (dev/CONTRACT_FOLD.md §2).
16// Every case in it is READ FROM THE FILE and driven through the shipped
17// `DaimondRender.foldScan` in the real page — not transcribed into this
18// file, so a case added to the fixture cannot silently stop being checked,
19// and a case changed cannot silently start agreeing with a copy.
20//
21// 2. THE ANSWER ABOVE THE FOLD STREAMS. Sampled every frame of a real turn
22// against the mock provider: the text above the fold is complete on screen
23// while the fold's own body is still filling, and the body grows across
24// frames rather than appearing whole at the end. This is the check the
25// feature exists for.
26//
27// 3. THE CONTROL IS MADE ONCE. A `MutationObserver` counts every
28// `details.md-fold` ever inserted into the bubble across the whole turn, and
29// the survivor is stamped so its identity can be read back. One insertion,
30// not one per frame: a fold rebuilt per frame is a fold that snaps shut
31// sixty times a second and takes the reader's place in it with it.
32//
33// 4. A CLOSED FOLD'S BODY IS NOT VISIBLE — AND A RECT WITH AREA IS NOT EVIDENCE.
34// A closed `<details>` in Chrome puts its contents behind
35// `content-visibility: hidden`, and a skipped subtree KEEPS ITS LAST LAYOUT:
36// `getBoundingClientRect()` on a paragraph inside a closed fold comes back
37// with real width and height, sitting below the one-line fold that is
38// supposedly containing it. Measured that way the check would pass on a fold
39// that never closes. So "shown" is two geometric facts together — the
40// content's rect lies INSIDE the fold's own rect, and the point at its
41// centre hit-tests to something inside the fold — and it is asserted BOTH
42// ways, closed and then open, so a measurement that can only ever say "no"
43// cannot pass for a proof.
44//
45// 5. THE OPEN SET REACHES WASM. `DaimondApp.set_open_folds` is wrapped on its
46// prototype and the real one still called, so what is recorded is what the
47// engine was actually handed. Opening a fold has to put that fold's key into
48// it. (What the ENGINE then does with the set is the Rust half's, and is not
49// asserted here.)
50//
51// 6. A FOLD WITH NOTHING ABOVE IT IS DRAWN OPEN. `say` could refuse an empty
52// summary — src/tools.rs calls it "the one failure worth refusing", because
53// it draws a fold the user must open to discover says nothing. Markup cannot
54// be refused, so the renderer takes the decision instead.
55//
56// 7. A FENCED FOLD IS NOT A FOLD. A `<details>` inside a code fence is a model
57// SHOWING markup. No control is drawn and the angle brackets stay on screen.
58//
59// 8. NOTHING EXECUTABLE RIDES IN ON THE LABEL. The summary is a NEW
60// `innerHTML` sink taking model text: the fold's control is built by hand,
61// so the label does not travel through `md` with the rest of the message.
62// It goes through the same sanitiser, and this is the check that says so
63// rather than the comment beside it.
64//
65// 9. A FOLD INSIDE A FOLD TAKES NO KEY. The renderer nests correctly for free,
66// because the browser's parser does; what has to be proved is that the KEY
67// does not, since only a top-level fold takes an ordinal
68// (CONTRACT_FOLD.md §8) and one extra here would rename every fold after it
69// in the engine's eyes.
70//
71// 12. PROSE FOLLOWED BY A TOOL CALL IS WORKING, NOT AN ANSWER. The fold governs the
72// answer; this governs everything else the model says, which on a twenty-call turn is
73// most of what a reader sees. The run after the LAST call is untouched and is the reply.
74//
75// 11. THE LABEL IS LEGIBLE. Measured against the ANSWER IT SITS BESIDE, not
76// against a number written here: same size, same ink. Every other check in
77// this file passes on a fold the reader never finds, which is what happened
78// -- see 11 below for whose screen it happened on.
79//
80// 10. THE READER'S CHOICE SURVIVES A RELOAD. The key is an ordinal and a label
81// and carries no message identity, which is exactly what makes this possible
82// without touching the stored chat schema.
83//
84// EACH CHECK IS PROVED AGAINST BROKEN CODE FIRST. `--break <name>` serves a
85// deliberately damaged copy of a real file to the real page (through
86// `page.route`), and the run is then expected to FAIL. A break whose anchor does
87// not appear exactly once aborts rather than passing quietly.
88//
89// node dev/verify_twodepth.mjs --break keysep # 1: the key's separator
90// node dev/verify_twodepth.mjs --break fencedfold # 1,7: a fenced fold counted
91// node dev/verify_twodepth.mjs --break nestordinal # 1,9: a nested fold counted
92// node dev/verify_twodepth.mjs --break nostream # 2: drawn only at the end
93// node dev/verify_twodepth.mjs --break perframe # 3: the control rebuilt
94// node dev/verify_twodepth.mjs --break holdforever # 1: the hold-back never let go
95// node dev/verify_twodepth.mjs --break flicker # 1: the half-written fold drawn
96// node dev/verify_twodepth.mjs --break alwaysopen # 4: a fold that never shuts
97// node dev/verify_twodepth.mjs --break bodygone # 4: a body that never shows
98// node dev/verify_twodepth.mjs --break openbroken # 5: the gesture not recorded
99// node dev/verify_twodepth.mjs --break rawlabel # 8: the label as live markup
100// node dev/verify_twodepth.mjs --break bareclosed # 6: nothing above it, shut
101// node dev/verify_twodepth.mjs --break barequiet # 6: the decision left unsaid
102// node dev/verify_twodepth.mjs --break noreload # 10: the choice not restored
103// node dev/verify_twodepth.mjs --break mutedlabel # 11: the label as a caption
104// node dev/verify_twodepth.mjs --break loudworking # 12: narration drawn as an answer
105// node dev/verify_twodepth.mjs # and then, clean
106//
107// eval "$(bash dev/world.sh 3 --up)"
108// node dev/verify_twodepth.mjs
109//
110// BRING THE WORLD DOWN AND UP BETWEEN RUNS. Measured on 2026-08-23: a second run against a
111// world already used reported 22 of 34 failed, and the same run against a freshly restarted
112// world reported 34 of 34. The mock keeps state across a run -- its call-id sequence and the
113// tool-round arithmetic it does over a conversation -- so a re-run is not a repeat. It cost an
114// hour of disbelieving a break that was working perfectly, and it is the fixture-order trap
115// this file already warns about, wearing a different coat.
116//
117// bash dev/world.sh 3 --down && eval "$(bash dev/world.sh 3 --up)"
118//
119// Needs dev/serve.mjs and dev/mockllm.mjs. No gateway and no real provider: the
120// mock is told exactly what to stream, so nothing here spends anything.
121import fs from 'node:fs';
122import path from 'node:path';
123import { fileURLToPath } from 'node:url';
124import { open, chat, shot, scratch, errors, signInAs } from './harness.mjs';
125
126const HERE = path.dirname(fileURLToPath(import.meta.url));
127const WWW = path.join(HERE, '..', 'www');
128
129const BREAK = (() => {
130 const i = process.argv.indexOf('--break');
131 return i > 0 ? String(process.argv[i + 1] || '') : '';
132})();
133
134const PROFILE = scratch('pw', 'twodepth' + (BREAK ? '-' + BREAK : ''));
135fs.rmSync(PROFILE, { recursive: true, force: true });
136
137const ok = [], bad = [];
138const check = (name, pass, detail) => {
139 (pass ? ok : bad).push(name);
140 console.log((pass ? ' ok ' : ' FAIL ') + name + (detail != null ? ' — ' + detail : ''));
141};
142
143// ── The breaks ───────────────────────────────────────────────────────
144const BREAKS = {
145 // The one name both languages must agree on, spelled differently. Nothing
146 // visible changes; only the fixture notices, which is what the fixture is for.
147 keysep: [{
148 file: 'js/render.js',
149 find: "\t\t\tf.key = i + ':' + f.summary;",
150 with: "\t\t\tf.key = i + '|' + f.summary;",
151 }],
152 // Fenced code read as markup: a model showing a reader how to write a fold
153 // has the demonstration folded away instead.
154 fencedfold: [{
155 file: 'js/render.js',
156 find: '\t\t\tif (inRanges(fenced, m.index)) continue;',
157 with: '\t\t\tif (false) continue;',
158 }],
159 // THE REGRESSION THIS FEATURE EXISTS TO AVOID: nothing on screen until the
160 // generation ends, which is what `say` did and what made it worth replacing.
161 nostream: [{
162 file: 'js/daimond.js',
163 find: '\t\tvar pinned = nearBottom();\n\t\tdrawAsst();\n'
164 + '\t\tif (pinned) chatOutput.scrollTop = chatOutput.scrollHeight;\n'
165 + '\t}\n\tfunction appendAssistantText(text) {',
166 with: '\t\tvar pinned = nearBottom();\n'
167 + '\t\tif (pinned) chatOutput.scrollTop = chatOutput.scrollHeight;\n'
168 + '\t}\n\tfunction appendAssistantText(text) {',
169 }],
170 // The control rebuilt every frame — the obvious implementation, and the one
171 // that loses the reader's open fold and their place in it sixty times a second.
172 perframe: [{
173 file: 'js/daimond.js',
174 find: '\t\t\tif (!cur || cur.id !== id) {',
175 with: '\t\t\tif (true) {',
176 }],
177 // The label put on screen as markup. It is the one place model text reaches
178 // `innerHTML` without going through `md` first, and it is invisible from
179 // anywhere but here.
180 rawlabel: [{
181 file: 'js/daimond.js',
182 find: '\t\tsum.innerHTML = DaimondRender.sanitize(seg.label);',
183 with: '\t\tsum.innerHTML = seg.label;',
184 }],
185 // The label drawn as a caption again: muted, and a size below the answer it
186 // belongs to. This is the state the owner met on 2026-08-23, and it is the one
187 // shape of this feature that nothing could see -- every behavioural check above
188 // passes on a fold nobody can find.
189 mutedlabel: [{
190 file: 'css/app.css',
191 find: 'details.md-fold > summary {\n\tpadding: 4px 0;\n\tcolor: var(--text-primary);'
192 + '\n\tfont-size: var(--fs-base);',
193 with: 'details.md-fold > summary {\n\tpadding: 4px 0;\n\tcolor: var(--text-muted);'
194 + '\n\tfont-size: var(--fs-sm);',
195 }],
196 // The seam placed on ANY answer long enough, which is FOLD-ALL by another
197 // route: the reader is handed a closed control and nothing to read beside it.
198 // dev/PROMPT_NOTES.md §5 measured a wording that did this 8 times in 8.
199 seamall: [{
200 file: 'js/render.js',
201 find: '\tvar SEAM_LEAD_MIN = 40;',
202 with: '\tvar SEAM_LEAD_MIN = 0;',
203 }],
204 // The blank line after the summary dropped. CONTRACT_FOLD.md §1: without it the
205 // element is one block of raw HTML and every heading inside it reaches the
206 // reader as literal hashes. Invisible from anywhere but the fixture.
207 seamblank: [{
208 file: 'js/render.js',
209 find: "'\\n\\n<details>\\n<summary>' + head.sum + '</summary>\\n\\n'",
210 with: "'\\n\\n<details>\\n<summary>' + head.sum + '</summary>\\n'",
211 }],
212 // Prose before a tool call left as an ANSWER, at full weight in the thread. This is the
213 // state the owner met on 2026-08-23: twenty paragraphs of "let me pin the line numbers"
214 // and "de.js is done cleanly", each a sentence or two and so each exempt from FOLD_NOTE
215 // by its own last rule. The fold was working and was pointed at the wrong text.
216 loudworking: [{
217 file: 'js/daimond.js',
218 find: "\t\t// See `demoteToWorking`.\n\t\tdemoteToWorking();",
219 with: "\t\t// See `demoteToWorking`.\n\t\tfinalizeAssistant();",
220 }],
221 // A nested fold given an ordinal of its own, which is what the contract's
222 // Amendment 1 forbids: the engine counts only top-level folds, so every key
223 // after a nested one would name a different fold in each half.
224 nestordinal: [{
225 file: 'js/render.js',
226 find: '\t\t\tif (top) found.push(fold);',
227 with: '\t\t\tfound.push(fold);',
228 }],
229 // The hold-back removed, so the browser's own half-formed disclosure is drawn
230 // for the few frames between `<details>` and `</summary>` and then replaced.
231 // This is not hypothetical: it is what this file caught, on 2026-08-21.
232 flicker: [{
233 file: 'js/render.js',
234 find: '\tfunction foldPending(rest) {\n',
235 with: '\tfunction foldPending(rest) {\n\t\treturn false;\n',
236 }],
237 // A fold that is never shut, so "closed hides its body" has nothing to measure.
238 alwaysopen: [{
239 file: 'js/daimond.js',
240 find: '\t\td.open = !!_openFolds[seg.key];',
241 with: '\t\td.open = true;',
242 }],
243 // The other half of the same check: a body that is never shown. Without this
244 // break, "closed is hidden" could be passing on a measurement that always
245 // answers hidden.
246 bodygone: [{
247 file: 'css/app.css',
248 find: 'details.md-fold > .md-fold-body > :last-child { margin-bottom: 0; }',
249 with: 'details.md-fold > .md-fold-body > :last-child { margin-bottom: 0; }\n'
250 + 'details.md-fold > .md-fold-body { display: none; }',
251 }],
252 // The gesture that manages the reader's screen no longer manages the model's
253 // working set: the fold opens, and the engine is told nothing.
254 openbroken: [{
255 file: 'js/daimond.js',
256 find: '\t\tif (el.open) _openFolds[k] = 1; else delete _openFolds[k];',
257 with: '\t\tif (el.open) { /* broken */ } else { /* broken */ }',
258 }],
259 // A fold with nothing above it, drawn shut: a whole answer behind a control
260 // the reader has to press to find out there was nothing else.
261 bareclosed: [{
262 file: 'js/daimond.js',
263 find: '\t\tif ((seg.open || headless) && !(seg.key in _openFolds)) _openFolds[seg.key] = 1;',
264 with: '\t\tif (seg.open && !(seg.key in _openFolds)) _openFolds[seg.key] = 1;',
265 }],
266 // The fold drawn open for the right reason but not SAYING so, which is how a
267 // decision quietly becomes a coincidence nobody can find again.
268 barequiet: [{
269 file: 'js/daimond.js',
270 find: "\t\tif (headless) d.classList.add('md-fold-bare');",
271 with: "\t\tif (false) d.classList.add('md-fold-bare');",
272 }],
273 // The hold-back never released, so a turn that died half way through a
274 // `<summary>` shows the reader nothing where its last words should be.
275 holdforever: [{
276 file: 'js/render.js',
277 find: '\t\t\tif (!settled && /<details/i.test(tail)) {',
278 with: '\t\t\tif (/<details/i.test(tail)) {',
279 }],
280 // The stored set never read back, so every reload shuts every fold.
281 noreload: [{
282 file: 'js/daimond.js',
283 find: "\t\t// Before a single fold is drawn: which of them this chat's reader had open.\n"
284 + '\t\tloadTextFolds();\n',
285 with: '',
286 }],
287};
288
289if (BREAK && !BREAKS[BREAK]) {
290 console.error(`unknown break '${BREAK}'; one of: ${Object.keys(BREAKS).join(', ')}`);
291 process.exit(2);
292}
293
294/// `src` with `spec` applied, or a hard stop.
295function damaged(src, spec) {
296 const n = src.split(spec.find).length - 1;
297 if (n !== 1) {
298 console.error(`break '${BREAK}': the anchor appears ${n} times in ${spec.file}, `
299 + 'so nothing was broken and the run below would prove nothing.');
300 process.exit(2);
301 }
302 return src.replace(spec.find, spec.with);
303}
304
305const TYPE = { '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript' };
306
307/// The damaged files, ONE BODY PER FILE: Playwright hands a request to the LAST
308/// route registered for its URL, so two routes on one file ship only the second.
309function damagedFiles() {
310 const byFile = new Map();
311 for (const spec of (BREAKS[BREAK] || [])) {
312 const src = byFile.has(spec.file) ? byFile.get(spec.file)
313 : fs.readFileSync(path.join(WWW, spec.file), 'utf8');
314 byFile.set(spec.file, damaged(src, spec));
315 }
316 return byFile;
317}
318
319async function serveBreaks(page) {
320 if (!BREAK) return;
321 for (const [file, body] of damagedFiles()) {
322 const type = TYPE[path.extname(file)] || 'text/plain';
323 await page.route('**/' + file, r => r.fulfill({ status: 200, contentType: type, body }));
324 }
325}
326
327// ── The fixture, read from disk ──────────────────────────────────────
328//
329// Not transcribed. A lane that believes a case is wrong reports it; a lane that
330// copies the cases into its own source has stopped testing the pin.
331const FIXTURE = path.join(HERE, 'fixtures', 'fold_keys.json');
332const fixture = JSON.parse(fs.readFileSync(FIXTURE, 'utf8'));
333// The SEAM's own fixture, the pin on the other thing the two languages must agree on:
334// what a `Fold:` line becomes. dev/CONTRACT_FOLD.md §15.
335const SEAMFIX = path.join(HERE, 'fixtures', 'fold_seam.json');
336const seamfix = JSON.parse(fs.readFileSync(SEAMFIX, 'utf8'));
337
338// ── What the model is told to stream ─────────────────────────────────
339//
340// The mock splits its reply on single spaces and sends one word per frame, so
341// these are written with single spaces and their newlines ride inside the words.
342// Every label is different, because two folds with the same ordinal and the same
343// label share a key ON PURPOSE (CONTRACT_FOLD.md §2) and a check that wants to
344// name one fold needs to have named one fold.
345const BODY = 'The cache was cold because the deploy dropped the warmed image and the '
346 + 'replacement came up empty. Nothing was lost. The first request after a deploy '
347 + 'now pays for the fill, every request after it does not, and the window in which '
348 + 'that is true is about ninety seconds on the current instance size. There is a '
349 + 'longer story about why the warmer runs after the cutover rather than before it, '
350 + 'which is the part worth changing.';
351
352// `open`, so the body is on screen while it fills and the stream can be MEASURED.
353// A closed fold's contents are not rendered, and "did the text grow" asked of an
354// unrendered subtree is a question with no answer.
355const STREAMED = 'Short answer: the cache was cold.\n\n'
356 + '<details open>\n<summary>The long version</summary>\n\n' + BODY + '\n\n</details>\n';
357const SHUT = 'It is fixed now.\n\n'
358 + '<details>\n<summary>What was actually wrong</summary>\n\n' + BODY + '\n\n</details>\n';
359const BARE = '<details>\n<summary>Everything I have</summary>\n\n' + BODY + '\n\n</details>\n';
360const FENCED = 'Write it like this:\n\n```html\n<details>\n<summary>A label</summary>\n\n'
361 + 'The detail.\n\n</details>\n```\n';
362// Never touched by this run, so the reload check has something that must come
363// back SHUT. Without one, "the reader's choice survived" would also be true of a
364// build that simply opened everything.
365const ALONE = 'And another thing.\n\n'
366 + '<details>\n<summary>Left alone</summary>\n\n' + BODY + '\n\n</details>\n';
367// The whole of an attack, in the LABEL rather than in the body, because the label
368// is the part this change hands to `innerHTML` on its own. The image is the one
369// that really fires: an `onerror` that has not fired yet is not an `onerror` that
370// was stripped, and the two look identical at zero milliseconds.
371const NASTY = 'Careful now.\n\n<details>\n<summary>Mind '
372 + '<img src="/no-such-image-7731.png" onerror="window.__pwned=1"> '
373 + '<script>window.__pwned=1;<\/script> '
374 + '<a href="javascript:window.__pwned=1">the</a> gap</summary>\n\n'
375 + 'The body.\n\n</details>\n';
376
377// ── What the model writes now, which is one line ─────────────────────
378//
379// Three wordings by three authors asked for the `<details>` and got it 5 times in
380// 76 (dev/PROMPT_NOTES.md §5, dev/REGISTER_NOTES.md §11). The markup is the app's
381// now. `SEAMED` is what a complying model sends; `SEAMBARE` is the same line with
382// nothing in front of it, which must NOT fold, or the app has reproduced the one
383// verdict CONTRACT_FOLD.md §5 calls worse than no control at all.
384const SEAMED = 'The cache was cold, and it is warm again now.\n\n'
385 + 'Fold: The deploy dropped the warmed image and the replacement came up empty, so the '
386 + 'first request after a cutover pays for the fill.\n\n' + BODY + '\n';
387const SEAMBARE = 'Fold: The deploy dropped the warmed image and the replacement came up '
388 + 'empty, so the first request pays.\n\n' + BODY + '\n';
389
390// A fold inside a fold. The renderer nests correctly for free, because the
391// browser's parser does; what has to be proved is that the KEY does not, since a
392// nested fold takes no ordinal (CONTRACT_FOLD.md §8) and an extra one here would
393// rename every fold after it in the engine's eyes.
394const NESTED = 'Here it is.\n\n<details>\n<summary>Outer label</summary>\n\n'
395 + 'The part above.\n\n<details>\n<summary>Inner label</summary>\n\n'
396 + 'The deepest part.\n\n</details>\n\nThe part below.\n\n</details>\n';
397
398const s = await open({ name: 'twodepth', profile: PROFILE, route: serveBreaks });
399const page = s.page;
400
401/// The bubble a turn just drew, by the label of the fold in it.
402const bubbleFor = (label) => page.evaluateHandle((lbl) => {
403 const all = [...document.querySelectorAll('#chat-output .chat-msg-assistant')];
404 return all.reverse().find(b => {
405 const sm = b.querySelector('details > summary');
406 return sm && sm.textContent.trim() === lbl;
407 }) || null;
408}, label);
409
410try {
411 await page.waitForFunction(() => window.DaimondRender && window.DaimondRender.foldScan,
412 null, { timeout: 20000 });
413
414 // ── 0c. EVERY turn pushes the open set, not just the one that was checked ──
415 //
416 // Static, and deliberately so: the browser checks below drive an ordinary
417 // chat, and an ordinary chat was never the path that was broken. `doSteer`
418 // -- the Diamond's own thread, which is the surface Daimond is developed
419 // from -- set `_generating` and streamed without ever calling
420 // `pushOpenFolds`, so a Diamond's app carried an EMPTY open set for its
421 // whole life and every fold body was stripped from every payload. The
422 // feature worked, was tested, and was inert where it mattered most.
423 //
424 // So the property is not "a turn pushes the open set" but "there is no turn
425 // that does not". Each `_generating = true` is a turn beginning; the push
426 // must be within reach of it in the same function. A third turn path added
427 // without the call reddens here rather than in a month of use.
428 {
429 const js = fs.readFileSync(path.join(WWW, 'js/daimond.js'), 'utf8');
430 const lines = js.split('\n');
431 const starts = [], missing = [];
432 lines.forEach((ln, i) => { if (/_generating\s*=\s*true/.test(ln)) starts.push(i); });
433 for (const i of starts) {
434 // The push sits beside the app the turn will run on, which is within a
435 // few lines of the flag in both existing paths. Sixty lines is wide
436 // enough for a commented one and far narrower than a function.
437 const near = lines.slice(Math.max(0, i - 60), i + 60).join('\n');
438 if (!/pushOpenFolds\s*\(/.test(near)) missing.push(`daimond.js:${i + 1}`);
439 }
440 check(`0c every turn pushes what is open on screen (${starts.length} turn path(s))`,
441 starts.length >= 2 && missing.length === 0,
442 missing.length ? `no pushOpenFolds near ${missing.join(', ')}`
443 : `${starts.length} turn path(s), all push`);
444 }
445
446 // ── 1. The key, against the fixture ───────────────────────────────
447 //
448 // Body length is counted in CODE POINTS on both sides, because the contract
449 // says `chars().count()` and a JS `.length` counts UTF-16 units — the two part
450 // company on the first emoji a model puts in a fold.
451 const keyed = await page.evaluate((cases) => cases.map((c) => {
452 const folds = window.DaimondRender.foldScan(c.input);
453 return { keys: folds.map(f => f.key), chars: folds.map(f => [...f.body].length) };
454 }), fixture.cases);
455 check('0 the fixture on disk was actually read',
456 Array.isArray(fixture.cases) && fixture.cases.length > 0
457 && keyed.length === fixture.cases.length,
458 `${fixture.cases.length} case(s) from ${path.relative(path.join(HERE, '..'), FIXTURE)}`);
459 const wrong = [];
460 fixture.cases.forEach((c, i) => {
461 const okK = JSON.stringify(keyed[i].keys) === JSON.stringify(c.keys);
462 const okC = JSON.stringify(keyed[i].chars) === JSON.stringify(c.body_chars);
463 if (!okK || !okC) {
464 wrong.push(`${c.name}: keys ${JSON.stringify(keyed[i].keys)} want ${JSON.stringify(c.keys)}`
465 + `, chars ${JSON.stringify(keyed[i].chars)} want ${JSON.stringify(c.body_chars)}`);
466 }
467 });
468 check(`1a every fixture case keys as the contract says (${fixture.cases.length})`,
469 wrong.length === 0, wrong.slice(0, 2).join(' | '));
470
471 // WHY THE RENDERER HOLDS A HALF-WRITTEN FOLD BACK, measured rather than
472 // assumed. `marked` hands raw HTML through, and the parser auto-closes an
473 // unfinished element at the end of the fragment -- so a `<details>` whose
474 // `</summary>` has not arrived is already a real, closed disclosure with a
475 // growing label. Drawn, it appears shut and then snaps open when the control
476 // proper replaces it. Recorded here because it is the reason for `foldPending`
477 // and nothing else in the tree says it.
478 const halfway = await page.evaluate(() => {
479 const d = document.createElement('div');
480 d.innerHTML = window.DaimondRender.md('<details>\n<summary>Half typed');
481 const f = d.querySelector('details');
482 return f ? { drawn: true, label: (f.querySelector('summary') || {}).textContent } : { drawn: false };
483 });
484 check('1b an unfinished `<details>` really is auto-closed into a live control',
485 halfway.drawn === true && halfway.label === 'Half typed', JSON.stringify(halfway));
486
487 // THE HOLD-BACK ITSELF, asked deterministically. Check 3a below puts the same
488 // question to a real stream, but whether a frame lands inside the few
489 // milliseconds between `<details>` and `</summary>` is a race, and a prover
490 // that only sometimes reddens is not a prover.
491 const pending = await page.evaluate(() => {
492 const half = 'Above the fold.\n\n<details open>\n<summary>Half typ';
493 const seg = (a) => a.map(x => x.kind + ':' + (x.kind === 'fold' ? x.key : x.text));
494 return {
495 live: seg(window.DaimondRender.foldSegments(half, false)),
496 done: seg(window.DaimondRender.foldSegments(half, true)),
497 };
498 });
499 check('1c a half-written fold draws nothing at all until it IS a fold',
500 pending.live.length === 1 && pending.live[0] === 'text:Above the fold.\n\n',
501 JSON.stringify(pending.live));
502 check('1d but a turn that died half way through one still shows what arrived',
503 pending.done.length === 1 && /<details open>/.test(pending.done[0]),
504 JSON.stringify(pending.done));
505
506 // ── The instrument ────────────────────────────────────────────────
507 //
508 // Installed before the turn, sampling once per frame. A MutationObserver
509 // counts insertions rather than reading a count at the end, because "one fold
510 // on screen" is exactly what a fold rebuilt sixty times still looks like.
511 //
512 // SCOPED TO ONE BUBBLE, by the count of them standing before the turn began.
513 // "The last assistant message" is not the same question: the app may draw
514 // another one while this turn is still going, and a count of folds taken over
515 // the whole thread would then answer for two messages at once.
516 await page.evaluate(() => {
517 const out = document.getElementById('chat-output');
518 const asst = () => out.querySelectorAll('.chat-msg-assistant');
519 const st = { made: 0, uid: 0, samples: [], sent: [], base: asst().length };
520 window.__twodepth = st;
521 const mine = () => asst()[st.base] || null;
522 const stamp = (el) => {
523 const b = mine();
524 if (!b || !b.contains(el) || el.dataset.probeUid) return;
525 el.dataset.probeUid = String(++st.uid);
526 st.made++;
527 };
528 const obs = new MutationObserver((recs) => {
529 for (const r of recs) {
530 for (const n of r.addedNodes) {
531 if (n.nodeType !== 1) continue;
532 if (n.matches && n.matches('details.md-fold')) stamp(n);
533 if (n.querySelectorAll) n.querySelectorAll('details.md-fold').forEach(stamp);
534 }
535 }
536 });
537 obs.observe(out, { childList: true, subtree: true });
538 const tick = () => {
539 const b = mine();
540 if (b) {
541 const fold = b.querySelector('details.md-fold');
542 const seg = b.querySelector('.md-seg');
543 const body = b.querySelector('.md-fold-body');
544 st.samples.push({
545 all: b.innerText.length,
546 seg: seg ? seg.innerText.length : -1,
547 body: body ? body.innerText.length : -1,
548 fold: !!fold,
549 bubbles: asst().length,
550 });
551 }
552 st.raf = requestAnimationFrame(tick);
553 };
554 tick();
555 st.stop = () => { obs.disconnect(); cancelAnimationFrame(st.raf); };
556 });
557
558 // The engine's own door, wrapped and still called. What is recorded is what
559 // the wasm boundary was handed, not what this file hoped it would be.
560 const shimmed = await page.evaluate(async () => {
561 const m = await import('/pkg/oxedyne_daimond.js');
562 const P = m.DaimondApp && m.DaimondApp.prototype;
563 if (!P || typeof P.set_open_folds !== 'function') return false;
564 const orig = P.set_open_folds;
565 P.set_open_folds = function (json) {
566 window.__twodepth.sent.push(json);
567 return orig.call(this, json);
568 };
569 return true;
570 });
571 check('0b the engine offers `set_open_folds` to wrap', shimmed === true);
572
573 // ── 2 & 3. A real turn, streamed ──────────────────────────────────
574 await chat(s, '@text ' + STREAMED);
575 const run = await page.evaluate(() => {
576 const st = window.__twodepth;
577 st.stop();
578 return { made: st.made, samples: st.samples, sent: st.sent.slice() };
579 });
580 const seen = run.samples;
581 const last = seen[seen.length - 1] || { all: 0, seg: -1, body: -1, fold: false };
582 const distinct = (k) => new Set(seen.map(x => x[k]).filter(v => v >= 0)).size;
583 const iFold = seen.findIndex(x => x.fold);
584
585 check('2a the answer arrived a piece at a time, not all at the end',
586 distinct('all') >= 4, `${distinct('all')} distinct length(s) over ${seen.length} frame(s)`);
587 check('2b the text ABOVE the fold was complete on screen while the fold was still filling',
588 iFold >= 0 && seen[iFold].seg > 0 && seen[iFold].body < last.body
589 && seen[iFold].seg === last.seg,
590 iFold < 0 ? 'no fold was ever drawn'
591 : `at the fold's first frame: above=${seen[iFold].seg}/${last.seg}, `
592 + `inside=${seen[iFold].body}/${last.body}`);
593 check('2c the fold\'s own body grew across frames',
594 distinct('body') >= 3 && seen.filter(x => x.body >= 0)
595 .every((x, i, a) => i === 0 || x.body >= a[i - 1].body),
596 `${distinct('body')} distinct length(s), final ${last.body}`);
597 check('3a the fold control was inserted ONCE for the whole stream',
598 run.made === 1, `${run.made} insertion(s) over ${seen.length} frame(s)`);
599 const uid = await page.evaluate(() => {
600 const b = document.querySelectorAll('#chat-output .chat-msg-assistant')[window.__twodepth.base];
601 const d = b && b.querySelector('details.md-fold');
602 return d ? d.dataset.probeUid : null;
603 });
604 check('3b and the fold still on screen is that same element',
605 uid === '1', String(uid) + `; ${new Set(seen.map(x => x.bubbles)).size} bubble count(s) seen`);
606
607 // ── 4. A closed fold's body ───────────────────────────────────────
608 await chat(s, '@text ' + SHUT);
609 /// Shown, in two geometric facts at once. See the header: a closed fold's
610 /// contents keep their last layout, so a rect with area proves nothing alone.
611 const measure = (label) => page.evaluate((lbl) => {
612 const all = [...document.querySelectorAll('#chat-output details.md-fold')];
613 const d = all.reverse().find(x => {
614 const sm = x.querySelector('summary');
615 return sm && sm.textContent.trim() === lbl;
616 });
617 if (!d) return null;
618 d.scrollIntoView({ block: 'center' });
619 const el = d.querySelector('.md-fold-body > *');
620 if (!el) return { open: d.open, rect: false, hit: false, why: 'no body' };
621 const a = d.getBoundingClientRect(), c = el.getBoundingClientRect();
622 const at = document.elementFromPoint(c.left + c.width / 2, c.top + c.height / 2);
623 return {
624 open: d.open,
625 // Real area, and inside the fold that is meant to be containing it.
626 rect: c.height > 0 && c.top >= a.top - 0.5 && c.bottom <= a.bottom + 0.5,
627 hit: !!at && d.contains(at),
628 h: Math.round(c.height),
629 };
630 }, label);
631 const shut = await measure('What was actually wrong');
632 check('4a a fold the model did not open starts closed',
633 !!shut && shut.open === false, shut ? JSON.stringify(shut) : 'no fold');
634 check('4b and its body is not on screen — containment AND a hit test',
635 !!shut && !shut.rect && !shut.hit, shut ? JSON.stringify(shut) : null);
636 // `el.click()`, not page.click with force: a forced click does nothing at all
637 // on this app headless, and has flaked several older verifiers.
638 await page.evaluate((lbl) => {
639 const all = [...document.querySelectorAll('#chat-output details.md-fold')];
640 const d = all.reverse().find(x => {
641 const sm = x.querySelector('summary');
642 return sm && sm.textContent.trim() === lbl;
643 });
644 if (d) d.querySelector('summary').click();
645 }, 'What was actually wrong');
646 await page.waitForTimeout(250);
647 const shown = await measure('What was actually wrong');
648 check('4c pressing the label puts it on screen — the same two facts, the other way',
649 !!shown && shown.open === true && shown.rect && shown.hit,
650 shown ? JSON.stringify(shown) : null);
651
652 // ── 5. The open set, at the wasm boundary ─────────────────────────
653 const sent = await page.evaluate(() => window.__twodepth.sent.slice());
654 const lastSent = sent.length ? sent[sent.length - 1] : '';
655 let parsed = null;
656 try { parsed = JSON.parse(lastSent); } catch (e) { /* the check below says so */ }
657 check('5a opening a fold hands the engine a set with that fold\'s key in it',
658 Array.isArray(parsed) && parsed.indexOf('0:What was actually wrong') >= 0,
659 lastSent ? lastSent.slice(0, 160) : 'nothing was ever handed to the engine');
660
661 // ── 6. A fold with nothing above it ───────────────────────────────
662 await chat(s, '@text ' + BARE);
663 const bare = await measure('Everything I have');
664 const bareMark = await page.evaluate(() => {
665 const all = [...document.querySelectorAll('#chat-output details.md-fold')];
666 const d = all.reverse().find(x => {
667 const sm = x.querySelector('summary');
668 return sm && sm.textContent.trim() === 'Everything I have';
669 });
670 return d ? { marked: d.classList.contains('md-fold-bare'),
671 above: (d.previousElementSibling || {}).textContent || '' } : null;
672 });
673 check('6a a fold with nothing above it is drawn OPEN, not shut',
674 !!bare && bare.open === true && bare.rect && bare.hit,
675 bare ? JSON.stringify(bare) : 'no fold');
676 check('6b and the renderer says so, rather than it being a coincidence',
677 !!bareMark && bareMark.marked === true && !String(bareMark.above).trim(),
678 bareMark ? JSON.stringify(bareMark) : null);
679
680 // ── 7. A fenced fold is a model showing markup ────────────────────
681 await chat(s, '@text ' + FENCED);
682 const fenced = await page.evaluate(() => {
683 const all = [...document.querySelectorAll('#chat-output .chat-msg-assistant')];
684 const b = all.reverse().find(x => /Write it like this/.test(x.innerText || ''));
685 if (!b) return null;
686 return {
687 folds: b.querySelectorAll('details').length,
688 literal: /<details>/.test(b.innerText || ''),
689 code: b.querySelectorAll('pre code').length,
690 };
691 });
692 check('7a a `<details>` inside a fence draws no control',
693 !!fenced && fenced.folds === 0, fenced ? JSON.stringify(fenced) : 'no bubble');
694 check('7b and the reader still sees the markup that was being shown',
695 !!fenced && fenced.literal && fenced.code > 0,
696 fenced ? JSON.stringify(fenced) : null);
697
698 // ── 8. Nothing executable rides in on the label ───────────────────
699 await chat(s, '@text ' + NASTY);
700 // The image has to be given time to fail before "nothing ran" means anything.
701 await page.waitForTimeout(900);
702 const nasty = await page.evaluate(() => {
703 const all = [...document.querySelectorAll('#chat-output details.md-fold')];
704 const d = all.reverse().find(x => /Mind/.test((x.querySelector('summary') || {}).textContent || ''));
705 if (!d) return null;
706 const sum = d.querySelector('summary');
707 return {
708 pwned: !!window.__pwned,
709 scripts: sum.querySelectorAll('script').length,
710 onerror: sum.querySelectorAll('[onerror]').length,
711 hrefs: [...sum.querySelectorAll('a')].map(a => a.getAttribute('href') || ''),
712 label: String(sum.textContent || '').replace(/\s+/g, ' ').trim(),
713 key: d.dataset.foldKey || '',
714 };
715 });
716 check('8a the label carries no script, no handler and no javascript: url',
717 !!nasty && nasty.pwned === false && nasty.scripts === 0 && nasty.onerror === 0
718 && nasty.hrefs.every(h => !/^javascript:/i.test(h)),
719 nasty ? JSON.stringify(nasty) : 'no fold');
720 check('8b but the words the model wrote are still its label',
721 !!nasty && nasty.label === 'Mind the gap',
722 nasty ? JSON.stringify(nasty.label) : null);
723 // THE KEY AND THE LABEL LEGITIMATELY DIFFER HERE, and it is worth pinning
724 // rather than filing off. §2 says the key is the summary's TEXT CONTENT with
725 // its tags removed, and a `<script>`'s body is text content -- a DOM reading
726 // gives the same answer as the scan does. The renderer drops the element
727 // whole, as it must. So the fold is NAMED with words nobody sees, and both
728 // halves of the app agree on that name, which is the only thing the key is
729 // for.
730 check('8c and the key keeps what the sanitiser threw away, because it is not the DOM',
731 !!nasty && nasty.key === '0:Mind window.__pwned=1; the gap',
732 nasty ? JSON.stringify(nasty.key) : null);
733
734 // ── 9. A fold inside a fold ───────────────────────────────────────
735 await chat(s, '@text ' + NESTED);
736 const nested = await page.evaluate(() => {
737 const all = [...document.querySelectorAll('#chat-output details.md-fold')];
738 const outer = all.reverse().find(d =>
739 (d.querySelector('summary') || {}).textContent.trim() === 'Outer label');
740 if (!outer) return null;
741 // Opened here, because a nested fold is only worth asking about once the
742 // fold holding it is on screen.
743 outer.querySelector('summary').click();
744 const inner = outer.querySelector('details.md-fold');
745 return {
746 outerKey: outer.dataset.foldKey || '',
747 innerKey: inner ? (inner.dataset.foldKey || '') : '(no inner fold)',
748 contained: !!inner && outer.contains(inner),
749 outerOpen: outer.open, innerOpen: inner ? inner.open : null,
750 };
751 });
752 check('9a the outer fold takes ordinal 0 and the inner takes no key at all',
753 !!nested && nested.outerKey === '0:Outer label' && nested.innerKey === '',
754 nested ? JSON.stringify(nested) : 'no fold');
755 check('9b and the inner one really is inside it, still shut when the outer opens',
756 !!nested && nested.contained && nested.outerOpen === true && nested.innerOpen === false,
757 nested ? JSON.stringify(nested) : null);
758
759 await chat(s, '@text ' + ALONE);
760
761 // ── 10. The reader's choice survives a reload ─────────────────────
762 //
763 // The fold opened at 4c, after a real reload of the page and a real unlock —
764 // not a redraw. The key holds no message identity, so nothing about the
765 // message had to be stored for this to work.
766 await page.reload({ waitUntil: 'domcontentloaded' });
767 await signInAs(s, 'twodepth');
768 await page.waitForSelector('#chat-output details.md-fold', { timeout: 20000 })
769 .catch(() => { /* the check below says what was there */ });
770 await page.waitForTimeout(600);
771 const after = await page.evaluate(() => {
772 const all = [...document.querySelectorAll('#chat-output details.md-fold')];
773 return all.map(d => ({
774 label: (d.querySelector('summary') || {}).textContent.trim(),
775 open: d.open,
776 }));
777 });
778 const restored = after.find(x => x.label === 'What was actually wrong');
779 const untouched = after.find(x => x.label === 'Left alone');
780 check('10a the fold the reader opened comes back open',
781 !!restored && restored.open === true, JSON.stringify(after));
782 check('10b and one the reader never touched comes back shut',
783 !!untouched && untouched.open === false, JSON.stringify(after));
784
785 // ── 12. Narration is working, and the last word is the answer ─────
786 //
787 // `@narrate` makes the mock send prose AND a tool call in one message, which no fixture
788 // here could do until today -- and a provider does it constantly. Both halves are checked
789 // in one turn, because the rule is a DIVISION and testing one side of it would pass on a
790 // build that folded everything, reply included.
791 await chat(s, '@narrate Let me pin the exact line numbers first, and check nothing else '
792 + 'renders that key. ;; file_list {"path":"."}');
793 await page.waitForTimeout(900);
794 const split = await page.evaluate(() => {
795 const out = document.querySelector('#chat-output');
796 const working = [...out.querySelectorAll('.chat-msg-working')];
797 const answers = [...out.querySelectorAll('.chat-msg-assistant')]
798 .map(d => (d.textContent || '').trim()).filter(Boolean);
799 return {
800 working: working.length,
801 workingSaid: working.map(w => (w.textContent || '').trim().slice(0, 60)),
802 // The narration must NOT be in any assistant bubble.
803 leaked: answers.some(t => t.indexOf('Let me pin the exact line numbers') >= 0),
804 // And the turn's last word must still be a real answer.
805 answered: answers.some(t => t.indexOf('Narration done') >= 0),
806 };
807 });
808 check('12a prose that precedes a tool call is drawn as working',
809 !!split && split.working >= 1, JSON.stringify(split));
810 check('12b and is NOT left in the thread as an answer',
811 !!split && split.leaked === false, JSON.stringify(split));
812 check('12c while the reply after the last call is still an answer',
813 !!split && split.answered === true, JSON.stringify(split));
814 // AND IT DOES NOT HIDE WITH THE TOOL STEPS, WHICH IS A REVERSAL.
815 //
816 // This check read `hidden === false` from the day the working tile was written until
817 // 2026-08-28, on the 2026-08-23 reasoning that a reader who turned the working off meant
818 // this too. What the rule actually did was take the MODEL'S OWN SENTENCES off the screen:
819 // a turn of twenty tool calls drew one final answer and nothing else, which is note 05 of
820 // the 2026-08-27 round arriving by its other door. The owner reversed it. The switch is
821 // called Steps and it hides the steps; the working is collapsed, not hidden.
822 //
823 // The tool step is measured in the same breath, because the reversal must not have taken
824 // the switch's real job with it. `dev/verify_visible.mjs` measures both as ink on a real
825 // screen; this pins the rule.
826 const underSwitch = await page.evaluate(() => {
827 const out = document.querySelector('#chat-output');
828 out.classList.add('hide-tools');
829 const w = out.querySelector('.chat-msg-working');
830 const t = out.querySelector('.tool-block');
831 const seen = {
832 working: w ? getComputedStyle(w).display !== 'none' : null,
833 step: t ? getComputedStyle(t).display !== 'none' : null,
834 };
835 out.classList.remove('hide-tools');
836 return seen;
837 });
838 check('12d the working does NOT hide with the tool steps — it is the model\'s own prose',
839 underSwitch.working === true, JSON.stringify(underSwitch));
840 check('12e while the tool step still does, which is what the switch is for',
841 underSwitch.step === false, JSON.stringify(underSwitch));
842
843 // ── 13. The seam the APP places ───────────────────────────────────
844 //
845 // Everything above this point tests a fold the MODEL wrote, and 5 answers in 76
846 // carried one. The app places the seam now: the model writes a line beginning
847 // `Fold:` and the app builds the element, keys it, strips it and draws it. So
848 // what has to be proved is the expansion -- character for character, because
849 // the engine builds the same string in Rust and a key the two disagree about is
850 // a fold the reader opens that never leaves the payload -- and the refusals,
851 // which are the half that keeps a length rule from becoming FOLD-ALL.
852 const seamed = await page.evaluate((cases) => cases.map((c) => {
853 const got = window.DaimondRender.seamText(c.input, true);
854 return { out: got === c.input ? null : got,
855 keys: window.DaimondRender.foldScan(got).map(f => f.key) };
856 }), seamfix.cases);
857 check('13 the seam fixture on disk was actually read',
858 Array.isArray(seamfix.cases) && seamfix.cases.length >= 14
859 && seamed.length === seamfix.cases.length,
860 `${seamfix.cases.length} case(s) from ${path.relative(path.join(HERE, '..'), SEAMFIX)}`);
861 const seamwrong = [];
862 seamfix.cases.forEach((c, i) => {
863 const want = c.seamed === null || c.seamed === undefined ? null : c.seamed;
864 if (seamed[i].out !== want) {
865 seamwrong.push(`${c.name}: got ${JSON.stringify(String(seamed[i].out).slice(0, 90))}`);
866 } else if (JSON.stringify(seamed[i].keys) !== JSON.stringify(c.keys)) {
867 seamwrong.push(`${c.name}: keys ${JSON.stringify(seamed[i].keys)}`);
868 }
869 });
870 check(`13a every seam case expands as the contract says (${seamfix.cases.length})`,
871 seamwrong.length === 0, seamwrong.slice(0, 2).join(' | '));
872
873 // A LINE THAT WOULD FOLD EVERYTHING FOLDS NOTHING, on screen and not in a unit
874 // test: the words the model wrote are all still there, and there is no control
875 // over them. This is the check the whole design turns on.
876 await chat(s, '@text ' + SEAMBARE);
877 const headless = await page.evaluate(() => {
878 const b = [...document.querySelectorAll('#chat-output .chat-msg-assistant')].pop();
879 if (!b) return { why: 'no bubble' };
880 return {
881 folds: b.querySelectorAll('details.md-fold').length,
882 marker: /(^|\n)\s*Fold:/.test(b.innerText),
883 kept: b.innerText.indexOf('the replacement came up empty') >= 0,
884 shown: b.innerText.length,
885 };
886 });
887 check('13b a seam with nothing above it folds NOTHING, and keeps every word',
888 headless.folds === 0 && headless.kept === true && headless.marker === false,
889 JSON.stringify(headless));
890
891 // AND THE QUALIFYING ONE FOLDS, or 13b would pass on a build where the seam
892 // never fired at all.
893 await chat(s, '@text ' + SEAMED);
894 const made = await page.evaluate(() => {
895 const b = [...document.querySelectorAll('#chat-output .chat-msg-assistant')].pop();
896 const d = b && b.querySelector('details.md-fold');
897 if (!d) return { why: 'no fold', text: b ? b.innerText.slice(0, 80) : null };
898 const sum = d.querySelector('summary');
899 const above = [...b.querySelectorAll('p')].find(x => !d.contains(x));
900 return {
901 key: d.dataset.foldKey,
902 open: d.open,
903 label: sum ? sum.textContent.trim() : null,
904 above: above ? above.textContent.trim() : null,
905 // The body is inside the control and not beside it: a seam that cut in
906 // the wrong place would leave the working in the bubble either way.
907 inside: d.textContent.indexOf('the warmer runs after the cutover') >= 0,
908 beside: b.innerText.indexOf('the warmer runs after the cutover') >= 0,
909 marker: /(^|\n)\s*Fold:/.test(b.innerText),
910 };
911 });
912 check('13c a qualifying seam becomes one real fold, keyed as the contract says',
913 made.key === '0:' + made.label && /deploy dropped the warmed image/.test(made.label || ''),
914 JSON.stringify(made).slice(0, 200));
915 check('13d with the answer above it and the working inside it, shut',
916 made.above === 'The cache was cold, and it is warm again now.'
917 && made.inside === true && made.beside === false && made.marker === false,
918 JSON.stringify(made).slice(0, 200));
919
920 // MID-STREAM, the one outcome the engine cannot have: a seam whose body has not
921 // arrived yet is held back rather than drawn and then unwound. `foldPending`
922 // makes the same argument for a half-written `<details>` and 1c proves it there.
923 const holding = await page.evaluate(() => {
924 const half = 'The cache was cold, and it is warm again now.\n\n'
925 + 'Fold: The deploy dropped the warmed image and the replacement came up empty.\n\nSo f';
926 const seg = (a) => a.map(x => x.kind + ':' + (x.kind === 'fold' ? x.key : x.text));
927 return { live: seg(window.DaimondRender.foldSegments(half, false)),
928 done: seg(window.DaimondRender.foldSegments(half, true)) };
929 });
930 check('13e a seam whose body is still arriving shows neither a fold nor the marker',
931 holding.live.length === 1
932 && holding.live[0] === 'text:The cache was cold, and it is warm again now.\n\n',
933 JSON.stringify(holding.live).slice(0, 160));
934 check('13f and a turn that died before the body arrived still shows what did',
935 holding.done.length === 1 && /Fold:/.test(holding.done[0]) === false
936 && /came up empty/.test(holding.done[0]),
937 JSON.stringify(holding.done).slice(0, 160));
938
939 // ── 11. The label is legible ──────────────────────────────────────
940 //
941 // A fold nobody finds is a fold nobody opens, and every check above this one
942 // passes on exactly that. The owner, 2026-08-23, located a real fold on his own
943 // screen only after being told it was there: the summary was `--text-muted` at
944 // `--fs-sm`, which between a reply above and a reply below reads as a caption
945 // for something rather than as a thing to read.
946 //
947 // MEASURED AGAINST THE ANSWER IT BELONGS TO, never against a value written
948 // here. A check pinned to `#ECE6DC` would go red on a theme and green on a
949 // regression under the pink one; a check pinned to the answer's own computed
950 // style moves with every theme and still catches a summary set apart as minor.
951 const legible = await page.evaluate(() => {
952 const d = [...document.querySelectorAll('#chat-output details.md-fold')]
953 .filter(x => !x.classList.contains('chat-msg-thinking')).pop();
954 if (!d) return { why: 'no fold on screen' };
955 const sum = d.querySelector('summary');
956 const bubble = d.closest('.chat-msg-content') || d.parentElement;
957 // The answer above the fold, in the same bubble. `!d.contains` because the
958 // fold's own body is full of paragraphs and one of those proves nothing.
959 const above = [...bubble.querySelectorAll('p')].find(x => !d.contains(x));
960 if (!sum || !above) return { why: 'no summary, or nothing above the fold' };
961 const a = getComputedStyle(sum), b = getComputedStyle(above);
962 return { size: a.fontSize, answerSize: b.fontSize,
963 ink: a.color, answerInk: b.color };
964 });
965 check('11a the summary is set at the size of the answer it belongs to',
966 !!legible && legible.size === legible.answerSize, JSON.stringify(legible));
967 check('11b and in the same ink, not the grey of a caption',
968 !!legible && legible.ink === legible.answerInk, JSON.stringify(legible));
969
970 // The attack's own 404 is expected -- the image is MEANT to fail, which is what
971 // makes 8a mean anything -- so it is not counted as the app throwing.
972 const errs = errors(s).filter(e => !/Failed to load resource/.test(e))
973 .filter(e => !/no-such-image-7731/.test(e));
974 check('nothing threw while it was on screen', errs.length === 0, errs.slice(0, 3).join(' | '));
975
976 // The picture, with the ink centred: the guide put away so the thread has the
977 // width, and the fold that streamed opened in the middle of it. A fold that is
978 // technically right and visually wrong is not done, and the only way to know
979 // which it is, is to look.
980 await page.evaluate(() => {
981 // The guide rides in the Web panel; its own closer sends the panel back to
982 // the tag row, which is what a reader would do before settling in to read.
983 const x = document.querySelector('#panel-web [data-close="web"]');
984 if (x) x.click();
985 });
986 await page.waitForTimeout(500);
987 await page.evaluate(() => {
988 const d = [...document.querySelectorAll('#chat-output details.md-fold')]
989 .find(x => (x.querySelector('summary') || {}).textContent.trim() === 'The long version');
990 if (d) { d.open = true; d.scrollIntoView({ block: 'center' }); }
991 });
992 await page.waitForTimeout(400);
993 await shot(s, 'twodepth' + (BREAK ? '-' + BREAK : ''));
994 // THE SEAM ON SCREEN, both ways round, because four claims about this product
995 // have been made from source alone and every one was wrong. One picture of an
996 // answer the app folded and one of an answer it refused to fold.
997 await page.evaluate(() => {
998 const d = [...document.querySelectorAll('#chat-output details.md-fold')]
999 .find(x => /deploy dropped the warmed image/.test(
1000 (x.querySelector('summary') || {}).textContent || ''));
1001 if (d) { d.open = false; d.scrollIntoView({ block: 'center' }); }
1002 });
1003 await page.waitForTimeout(400);
1004 await shot(s, 'twodepth-seam-folded' + (BREAK ? '-' + BREAK : ''));
1005 await page.evaluate(() => {
1006 const b = [...document.querySelectorAll('#chat-output .chat-msg-assistant')]
1007 .filter(x => x.querySelectorAll('details.md-fold').length === 0).pop();
1008 if (b) b.scrollIntoView({ block: 'center' });
1009 });
1010 await page.waitForTimeout(400);
1011 await shot(s, 'twodepth-seam-refused' + (BREAK ? '-' + BREAK : ''));
1012} catch (e) {
1013 check('the run got to the end of itself', false,
1014 String(e && e.message ? e.message : e).split('\n')[0]);
1015 try { await shot(s, 'twodepth-threw' + (BREAK ? '-' + BREAK : '')); } catch (e2) { /* none */ }
1016} finally {
1017 await s.close();
1018}
1019
1020if (BREAK) {
1021 console.log(`\nbreak '${BREAK}': ${bad.length} check(s) failed`
1022 + (bad.length ? ' — ' + bad.join('; ') : ' — NOTHING FAILED, so the checks above prove nothing'));
1023 process.exit(bad.length ? 0 : 1); // a break MUST fail something
1024}
1025console.log(bad.length === 0
1026 ? `\ntwodepth: all ${ok.length} checks passed`
1027 : `\ntwodepth: ${bad.length} of ${ok.length + bad.length} checks FAILED`);
1028process.exit(bad.length === 0 ? 0 : 1);