oxedyne/daimond/dev/verify_artefacts.mjs
10.9 KiB, 1 run
created by r2519314175:239, 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_artefacts.mjs — what a Diamond produced, derived rather than declared. |
| 2 | // |
| 3 | // Nobody maintains this list. Every tool call is already recorded on the turn as a |
| 4 | // `tool_log` carrying its name and arguments, so the artefacts of a stretch of work can be |
| 5 | // read straight off it — and a derived list cannot drift, because there is nothing to keep |
| 6 | // up to date. The harvest happens at the fold, which is both the last moment the tool calls |
| 7 | // still exist (folding drops the back-and-forth) and a moment the user has already blessed. |
| 8 | // |
| 9 | // What is pinned here: that writes count and reads do not, that re-folding does not stack |
| 10 | // duplicates, that a link to a since-deleted file says so rather than failing quietly, and |
| 11 | // that the strip reads as an offer while there is nothing in it -- it used to hide, which |
| 12 | // took the `+` inside it out of reach in the one state that control exists for. |
| 13 | // |
| 14 | // Run with dev/serve.mjs up. No gateway needed. |
| 15 | import { open, signInAs } from './harness.mjs'; |
| 16 | |
| 17 | const ok = [], bad = []; |
| 18 | const check = (name, pass, detail) => { |
| 19 | (pass ? ok : bad).push(name + (detail ? ' — ' + detail : '')); |
| 20 | console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : '')); |
| 21 | }; |
| 22 | |
| 23 | const s = await open({ name: 'artefacts', connect: false }); |
| 24 | const p = s.page; |
| 25 | await p.waitForTimeout(2500); |
| 26 | |
| 27 | // ── A Diamond, and a chat whose turns carry tool calls ───────────────────── |
| 28 | const diamondId = await p.evaluate(async () => { |
| 29 | const mod = await import('../pkg/oxedyne_daimond.js'); |
| 30 | const app = new mod.DaimondApp('http://127.0.0.1/v1/chat/completions', '', 'none', 256, '', true); |
| 31 | return await app.create_diamond('Ship the launch'); |
| 32 | }); |
| 33 | check('a Diamond was created', !!diamondId, diamondId); |
| 34 | |
| 35 | // The harvest reads `tool_log` messages, so this is exactly the shape a real turn leaves. |
| 36 | const harvested = await p.evaluate(async ({ diamondId }) => { |
| 37 | const tl = (name, args) => ({ role: 'tool_log', name, args: JSON.stringify(args) }); |
| 38 | const messages = [ |
| 39 | { role: 'user', content: 'do the thing' }, |
| 40 | tl('file_read', { path: 'notes/old-research.md' }), // a READ: not an artefact |
| 41 | tl('file_search',{ query: 'pricing' }), // also not |
| 42 | tl('file_write', { path: 'notes/pricing.md', content: 'x' }), |
| 43 | tl('file_edit', { path: 'notes/launch-copy.md' }), |
| 44 | tl('web_open', { url: 'https://stripe.com/docs?a=1' }), |
| 45 | tl('web_fetch', { url: 'https://example.com/background' }), // background: not deliberate |
| 46 | tl('file_write', { path: 'notes/pricing.md', content: 'again' }), // same file twice |
| 47 | { role: 'assistant', content: 'done' }, |
| 48 | ]; |
| 49 | // Drive the harvest the way an accepted fold does. |
| 50 | await window.DaimondArtefacts.harvest(diamondId, { sourceRun: { messages } }); |
| 51 | const mod = await import('../pkg/oxedyne_daimond.js'); |
| 52 | const app = new mod.DaimondApp('http://127.0.0.1/v1/chat/completions', '', 'none', 256, '', true); |
| 53 | return JSON.parse(await app.links_touching('diamond:' + diamondId) || '[]'); |
| 54 | }, { diamondId }); |
| 55 | |
| 56 | const refs = harvested.map(l => l.to); |
| 57 | check('a written file became an artefact', refs.includes('file:notes/pricing.md')); |
| 58 | check('an edited file became one too', refs.includes('file:notes/launch-copy.md')); |
| 59 | check('an opened page became one', refs.includes('url:https://stripe.com/docs?a=1')); |
| 60 | check('a file that was only READ did not', |
| 61 | !refs.includes('file:notes/old-research.md'), |
| 62 | 'reads are noise: forty lookups would drown the one thing that matters'); |
| 63 | check('a background fetch did not', !refs.includes('url:https://example.com/background')); |
| 64 | check('the same file written twice is one artefact', |
| 65 | refs.filter(r => r === 'file:notes/pricing.md').length === 1); |
| 66 | check('a produced file says so', harvested.find(l => l.to === 'file:notes/pricing.md')?.rel === 'produced'); |
| 67 | check('an opened page says consulted', |
| 68 | harvested.find(l => l.to.startsWith('url:'))?.rel === 'consulted'); |
| 69 | check('the harvest names itself as the source', |
| 70 | harvested.every(l => l.by === 'fold'), harvested[0]?.by); |
| 71 | check('exactly the three artefacts, and nothing else', harvested.length === 3, |
| 72 | `${harvested.length}: ${refs.join(', ')}`); |
| 73 | |
| 74 | // ── Re-folding must not stack duplicates ──────────────────────────────── |
| 75 | const again = await p.evaluate(async ({ diamondId }) => { |
| 76 | const tl = (name, args) => ({ role: 'tool_log', name, args: JSON.stringify(args) }); |
| 77 | const messages = [ |
| 78 | tl('file_write', { path: 'notes/pricing.md', content: 'third time' }), |
| 79 | tl('file_write', { path: 'notes/new-thing.md', content: 'new' }), |
| 80 | ]; |
| 81 | await window.DaimondArtefacts.harvest(diamondId, { sourceRun: { messages } }); |
| 82 | const mod = await import('../pkg/oxedyne_daimond.js'); |
| 83 | const app = new mod.DaimondApp('http://127.0.0.1/v1/chat/completions', '', 'none', 256, '', true); |
| 84 | return JSON.parse(await app.links_touching('diamond:' + diamondId) || '[]'); |
| 85 | }, { diamondId }); |
| 86 | check('folding again does not stack a duplicate', again.length === 4, |
| 87 | `${again.length} artefact(s)`); |
| 88 | check('but a genuinely new artefact is added', |
| 89 | again.some(l => l.to === 'file:notes/new-thing.md')); |
| 90 | |
| 91 | // ── A malformed tool call is skipped, not thrown on ───────────────────── |
| 92 | const malformed = await p.evaluate(async ({ diamondId }) => { |
| 93 | const messages = [ |
| 94 | { role: 'tool_log', name: 'file_write', args: 'not json at all{' }, |
| 95 | { role: 'tool_log', name: 'file_write', args: JSON.stringify({ nopath: 1 }) }, |
| 96 | { role: 'tool_log', name: 'file_write', args: JSON.stringify({ path: ' ' }) }, |
| 97 | { role: 'tool_log', name: 'file_write', args: JSON.stringify({ path: 'notes/fine.md' }) }, |
| 98 | ]; |
| 99 | try { |
| 100 | await window.DaimondArtefacts.harvest(diamondId, { sourceRun: { messages } }); |
| 101 | } catch (e) { return { threw: String(e) }; } |
| 102 | const mod = await import('../pkg/oxedyne_daimond.js'); |
| 103 | const app = new mod.DaimondApp('http://127.0.0.1/v1/chat/completions', '', 'none', 256, '', true); |
| 104 | return { links: JSON.parse(await app.links_touching('diamond:' + diamondId) || '[]') }; |
| 105 | }, { diamondId }); |
| 106 | check('a malformed tool call does not throw inside an accepted fold', !malformed.threw, |
| 107 | malformed.threw); |
| 108 | check('and the good one beside it still lands', |
| 109 | (malformed.links || []).some(l => l.to === 'file:notes/fine.md')); |
| 110 | |
| 111 | // ── The strip: hidden at zero, and a count when there is something ────── |
| 112 | // |
| 113 | // The Diamonds above were made through a fresh app instance, which the running rail knows |
| 114 | // nothing about, so it is reloaded here before anything is clicked. |
| 115 | await p.reload({ waitUntil: 'domcontentloaded' }); |
| 116 | await signInAs(s, 'artefacts'); // a reload puts the identity gate back |
| 117 | await p.waitForTimeout(3000); |
| 118 | |
| 119 | const strip = await p.evaluate(async ({ diamondId }) => { |
| 120 | // Select it the way a user does: click its row in the rail. |
| 121 | const row = Array.from(document.querySelectorAll('#diamond-list .diamond-box')) |
| 122 | .find(e => /Ship the launch/.test(e.textContent)); |
| 123 | if (row) row.click(); |
| 124 | await new Promise(r => setTimeout(r, 1200)); |
| 125 | const el = document.getElementById('arte-strip'); |
| 126 | const list = document.getElementById('arte-list'); |
| 127 | return { |
| 128 | shown: el ? el.style.display !== 'none' : null, |
| 129 | text: el ? el.textContent.trim() : null, |
| 130 | listShown: list ? list.style.display !== 'none' : null, |
| 131 | }; |
| 132 | }, { diamondId }); |
| 133 | check('the strip is shown once there are artefacts', strip.shown === true, String(strip.shown)); |
| 134 | // Asked of the running app rather than spelled here: the strip names a |
| 135 | // Diamond's workspace now, and the word is different in each of the eight |
| 136 | // languages. What this check is really about is that the strip carries a COUNT |
| 137 | // rather than a list, and that survives the rename. |
| 138 | const wantCount = await p.evaluate(() => { |
| 139 | try { return '◈ ' + window.DaimondI18n.tn('dws.count', 5); } catch (e) { return null; } |
| 140 | }); |
| 141 | check('it is a count, not a list', strip.text === wantCount, `${strip.text} vs ${wantCount}`); |
| 142 | check('the list stays closed until it is clicked', strip.listShown === false, |
| 143 | String(strip.listShown)); |
| 144 | |
| 145 | const opened = await p.evaluate(async () => { |
| 146 | document.getElementById('arte-strip').click(); |
| 147 | await new Promise(r => setTimeout(r, 700)); |
| 148 | const list = document.getElementById('arte-list'); |
| 149 | return { |
| 150 | shown: list.style.display !== 'none', |
| 151 | rows: list.querySelectorAll('.arte-row').length, |
| 152 | first: (list.querySelector('.arte-open') || {}).textContent, |
| 153 | kinds: Array.from(list.querySelectorAll('.arte-kind')).map(e => e.textContent), |
| 154 | }; |
| 155 | }); |
| 156 | check('clicking opens the list', opened.shown === true); |
| 157 | check('every artefact has a row', opened.rows === 5, `${opened.rows} row(s)`); |
| 158 | check('each row says what kind of thing it is', |
| 159 | opened.kinds.every(k => ['file', 'url'].includes(k)), opened.kinds.join(',')); |
| 160 | |
| 161 | // ── The reference-insert: the thing that makes it a picker ────────────── |
| 162 | const inserted = await p.evaluate(async () => { |
| 163 | document.querySelector('.arte-row .arte-use').click(); |
| 164 | await new Promise(r => setTimeout(r, 300)); |
| 165 | const box = document.getElementById('chat-input'); |
| 166 | return box ? box.value : null; |
| 167 | }); |
| 168 | check('a row can put its reference in the steer box', |
| 169 | /notes\/|https:/.test(inserted || ''), inserted); |
| 170 | |
| 171 | // ── An empty Diamond gets no empty shelf ────────────────────────────────── |
| 172 | await p.evaluate(async () => { |
| 173 | const mod = await import('../pkg/oxedyne_daimond.js'); |
| 174 | const app = new mod.DaimondApp('http://127.0.0.1/v1/chat/completions', '', 'none', 256, '', true); |
| 175 | await app.create_diamond('Nothing done yet'); |
| 176 | }); |
| 177 | await p.reload({ waitUntil: 'domcontentloaded' }); |
| 178 | await signInAs(s, 'artefacts'); |
| 179 | await p.waitForTimeout(3000); |
| 180 | |
| 181 | // The strip USED TO HIDE at zero, and this pinned that. It cannot: the control |
| 182 | // that attaches the first thing lives inside the list the strip opens, so a |
| 183 | // strip that hid itself hid the only way in, in exactly the state it is for. |
| 184 | // What it must do now is read as an offer rather than as a count of nothing -- |
| 185 | // and still open onto that control. |
| 186 | const empty = await p.evaluate(async () => { |
| 187 | const row = Array.from(document.querySelectorAll('#diamond-list .diamond-box')) |
| 188 | .find(e => /Nothing done yet/.test(e.textContent)); |
| 189 | if (!row) return { row: 'no such row' }; |
| 190 | row.click(); |
| 191 | await new Promise(r => setTimeout(r, 1200)); |
| 192 | const el = document.getElementById('arte-strip'); |
| 193 | if (!el) return { row: 'ok', strip: 'absent' }; |
| 194 | const shown = getComputedStyle(el).display !== 'none'; |
| 195 | el.click(); |
| 196 | await new Promise(r => setTimeout(r, 700)); |
| 197 | return { |
| 198 | row: 'ok', |
| 199 | shown: shown, |
| 200 | text: el.textContent.trim(), |
| 201 | add: !!document.querySelector('#arte-list [data-act="attach-add"]'), |
| 202 | }; |
| 203 | }); |
| 204 | check('a Diamond with no artefacts still offers the strip, saying it holds nothing', |
| 205 | empty.shown === true && /\S/.test(empty.text || '') && !/\d/.test(empty.text || ''), |
| 206 | JSON.stringify(empty)); |
| 207 | check('and opening it reaches the control that attaches the first thing', |
| 208 | empty.add === true, JSON.stringify(empty)); |
| 209 | |
| 210 | await s.close(); |
| 211 | |
| 212 | console.log(`\n${ok.length} passed, ${bad.length} failed`); |
| 213 | if (bad.length) console.log('FAILED:\n ' + bad.join('\n ')); |
| 214 | process.exit(bad.length ? 1 : 0); |