Oregami
Repositories/oxedyne/daimond

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.
15import { open, signInAs } from './harness.mjs';
16
17const ok = [], bad = [];
18const check = (name, pass, detail) => {
19 (pass ? ok : bad).push(name + (detail ? ' — ' + detail : ''));
20 console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : ''));
21};
22
23const s = await open({ name: 'artefacts', connect: false });
24const p = s.page;
25await p.waitForTimeout(2500);
26
27// ── A Diamond, and a chat whose turns carry tool calls ─────────────────────
28const 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});
33check('a Diamond was created', !!diamondId, diamondId);
34
35// The harvest reads `tool_log` messages, so this is exactly the shape a real turn leaves.
36const 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
56const refs = harvested.map(l => l.to);
57check('a written file became an artefact', refs.includes('file:notes/pricing.md'));
58check('an edited file became one too', refs.includes('file:notes/launch-copy.md'));
59check('an opened page became one', refs.includes('url:https://stripe.com/docs?a=1'));
60check('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');
63check('a background fetch did not', !refs.includes('url:https://example.com/background'));
64check('the same file written twice is one artefact',
65 refs.filter(r => r === 'file:notes/pricing.md').length === 1);
66check('a produced file says so', harvested.find(l => l.to === 'file:notes/pricing.md')?.rel === 'produced');
67check('an opened page says consulted',
68 harvested.find(l => l.to.startsWith('url:'))?.rel === 'consulted');
69check('the harvest names itself as the source',
70 harvested.every(l => l.by === 'fold'), harvested[0]?.by);
71check('exactly the three artefacts, and nothing else', harvested.length === 3,
72 `${harvested.length}: ${refs.join(', ')}`);
73
74// ── Re-folding must not stack duplicates ────────────────────────────────
75const 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 });
86check('folding again does not stack a duplicate', again.length === 4,
87 `${again.length} artefact(s)`);
88check('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 ─────────────────────
92const 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 });
106check('a malformed tool call does not throw inside an accepted fold', !malformed.threw,
107 malformed.threw);
108check('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.
115await p.reload({ waitUntil: 'domcontentloaded' });
116await signInAs(s, 'artefacts'); // a reload puts the identity gate back
117await p.waitForTimeout(3000);
118
119const 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 });
133check('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.
138const wantCount = await p.evaluate(() => {
139 try { return '◈ ' + window.DaimondI18n.tn('dws.count', 5); } catch (e) { return null; }
140});
141check('it is a count, not a list', strip.text === wantCount, `${strip.text} vs ${wantCount}`);
142check('the list stays closed until it is clicked', strip.listShown === false,
143 String(strip.listShown));
144
145const 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});
156check('clicking opens the list', opened.shown === true);
157check('every artefact has a row', opened.rows === 5, `${opened.rows} row(s)`);
158check('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 ──────────────
162const 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});
168check('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 ──────────────────────────────────
172await 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});
177await p.reload({ waitUntil: 'domcontentloaded' });
178await signInAs(s, 'artefacts');
179await 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.
186const 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});
204check('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));
207check('and opening it reaches the control that attaches the first thing',
208 empty.add === true, JSON.stringify(empty));
209
210await s.close();
211
212console.log(`\n${ok.length} passed, ${bad.length} failed`);
213if (bad.length) console.log('FAILED:\n ' + bad.join('\n '));
214process.exit(bad.length ? 1 : 0);