Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/verify_writeplace.mjs

13.4 KiB, 1 run

created by r2519314175:833, 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_writeplace.mjs — a write that landed in browser storage never reads as a write to disk.
2//
3// THE DEFECT, AND IT IS THE WORST SHAPE THE OPFS/DISK SPLIT TAKES. Daimond has two
4// filesystems: the browser's own storage, which the file tools reach, and the folder on
5// this computer, which only `run` reaches. `two_places_note` (src/tools.rs, lane H,
6// 2026-08-24) taught seven tools to say which one they had just answered about, and
7// `absent_here` wired them up. `file_write` was left out, because the note works by
8// asking whether the path EXISTS and a write to a path that does not exist is the
9// ordinary case.
10//
11// What was left is not a missing sentence. `opfs::descend` calls `set_create(true)` on
12// every directory component it walks, so a write into a folder browser storage does not
13// hold MAKES THE FOLDER instead of failing. A daimon that has just found `src/tools.rs`
14// with a `grep` on the machine, and writes to it with `file_write`, is answered
15//
16// Wrote 812 bytes to src/tools.rs.
17//
18// with outcome `done`. A folder named `src` now exists in browser storage that nobody
19// asked for, holding a file the daimon believes is on disk. And the deceit compounds:
20// `file_read` of that path now SUCCEEDS, `file_list` of that folder lists the file, and
21// `absent_here` answers nothing anywhere, because the entry is present. Every check the
22// daimon can afterwards make agrees that the work landed. Nothing on the machine changed.
23//
24// That is why a sentence alone was judged insufficient here and is not what this pins. A
25// note on the answer is one line contradicted by every later signal; the write itself has
26// to not happen.
27//
28// SEVEN PROPERTIES:
29//
30// 0. NO HAND, NO NOISE. With no machine hand paired there is one filesystem and nothing
31// to disambiguate, so an ordinary write answers exactly as it always did. Without
32// this, a build that refused or annotated every write would pass everything below.
33// 1. A WRITE THAT CANNOT REACH THE MACHINE DOES NOT PRETEND TO. Hand paired, no folder
34// open, and the write would have to invent a folder to land: it is REFUSED, and —
35// the load-bearing half — NOTHING IS CREATED. Not the file, not the folder.
36// 2. THE REFUSAL IS A ROUTE. It names `run` for the machine, `dir_create` for browser
37// storage, and opening the folder for making the two one place.
38// 3. AND THE ROUTE IS ONE CALL. `dir_create` then the same write lands. A daimon that
39// really meant browser storage is delayed by one call, not stopped.
40// 4. A WRITE THAT DOES LAND SAYS WHERE IT LANDED. The folder is already there, so
41// nothing is invented and nothing is refused — but the answer still names the
42// filesystem, because a daimon reading `Wrote 812 bytes` has been told nothing.
43// 5. AND ITS FIRST LINE IS UNCHANGED. `dev/verify_replylen.mjs` reads `Wrote (\d+)
44// bytes to <file>` out of a tool answer. The note goes on a SECOND line, for the
45// reason `dev/verify_refusedpath.mjs` check 1c records: a reader that stops
46// recognising an answer does not read it as unknown, it reads it as something else.
47// 6. A DIAMOND'S OWN STORAGE IS NOT SECOND-GUESSED. `diamonds/`, `chats/` and `mail/`
48// ARE browser storage by design — the system prompt says so and the page's own mail
49// writer creates folders under `mail/` on every refresh. Refusing those would break
50// the product to fix a different fault.
51//
52// THE RED, AND HOW IT WAS TAKEN. The breaks here are in Rust, so they are not `--break`
53// flags over `www/js/daimond.js` the way `dev/verify_refusedpath.mjs`'s are: the engine
54// has to be rebuilt, which is two minutes. The world before the fix is the whole of it —
55// `Tool::FileWrite` with no `write_place` call at all — and this file was written and run
56// against that bundle first. What it printed is in the commit message. Reproduce it by
57// deleting the two lines in the `Tool::FileWrite` arm that read
58//
59// let place = res!(write_place(ctx, &raw, &path).await);
60// if let WritePlace::Inventing(dir) = &place {
61//
62// with their bodies, and the `place_line` that follows, then `bash dev/build-wasm.sh`.
63// Checks 1, 2 and 4 go red together; 1's second half is the one to watch, because it is
64// the only one that says the bytes went somewhere.
65//
66// eval "$(bash dev/world.sh 6 --up)"
67// node dev/verify_writeplace.mjs
68//
69// Needs dev/serve.mjs. No mock and no gateway: every call here goes straight to the real
70// registry through `run_tool_outcome`, which is the door `dev/CONTRACT_OUTCOME.md` §1
71// defines and the only one that answers what became of a call.
72import fs from 'node:fs';
73import path from 'node:path';
74import { fileURLToPath } from 'node:url';
75import { open, scratch } from './harness.mjs';
76
77const HERE = path.dirname(fileURLToPath(import.meta.url));
78
79let bad = 0;
80const check = (pass, name, detail) => {
81 if (!pass) bad++;
82 console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : ''));
83};
84
85const PROFILE = scratch('pw', 'writeplace');
86fs.rmSync(PROFILE, { recursive: true, force: true });
87
88const s = await open({ name: 'writeplace', profile: PROFILE, connect: false });
89const { page: p } = s;
90
91/// One call through the real registry, with the outcome the engine gave it.
92const call = (name, args) => p.evaluate(async (a) => {
93 const mod = await import('../pkg/oxedyne_daimond.js');
94 const app = new mod.DaimondApp('http://127.0.0.1/v1/chat/completions', '', 'none', 256, '', true);
95 const r = await app.run_tool_outcome(a.name, JSON.stringify(a.args));
96 return { outcome: (r && r.outcome) || 'none', text: String((r && r.text) || '') };
97}, { name, args });
98
99/// A machine hand, or the page's own relay saying there is none.
100///
101/// `hand.js` installs `window.DaimondHand` on EVERY page — it is the shim that answers "no
102/// hand is installed" — so the unpaired world is the page LEFT ALONE, and `hasHand()` is
103/// what tells the two apart. A REAL pairing needs the extension and a native host, neither
104/// of which an automated browser can be given, so the paired world is a stub that answers
105/// that one question the way a paired relay does. What is under test is what `file_write`
106/// does when a second filesystem exists, not how it came to exist.
107const hand = (on) => p.evaluate((v) => {
108 if (v) {
109 window.__realHand = window.__realHand || window.DaimondHand;
110 window.DaimondHand = { hasHand: () => true, status: async () => '{}', run: async () => '{}' };
111 } else if (window.__realHand) {
112 window.DaimondHand = window.__realHand;
113 }
114 return !!(window.DaimondHand && window.DaimondHand.hasHand && window.DaimondHand.hasHand());
115}, on);
116
117/// What browser storage actually holds, asked of the browser and not of the tool that
118/// was just refused. A tool that lied about writing would also lie about reading.
119const held = (dir, file) => p.evaluate(async (a) => {
120 const root = await DaimondCloud.opfsRoot();
121 const walk = async (parts) => {
122 let d = root;
123 for (const seg of parts) { d = await d.getDirectoryHandle(seg); }
124 return d;
125 };
126 const out = { dir: false, file: false };
127 try { await walk(a.dir.split('/').filter(Boolean)); out.dir = true; } catch (e) { /* absent */ }
128 try {
129 const parts = a.file.split('/').filter(Boolean);
130 const d = await walk(parts.slice(0, -1));
131 await d.getFileHandle(parts[parts.length - 1]);
132 out.file = true;
133 } catch (e) { /* absent */ }
134 return out;
135}, { dir, file });
136
137const firstLine = (t) => String(t).split('\n')[0];
138
139try {
140 await p.waitForFunction(() => !!(window.DaimondCloud && window.DaimondCloud.opfsRoot),
141 null, { timeout: 20000 });
142
143 // ── 0. The control: one filesystem, and the answer it always gave ───────────
144 //
145 // Not a formality. Every check below reads a sentence the engine adds; a build that
146 // added it unconditionally would satisfy all of them and would have made the ordinary
147 // single-filesystem answer worse for every user who has no hand installed.
148 // The relay is the page's own, untouched, which is the state of every browser that has
149 // never paired one. `hand::present()` asked `relay().is_ok()` until 2026-08-24 and got
150 // TRUE here, because the object it looked for is the shim that says "no hand installed" —
151 // so the two-filesystems note was being told to users who had one filesystem. This check
152 // is red against that build, and it is the reason `present()` now asks `hasHand()`.
153 const none = await hand(false);
154 check(none === false, 'THE PAGE\'S OWN RELAY IS NOT A PAIRED HAND', `hasHand()=${none}`);
155 const plain = await call('file_write', { path: 'wp0/a/b.txt', content: 'plain' });
156 check(plain.outcome === 'done' && /^Wrote \d+ bytes to wp0\/a\/b\.txt\.$/.test(plain.text.trim()),
157 'NO HAND, NO NOISE — with one filesystem the answer is the one it always was',
158 JSON.stringify(plain.text.slice(0, 120)));
159
160 // ── 1. The silence, ended ───────────────────────────────────────────────────
161 //
162 // `wpm` is a folder browser storage does not hold. Before this change the write
163 // invented it and answered `Wrote 6 bytes to wpm/deep/thing.rs.`, outcome `done`.
164 const on = await hand(true);
165 check(on === true, 'THE FIXTURE STANDS: a hand is present and no folder is open', String(on));
166 const meant = await call('file_write', { path: 'wpm/deep/thing.rs', content: 'fn main(){}' });
167 const after = await held('wpm', 'wpm/deep/thing.rs');
168 check(meant.outcome === 'refused',
169 'A WRITE THAT CANNOT REACH THE MACHINE DOES NOT REPORT SUCCESS',
170 `${meant.outcome}: ${JSON.stringify(meant.text.slice(0, 150))}`);
171 check(after.dir === false && after.file === false,
172 'AND NOTHING WAS CREATED — no invented folder, no bytes, so every later read '
173 + 'tells the truth too',
174 `folder wpm ${after.dir ? 'EXISTS' : 'absent'}, file ${after.file ? 'EXISTS' : 'absent'}`);
175
176 // ── 2. A refusal that is a route ────────────────────────────────────────────
177 //
178 // Three ways out, because there are three different things the daimon may have meant,
179 // and a refusal that names none of them is a wall. `run` is the machine; `dir_create`
180 // is browser storage, said on purpose; opening the folder is how the user makes the
181 // two one place and stops this happening at all.
182 const says = String(meant.text);
183 check(/\brun\b/.test(says), 'THE REFUSAL NAMES run, for the machine', '');
184 check(/\bdir_create\b/.test(says), 'and dir_create, for browser storage said on purpose', '');
185 check(/open the folder/.test(says), 'and opening the folder, which makes the two one place', '');
186 check(/the file tools reach while no folder is open/.test(says),
187 'and it carries the clause both file tools share, which dev/reflux.mjs keys opfsSplit on',
188 '');
189
190 // ── 3. And the route is one call ────────────────────────────────────────────
191 const made = await call('dir_create', { path: 'wpm/deep' });
192 const again = await call('file_write', { path: 'wpm/deep/thing.rs', content: 'fn main(){}' });
193 const now = await held('wpm', 'wpm/deep/thing.rs');
194 check(made.outcome === 'done' && again.outcome === 'done' && now.file === true,
195 'THE ROUTE IS ONE CALL — dir_create, then the same write lands',
196 `${made.outcome} / ${again.outcome} / file ${now.file ? 'present' : 'ABSENT'}`);
197
198 // ── 4. A write that lands says where ────────────────────────────────────────
199 //
200 // Nothing is invented now, so nothing is refused — and this is the case a note alone
201 // was always going to have to cover, because a daimon overwriting a file it has
202 // already read gets no refusal and still has to know which disk it is on.
203 const landed = await call('file_write', { path: 'wpm/deep/second.rs', content: 'fn two(){}' });
204 check(landed.outcome === 'done'
205 && /this browser's own storage/.test(landed.text)
206 && /the file tools reach while no folder is open/.test(landed.text),
207 'A WRITE THAT LANDS NAMES THE FILESYSTEM IT LANDED IN',
208 JSON.stringify(landed.text.slice(0, 200)));
209
210 // ── 5. And its first line is unchanged ──────────────────────────────────────
211 check(/^Wrote \d+ bytes to wpm\/deep\/second\.rs\.$/.test(firstLine(landed.text).trim()),
212 'THE FIRST LINE IS UNCHANGED, so dev/verify_replylen.mjs still reads the byte count',
213 JSON.stringify(firstLine(landed.text)));
214
215 // ── 6. A Diamond's own storage is not second-guessed ────────────────────────
216 //
217 // `mail/` is browser storage by design and the page's own mailbox writer creates
218 // folders under it unasked (`www/js/mail.js`). A rule that refused a store path would
219 // have broken mail to fix a fault mail does not have.
220 const store = await call('file_write',
221 { path: 'mail/someone@example.com/INBOX/index.md', content: '| UID |\n' });
222 check(store.outcome === 'done' && !/Refused/.test(store.text),
223 'A STORE PATH IS BROWSER STORAGE BY DESIGN — mail/ writes through, folders and all',
224 `${store.outcome}: ${JSON.stringify(store.text.slice(0, 90))}`);
225 const dia = await call('file_write',
226 { path: 'diamonds/wp-test/notes/n.md', content: 'note\n' });
227 check(dia.outcome === 'done' && !/Refused/.test(dia.text),
228 'and so is a Diamond\'s own directory', `${dia.outcome}`);
229} finally {
230 await s.close();
231}
232
233console.log(bad ? `\n${bad} FAILED` : '\nall checks passed');
234process.exit(bad ? 1 : 0);