amalgm 0.1.261 → 0.1.262

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cli.js CHANGED
@@ -1215,10 +1215,16 @@ function selectEntity(status, selector) {
1215
1215
  const uuid = selector && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(selector)
1216
1216
  ? selector.toLowerCase()
1217
1217
  : null;
1218
+ // A UUID is an entity's globally unique address. Do not let rows without
1219
+ // an optional display path turn that exact address into a fuzzy selector.
1220
+ if (uuid) {
1221
+ const match = entities.find((entity) => entity.uuid === uuid);
1222
+ if (!match) throw new Error(`No cloud-backed entity matches ${selector}.`);
1223
+ return match;
1224
+ }
1218
1225
  const localPath = selector && !uuid ? path.resolve(selector) : null;
1219
- const cloudPath = uuid ? null : String(selector).replace(/^\/+|\/+$/g, '');
1220
- const matches = entities.filter((entity) => entity.uuid === uuid
1221
- || entity.localPath === localPath
1226
+ const cloudPath = String(selector).replace(/^\/+|\/+$/g, '');
1227
+ const matches = entities.filter((entity) => entity.localPath === localPath
1222
1228
  || entity.cloudPath === cloudPath
1223
1229
  || entity.name === selector);
1224
1230
  if (matches.length === 0) throw new Error(`No cloud-backed entity matches ${selector}.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amalgm",
3
- "version": "0.1.261",
3
+ "version": "0.1.262",
4
4
  "description": "Amalgm local computer runtime: login, MCP, chat, events, previews, and tunnels.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Memoized statement preparation for one database handle.
5
+ *
6
+ * better-sqlite3 compiles the SQL text on EVERY `db.prepare()` call — it
7
+ * keeps no statement cache of its own. The stores' row-level verbs run
8
+ * thousands of times per scan (a first census inserts one row per file),
9
+ * so recompiling identical SQL each call was a measured double-digit
10
+ * share of a large registration. Each store builds one preparer over its
11
+ * injected handle and asks it instead; the SQL string is the whole key,
12
+ * so a handful of table-templated queries memoize to a handful of
13
+ * statements, never an unbounded set.
14
+ */
15
+ function preparer(db) {
16
+ const statements = new Map();
17
+ return (sql) => {
18
+ let statement = statements.get(sql);
19
+ if (!statement) {
20
+ statement = db.prepare(sql);
21
+ statements.set(sql, statement);
22
+ }
23
+ return statement;
24
+ };
25
+ }
26
+
27
+ module.exports = { preparer };
@@ -407,8 +407,10 @@ function createObserver(options) {
407
407
  || linkDiff.renames.length || linkDiff.creates.length || linkDiff.deletes.length
408
408
  || fileDiff.renames.length || fileDiff.creates.length || fileDiff.deletes.length;
409
409
  if (structural) return false;
410
- applyLinkDiff(root, linkDiff); // changes and retags: payload-local facts
411
- applyDiff(root, fileDiff); // checks only: content truth on existing channels
410
+ store.transaction(() => {
411
+ applyLinkDiff(root, linkDiff); // changes and retags: payload-local facts
412
+ applyDiff(root, fileDiff); // checks only: content truth on existing channels
413
+ });
412
414
  return true;
413
415
  }
414
416
 
@@ -448,11 +450,17 @@ function createObserver(options) {
448
450
  : { ...diff, deletes: diff.deletes.filter((row) => !underUnsettled(row.relPath)) });
449
451
  if (unsettled.length > 0 && state) armRetry(root, state);
450
452
 
451
- applyDirDiff(root, concluded(diffDirState(store.dirsForRoot(root.rootId), dirs)));
452
- applyLinkDiff(root, concluded(diffLinkState(store.linksForRoot(root.rootId), links)));
453
- const rows = store.filesForRoot(root.rootId);
454
- const mustCheck = checkAll ? new Set(entries.map((entry) => entry.relPath)) : rung;
455
- applyDiff(root, concluded(diffRootState(rows, entries, mustCheck)));
453
+ // One walk's conclusions are one durable transition: a census over
454
+ // thousands of channels commits once, not once per row. Events stay
455
+ // hints — a consumer told early about a rolled-back row re-reads
456
+ // store truth and finds the old state, which concludes nothing.
457
+ store.transaction(() => {
458
+ applyDirDiff(root, concluded(diffDirState(store.dirsForRoot(root.rootId), dirs)));
459
+ applyLinkDiff(root, concluded(diffLinkState(store.linksForRoot(root.rootId), links)));
460
+ const rows = store.filesForRoot(root.rootId);
461
+ const mustCheck = checkAll ? new Set(entries.map((entry) => entry.relPath)) : rung;
462
+ applyDiff(root, concluded(diffRootState(rows, entries, mustCheck)));
463
+ });
456
464
  }
457
465
 
458
466
  /**
@@ -15,6 +15,8 @@
15
15
  * is durable, or its loss is explicit.
16
16
  */
17
17
 
18
+ const { preparer } = require('../lib/prepared');
19
+
18
20
  const SCHEMA = `
19
21
  CREATE TABLE IF NOT EXISTS observer_roots (
20
22
  root_id TEXT PRIMARY KEY,
@@ -141,6 +143,8 @@ function createStore(db) {
141
143
  db.exec(SCHEMA);
142
144
  db.pragma(`user_version = ${GENERATION}`);
143
145
 
146
+ const prepared = preparer(db);
147
+
144
148
  /**
145
149
  * One directory's immediate child rows — the scoped scan's slice of
146
150
  * memory, served by the (root_id, rel_path) unique index. '0' is the
@@ -150,33 +154,43 @@ function createStore(db) {
150
154
  */
151
155
  function immediateRows(table, rootId, relDir) {
152
156
  if (relDir === '') {
153
- return db.prepare(`SELECT * FROM ${table} WHERE root_id = ? AND instr(rel_path, '/') = 0 ORDER BY rel_path`)
157
+ return prepared(`SELECT * FROM ${table} WHERE root_id = ? AND instr(rel_path, '/') = 0 ORDER BY rel_path`)
154
158
  .all(rootId);
155
159
  }
156
160
  const prefix = `${relDir}/`;
157
- return db.prepare(`SELECT * FROM ${table} WHERE root_id = ? AND rel_path >= ? AND rel_path < ? ORDER BY rel_path`)
161
+ return prepared(`SELECT * FROM ${table} WHERE root_id = ? AND rel_path >= ? AND rel_path < ? ORDER BY rel_path`)
158
162
  .all(rootId, prefix, `${relDir}0`)
159
163
  .filter((row) => !row.rel_path.slice(prefix.length).includes('/'));
160
164
  }
161
165
 
162
166
  return {
167
+ /**
168
+ * One scan's whole apply phase commits as one durable transition.
169
+ * Row-level verbs each pay a full journal cycle when run bare, so a
170
+ * census over thousands of channels was thousands of commits; batched,
171
+ * it is one. Nesting is safe — SQLite savepoints — and a crash
172
+ * mid-batch leaves the previous truth whole, exactly like applyDirDiff.
173
+ */
174
+ transaction(fn) {
175
+ return db.transaction(fn)();
176
+ },
163
177
  addRoot({ rootId, path, kind, device = null, inode = null }) {
164
- db.prepare('INSERT INTO observer_roots (root_id, path, kind, device, inode) VALUES (?, ?, ?, ?, ?)')
178
+ prepared('INSERT INTO observer_roots (root_id, path, kind, device, inode) VALUES (?, ?, ?, ?, ?)')
165
179
  .run(rootId, path, kind, device, inode);
166
180
  },
167
181
  rootByPath(path) {
168
- const row = db.prepare('SELECT * FROM observer_roots WHERE path = ?').get(path);
182
+ const row = prepared('SELECT * FROM observer_roots WHERE path = ?').get(path);
169
183
  return row ? rowToRoot(row) : null;
170
184
  },
171
185
  listRoots() {
172
- return db.prepare('SELECT * FROM observer_roots ORDER BY path').all().map(rowToRoot);
186
+ return prepared('SELECT * FROM observer_roots ORDER BY path').all().map(rowToRoot);
173
187
  },
174
188
  setRootKind(rootId, kind) {
175
- db.prepare('UPDATE observer_roots SET kind = ? WHERE root_id = ?').run(kind, rootId);
189
+ prepared('UPDATE observer_roots SET kind = ? WHERE root_id = ?').run(kind, rootId);
176
190
  },
177
191
  /** Identity persists; local presence is separate evidence. */
178
192
  setRootPresent(rootId, present) {
179
- db.prepare('UPDATE observer_roots SET present = ? WHERE root_id = ?').run(present ? 1 : 0, rootId);
193
+ prepared('UPDATE observer_roots SET present = ? WHERE root_id = ?').run(present ? 1 : 0, rootId);
180
194
  },
181
195
  /**
182
196
  * A root's identity is permanent; its address is not — and a family
@@ -186,7 +200,7 @@ function createStore(db) {
186
200
  * Device and inode are continuity itself and never churn on a rename.
187
201
  */
188
202
  moveRoots(moves) {
189
- const move = db.prepare('UPDATE observer_roots SET path = ? WHERE root_id = ?');
203
+ const move = prepared('UPDATE observer_roots SET path = ? WHERE root_id = ?');
190
204
  db.transaction(() => {
191
205
  for (const { rootId, path } of moves) move.run(path, rootId);
192
206
  })();
@@ -195,18 +209,18 @@ function createStore(db) {
195
209
  * new identity at a known address (see enrollRoot) — the one write
196
210
  * where a root's own device:inode may change. */
197
211
  setRootIdentity(rootId, device, inode) {
198
- db.prepare('UPDATE observer_roots SET device = ?, inode = ? WHERE root_id = ?')
212
+ prepared('UPDATE observer_roots SET device = ?, inode = ? WHERE root_id = ?')
199
213
  .run(device, inode, rootId);
200
214
  },
201
215
  removeRoot(rootId) {
202
- db.prepare('DELETE FROM observer_files WHERE root_id = ?').run(rootId);
203
- db.prepare('DELETE FROM observer_dirs WHERE root_id = ?').run(rootId);
204
- db.prepare('DELETE FROM observer_links WHERE root_id = ?').run(rootId);
205
- db.prepare('DELETE FROM observer_roots WHERE root_id = ?').run(rootId);
216
+ prepared('DELETE FROM observer_files WHERE root_id = ?').run(rootId);
217
+ prepared('DELETE FROM observer_dirs WHERE root_id = ?').run(rootId);
218
+ prepared('DELETE FROM observer_links WHERE root_id = ?').run(rootId);
219
+ prepared('DELETE FROM observer_roots WHERE root_id = ?').run(rootId);
206
220
  },
207
221
 
208
222
  filesForRoot(rootId) {
209
- return db.prepare('SELECT * FROM observer_files WHERE root_id = ? ORDER BY rel_path').all(rootId)
223
+ return prepared('SELECT * FROM observer_files WHERE root_id = ? ORDER BY rel_path').all(rootId)
210
224
  .map(rowToFile);
211
225
  },
212
226
  filesInDir(rootId, relDir) {
@@ -220,11 +234,11 @@ function createStore(db) {
220
234
  },
221
235
  fileByPath(rootId, relPath) {
222
236
  return rowToFile(
223
- db.prepare('SELECT * FROM observer_files WHERE root_id = ? AND rel_path = ?').get(rootId, relPath),
237
+ prepared('SELECT * FROM observer_files WHERE root_id = ? AND rel_path = ?').get(rootId, relPath),
224
238
  );
225
239
  },
226
240
  insertFile(file) {
227
- db.prepare(`
241
+ prepared(`
228
242
  INSERT INTO observer_files (file_id, root_id, rel_path, device, inode, size, mtime_ms, content_hash, is_binary, hashed_at_ms)
229
243
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
230
244
  `).run(
@@ -233,18 +247,18 @@ function createStore(db) {
233
247
  );
234
248
  },
235
249
  updateFile(fileId, { relPath, device, inode, size, mtimeMs, contentHash, binary, hashedAtMs }) {
236
- db.prepare(`
250
+ prepared(`
237
251
  UPDATE observer_files
238
252
  SET rel_path = ?, device = ?, inode = ?, size = ?, mtime_ms = ?, content_hash = ?, is_binary = ?, hashed_at_ms = ?
239
253
  WHERE file_id = ?
240
254
  `).run(relPath, device, inode, size, mtimeMs, contentHash, binary ? 1 : 0, hashedAtMs, fileId);
241
255
  },
242
256
  removeFile(fileId) {
243
- db.prepare('DELETE FROM observer_files WHERE file_id = ?').run(fileId);
257
+ prepared('DELETE FROM observer_files WHERE file_id = ?').run(fileId);
244
258
  },
245
259
 
246
260
  dirsForRoot(rootId) {
247
- return db.prepare('SELECT * FROM observer_dirs WHERE root_id = ? ORDER BY rel_path').all(rootId)
261
+ return prepared('SELECT * FROM observer_dirs WHERE root_id = ? ORDER BY rel_path').all(rootId)
248
262
  .map(rowToDir);
249
263
  },
250
264
  /**
@@ -257,9 +271,9 @@ function createStore(db) {
257
271
  * the next scan re-detects the same batch.
258
272
  */
259
273
  applyDirDiff({ deletes, moves, retags, creates }) {
260
- const remove = db.prepare('DELETE FROM observer_dirs WHERE dir_id = ?');
261
- const insert = db.prepare('INSERT INTO observer_dirs (dir_id, root_id, rel_path, device, inode) VALUES (?, ?, ?, ?, ?)');
262
- const retag = db.prepare('UPDATE observer_dirs SET rel_path = ?, device = ?, inode = ? WHERE dir_id = ?');
274
+ const remove = prepared('DELETE FROM observer_dirs WHERE dir_id = ?');
275
+ const insert = prepared('INSERT INTO observer_dirs (dir_id, root_id, rel_path, device, inode) VALUES (?, ?, ?, ?, ?)');
276
+ const retag = prepared('UPDATE observer_dirs SET rel_path = ?, device = ?, inode = ? WHERE dir_id = ?');
263
277
  db.transaction(() => {
264
278
  for (const dirId of deletes) remove.run(dirId);
265
279
  for (const dir of moves) remove.run(dir.dirId);
@@ -269,7 +283,7 @@ function createStore(db) {
269
283
  })();
270
284
  },
271
285
  removeDir(dirId) {
272
- db.prepare('DELETE FROM observer_dirs WHERE dir_id = ?').run(dirId);
286
+ prepared('DELETE FROM observer_dirs WHERE dir_id = ?').run(dirId);
273
287
  },
274
288
 
275
289
  // Link channels: a pointer's whole payload is its target string, read
@@ -277,19 +291,19 @@ function createStore(db) {
277
291
  // a rename only ever targets a row-free slot), so link rows commit
278
292
  // per-row like files — every commit is a valid last-known truth.
279
293
  linksForRoot(rootId) {
280
- return db.prepare('SELECT * FROM observer_links WHERE root_id = ? ORDER BY rel_path').all(rootId)
294
+ return prepared('SELECT * FROM observer_links WHERE root_id = ? ORDER BY rel_path').all(rootId)
281
295
  .map(rowToLink);
282
296
  },
283
297
  insertLink({ linkId, rootId, relPath, device, inode, target }) {
284
- db.prepare('INSERT INTO observer_links (link_id, root_id, rel_path, device, inode, target) VALUES (?, ?, ?, ?, ?, ?)')
298
+ prepared('INSERT INTO observer_links (link_id, root_id, rel_path, device, inode, target) VALUES (?, ?, ?, ?, ?, ?)')
285
299
  .run(linkId, rootId, relPath, device, inode, target);
286
300
  },
287
301
  updateLink(linkId, { relPath, device, inode, target }) {
288
- db.prepare('UPDATE observer_links SET rel_path = ?, device = ?, inode = ?, target = ? WHERE link_id = ?')
302
+ prepared('UPDATE observer_links SET rel_path = ?, device = ?, inode = ?, target = ? WHERE link_id = ?')
289
303
  .run(relPath, device, inode, target, linkId);
290
304
  },
291
305
  removeLink(linkId) {
292
- db.prepare('DELETE FROM observer_links WHERE link_id = ?').run(linkId);
306
+ prepared('DELETE FROM observer_links WHERE link_id = ?').run(linkId);
293
307
  },
294
308
  };
295
309
  }
@@ -86,6 +86,17 @@ function createRegistrationService({
86
86
 
87
87
  const evidenceDb = new Database(path.join(dir, 'observer.db'));
88
88
  const identityDb = new Database(path.join(dir, 'identity.db'));
89
+ // WAL, decided here because this root OWNS the handles: SQLite's default
90
+ // journal mode pays a create-fsync-delete journal cycle on every commit,
91
+ // which made a large registration ~65% commit ceremony. WAL appends to
92
+ // one log instead, and synchronous=NORMAL keeps every commit durable
93
+ // against process crash (an OS crash can lose the tail, never corrupt —
94
+ // detection re-derives evidence, and identity commits are re-walked by
95
+ // the registration that was still running).
96
+ for (const handle of [evidenceDb, identityDb]) {
97
+ handle.pragma('journal_mode = WAL');
98
+ handle.pragma('synchronous = NORMAL');
99
+ }
89
100
  const entityContent = createEntityContent({ dir });
90
101
  // createRegistration owns the schema migration. Give entity-cloud a stable
91
102
  // lookup facade now, then connect it to that one migrated state store once
@@ -873,7 +884,13 @@ function createRegistrationService({
873
884
  /** `amalgm register <path>` — register, then begin Watch + Detect. */
874
885
  register: async (targetPath) => {
875
886
  entityCloud.assertRegistrationReady();
876
- if (estimatedRegistrationRecords(targetPath) < ATOMIC_REGISTRATION_THRESHOLD) {
887
+ // No active cloud replica means no cloud is listening — the journal
888
+ // itself already no-ops there, so a large tree takes the same plain
889
+ // local registration a small one does instead of demanding an atomic
890
+ // replacement that has nowhere to land. Size never changes whether a
891
+ // signed-out registration is allowed.
892
+ const cloudActive = entityCloud.replica()?.state === 'active';
893
+ if (!cloudActive || estimatedRegistrationRecords(targetPath) < ATOMIC_REGISTRATION_THRESHOLD) {
877
894
  return boundary.current.add(targetPath);
878
895
  }
879
896
  const result = boundary.current.registry.withoutJournal(() => boundary.current.add(targetPath));
@@ -886,9 +903,10 @@ function createRegistrationService({
886
903
  * materialize. Small selections retain their ordinary per-entity intent. */
887
904
  registerMany: async (targetPaths) => {
888
905
  entityCloud.assertRegistrationReady();
889
- const needsAtomicReplacement = targetPaths.some((targetPath) => (
890
- estimatedRegistrationRecords(targetPath) >= ATOMIC_REGISTRATION_THRESHOLD
891
- ));
906
+ const needsAtomicReplacement = entityCloud.replica()?.state === 'active'
907
+ && targetPaths.some((targetPath) => (
908
+ estimatedRegistrationRecords(targetPath) >= ATOMIC_REGISTRATION_THRESHOLD
909
+ ));
892
910
  if (!needsAtomicReplacement) return boundary.current.addMany(targetPaths);
893
911
  const results = boundary.current.registry.withoutJournal(() => boundary.current.addMany(targetPaths));
894
912
  entityCloud.replaceSnapshot(boundary.current.registry.syncRecords());
@@ -103,6 +103,8 @@
103
103
  const fs = require('fs');
104
104
  const path = require('path');
105
105
 
106
+ const { preparer } = require('../lib/prepared');
107
+
106
108
  const FILE_TYPES = new Set(['file.text', 'file.binary']);
107
109
  const LEAF_TYPES = new Set(['file.text', 'file.binary', 'link']);
108
110
  // Adoption candidates by ground shape: a dir channel can be an arrived
@@ -132,6 +134,7 @@ const MEMORY_TABLES = ['registry_meta', 'channel_bindings', 'contested_ground'];
132
134
  * Retiring a tree drops its whole scope — bindings and markers together.
133
135
  */
134
136
  function createEvidenceMemory(db) {
137
+ const prepared = preparer(db);
135
138
  const present = db.prepare(
136
139
  `SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${MEMORY_TABLES.map(() => '?').join(', ')})`,
137
140
  ).all(...MEMORY_TABLES).map((row) => row.name);
@@ -145,30 +148,30 @@ function createEvidenceMemory(db) {
145
148
  }
146
149
  return {
147
150
  epoch: () => meta.epoch_uuid,
148
- bindings: (scopeId) => db.prepare('SELECT channel_id, entity_uuid FROM channel_bindings WHERE scope_id = ?')
151
+ bindings: (scopeId) => prepared('SELECT channel_id, entity_uuid FROM channel_bindings WHERE scope_id = ?')
149
152
  .all(scopeId).map((row) => ({ channelId: row.channel_id, uuid: row.entity_uuid })),
150
- bind: (scopeId, channelId, uuid) => db.prepare(
153
+ bind: (scopeId, channelId, uuid) => prepared(
151
154
  'INSERT INTO channel_bindings (channel_id, scope_id, entity_uuid) VALUES (?, ?, ?)'
152
155
  + ' ON CONFLICT(channel_id) DO UPDATE SET scope_id = excluded.scope_id, entity_uuid = excluded.entity_uuid',
153
156
  ).run(channelId, scopeId, uuid),
154
- unbind: (channelId) => db.prepare('DELETE FROM channel_bindings WHERE channel_id = ?').run(channelId),
157
+ unbind: (channelId) => prepared('DELETE FROM channel_bindings WHERE channel_id = ?').run(channelId),
155
158
  /** Across scopes — channel ids are globally unique. */
156
- boundTo: (channelId) => db.prepare('SELECT entity_uuid FROM channel_bindings WHERE channel_id = ?')
159
+ boundTo: (channelId) => prepared('SELECT entity_uuid FROM channel_bindings WHERE channel_id = ?')
157
160
  .get(channelId)?.entity_uuid,
158
- allBindings: () => db.prepare('SELECT channel_id, scope_id, entity_uuid FROM channel_bindings').all()
161
+ allBindings: () => prepared('SELECT channel_id, scope_id, entity_uuid FROM channel_bindings').all()
159
162
  .map((row) => ({ channelId: row.channel_id, scopeId: row.scope_id, uuid: row.entity_uuid })),
160
163
  /** A retired tree's whole evidence namespace, gone as one act. */
161
164
  dropScope: (scopeId) => {
162
- db.prepare('DELETE FROM channel_bindings WHERE scope_id = ?').run(scopeId);
163
- db.prepare('DELETE FROM contested_ground WHERE scope_id = ?').run(scopeId);
165
+ prepared('DELETE FROM channel_bindings WHERE scope_id = ?').run(scopeId);
166
+ prepared('DELETE FROM contested_ground WHERE scope_id = ?').run(scopeId);
164
167
  },
165
- contests: (scopeId) => db.prepare('SELECT rel_path, claimant_uuid, kind FROM contested_ground WHERE scope_id = ?')
168
+ contests: (scopeId) => prepared('SELECT rel_path, claimant_uuid, kind FROM contested_ground WHERE scope_id = ?')
166
169
  .all(scopeId).map((row) => ({ relPath: row.rel_path, uuid: row.claimant_uuid, kind: row.kind })),
167
- contest: (scopeId, relPath, uuid, kind) => db.prepare(
170
+ contest: (scopeId, relPath, uuid, kind) => prepared(
168
171
  'INSERT INTO contested_ground (scope_id, rel_path, claimant_uuid, kind) VALUES (?, ?, ?, ?)'
169
172
  + ' ON CONFLICT(scope_id, rel_path) DO UPDATE SET claimant_uuid = excluded.claimant_uuid, kind = excluded.kind',
170
173
  ).run(scopeId, relPath, uuid, kind),
171
- clearContest: (scopeId, relPath) => db.prepare('DELETE FROM contested_ground WHERE scope_id = ? AND rel_path = ?')
174
+ clearContest: (scopeId, relPath) => prepared('DELETE FROM contested_ground WHERE scope_id = ? AND rel_path = ?')
172
175
  .run(scopeId, relPath),
173
176
  };
174
177
  }
@@ -359,6 +362,27 @@ function createEvidence(options) {
359
362
  function reconcile() {
360
363
  dirty = false; // an event landing mid-reconcile re-dirties honestly
361
364
  try {
365
+ // One reconciliation is ONE durable truth transition: a first census
366
+ // over thousands of channels lands as one commit, not one per record.
367
+ // The registry's own transaction defers every bell to the commit and
368
+ // discards them all on rollback, so a failed pass publishes nothing.
369
+ registry.transaction(pass);
370
+ } catch (error) {
371
+ // The transaction rolled every durable write back; the in-memory
372
+ // caches may still hold entries from the rolled-back half. Durable
373
+ // truth first, cache second — so the caches reload from the tables
374
+ // before the retry, never believing a write that no longer exists.
375
+ channelToUuid.clear();
376
+ for (const row of memory.bindings(rootId)) channelToUuid.set(row.channelId, row.uuid);
377
+ contested.clear();
378
+ for (const row of memory.contests(rootId)) contested.set(row.relPath, { uuid: row.uuid, kind: row.kind });
379
+ dirty = true;
380
+ throw error;
381
+ }
382
+ }
383
+
384
+ function pass() {
385
+ {
362
386
  const rootRow = observer.listRoots().find((row) => row.rootId === rootId);
363
387
  if (!rootRow) return; // not enrolled yet — nothing is claimed
364
388
  groundPath = rootRow.path; // the address is derived, never the lens's identity
@@ -771,9 +795,6 @@ function createEvidence(options) {
771
795
  : null);
772
796
  }
773
797
  for (const [relPath, hash] of nowDivergent) divergent.set(relPath, hash);
774
- } catch (error) {
775
- dirty = true;
776
- throw error;
777
798
  }
778
799
  }
779
800
 
@@ -26,6 +26,8 @@
26
26
  * (address), never by name, so two trees may share a basename.
27
27
  */
28
28
 
29
+ const { preparer } = require('../lib/prepared');
30
+
29
31
  const SCHEMA = `
30
32
  CREATE TABLE IF NOT EXISTS registry_meta (
31
33
  singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
@@ -246,56 +248,58 @@ function createStore(db) {
246
248
  db.exec(SCHEMA);
247
249
  db.pragma(`user_version = ${GENERATION}`);
248
250
 
251
+ const prepared = preparer(db);
252
+
249
253
  return {
250
254
  meta() {
251
- const row = db.prepare('SELECT * FROM registry_meta WHERE singleton = 1').get();
255
+ const row = prepared('SELECT * FROM registry_meta WHERE singleton = 1').get();
252
256
  return row ? { epochUuid: row.epoch_uuid, createdAtMs: row.created_at_ms } : null;
253
257
  },
254
258
  initMeta({ epochUuid, createdAtMs }) {
255
- db.prepare('INSERT INTO registry_meta (singleton, epoch_uuid, created_at_ms) VALUES (1, ?, ?)')
259
+ prepared('INSERT INTO registry_meta (singleton, epoch_uuid, created_at_ms) VALUES (1, ?, ?)')
256
260
  .run(epochUuid, createdAtMs);
257
261
  },
258
262
 
259
263
  byUuid(uuid) {
260
- return rowToEntity(db.prepare('SELECT * FROM entities WHERE uuid = ?').get(uuid));
264
+ return rowToEntity(prepared('SELECT * FROM entities WHERE uuid = ?').get(uuid));
261
265
  },
262
266
  /** Every registered tree's top record: the active parentless rows. */
263
267
  activeRoots() {
264
- return db.prepare("SELECT * FROM entities WHERE parent_uuid IS NULL AND status = 'active' ORDER BY uuid")
268
+ return prepared("SELECT * FROM entities WHERE parent_uuid IS NULL AND status = 'active' ORDER BY uuid")
265
269
  .all().map(rowToEntity);
266
270
  },
267
271
  activeAtSlot(parentUuid, name) {
268
272
  return rowToEntity(
269
- db.prepare("SELECT * FROM entities WHERE parent_uuid IS ? AND name = ? AND status = 'active'")
273
+ prepared("SELECT * FROM entities WHERE parent_uuid IS ? AND name = ? AND status = 'active'")
270
274
  .get(parentUuid, name),
271
275
  );
272
276
  },
273
277
  childrenOf(parentUuid) {
274
- return db.prepare('SELECT * FROM entities WHERE parent_uuid = ? ORDER BY uuid').all(parentUuid)
278
+ return prepared('SELECT * FROM entities WHERE parent_uuid = ? ORDER BY uuid').all(parentUuid)
275
279
  .map(rowToEntity);
276
280
  },
277
281
  activeChildrenOf(parentUuid) {
278
- return db.prepare("SELECT * FROM entities WHERE parent_uuid = ? AND status = 'active' ORDER BY uuid")
282
+ return prepared("SELECT * FROM entities WHERE parent_uuid = ? AND status = 'active' ORDER BY uuid")
279
283
  .all(parentUuid).map(rowToEntity);
280
284
  },
281
285
  activeByType(type) {
282
- return db.prepare("SELECT * FROM entities WHERE type = ? AND status = 'active' ORDER BY uuid")
286
+ return prepared("SELECT * FROM entities WHERE type = ? AND status = 'active' ORDER BY uuid")
283
287
  .all(type).map(rowToEntity);
284
288
  },
285
289
  /** Logical graph rows for the cloud registry. This deliberately excludes
286
290
  * observer bindings and every machine-local table. */
287
291
  allEntities() {
288
- return db.prepare('SELECT * FROM entities ORDER BY uuid').all().map(rowToEntity);
292
+ return prepared('SELECT * FROM entities ORDER BY uuid').all().map(rowToEntity);
289
293
  },
290
294
 
291
295
  insert(entity) {
292
- db.prepare(`
296
+ prepared(`
293
297
  INSERT INTO entities (uuid, type, parent_uuid, name, status, payload_version)
294
298
  VALUES (?, ?, ?, ?, ?, ?)
295
299
  `).run(entity.uuid, entity.type, entity.parentUuid, entity.name, entity.status, entity.payloadVersion);
296
300
  },
297
301
  update(uuid, { type, parentUuid, name, status, payloadVersion }) {
298
- db.prepare(`
302
+ prepared(`
299
303
  UPDATE entities SET type = ?, parent_uuid = ?, name = ?, status = ?, payload_version = ?
300
304
  WHERE uuid = ?
301
305
  `).run(type, parentUuid, name, status, payloadVersion, uuid);
@@ -313,17 +317,17 @@ function createStore(db) {
313
317
  */
314
318
  rebindIdentities(mappings) {
315
319
  const retired = mappings.filter((mapping) => mapping.oldUuid !== mapping.newUuid);
316
- const retire = db.prepare("UPDATE entities SET status = 'trashed' WHERE uuid = ?");
317
- const insert = db.prepare(`
320
+ const retire = prepared("UPDATE entities SET status = 'trashed' WHERE uuid = ?");
321
+ const insert = prepared(`
318
322
  INSERT INTO entities (uuid, type, parent_uuid, name, status, payload_version)
319
323
  VALUES (?, ?, ?, ?, ?, ?)
320
324
  `);
321
- const reparent = db.prepare('UPDATE entities SET parent_uuid = ? WHERE parent_uuid = ?');
322
- const rebindChannels = db.prepare('UPDATE channel_bindings SET entity_uuid = ? WHERE entity_uuid = ?');
323
- const rebindClaims = db.prepare('UPDATE contested_ground SET claimant_uuid = ? WHERE claimant_uuid = ?');
324
- const rebindRepoState = db.prepare('UPDATE repo_states SET entity_uuid = ? WHERE entity_uuid = ?');
325
- const rebindReference = db.prepare("UPDATE entities SET payload_version = ? WHERE type = 'reference' AND payload_version = ?");
326
- const remove = db.prepare('DELETE FROM entities WHERE uuid = ?');
325
+ const reparent = prepared('UPDATE entities SET parent_uuid = ? WHERE parent_uuid = ?');
326
+ const rebindChannels = prepared('UPDATE channel_bindings SET entity_uuid = ? WHERE entity_uuid = ?');
327
+ const rebindClaims = prepared('UPDATE contested_ground SET claimant_uuid = ? WHERE claimant_uuid = ?');
328
+ const rebindRepoState = prepared('UPDATE repo_states SET entity_uuid = ? WHERE entity_uuid = ?');
329
+ const rebindReference = prepared("UPDATE entities SET payload_version = ? WHERE type = 'reference' AND payload_version = ?");
330
+ const remove = prepared('DELETE FROM entities WHERE uuid = ?');
327
331
 
328
332
  db.transaction(() => {
329
333
  for (const mapping of retired) retire.run(mapping.oldUuid);
@@ -365,10 +369,10 @@ function createStore(db) {
365
369
  * for an exchange. One transaction: the in-between never exists.
366
370
  */
367
371
  applyMoves(moves) {
368
- const toTemp = db.prepare('UPDATE entities SET name = ? WHERE uuid = ?');
372
+ const toTemp = prepared('UPDATE entities SET name = ? WHERE uuid = ?');
369
373
  // An optional type rides the same transition (adoption lands a
370
374
  // workspace as a folder) — a separate write could tear.
371
- const toFinal = db.prepare('UPDATE entities SET parent_uuid = ?, name = ?, type = COALESCE(?, type) WHERE uuid = ?');
375
+ const toFinal = prepared('UPDATE entities SET parent_uuid = ?, name = ?, type = COALESCE(?, type) WHERE uuid = ?');
372
376
  db.transaction(() => {
373
377
  for (const move of moves) toTemp.run(`\0${move.uuid}`, move.uuid);
374
378
  for (const move of moves) toFinal.run(move.parentUuid, move.name, move.type ?? null, move.uuid);
@@ -376,7 +380,7 @@ function createStore(db) {
376
380
  },
377
381
 
378
382
  cloudReplica() {
379
- const row = db.prepare('SELECT * FROM entity_cloud_replica WHERE singleton = 1').get();
383
+ const row = prepared('SELECT * FROM entity_cloud_replica WHERE singleton = 1').get();
380
384
  if (!row) return null;
381
385
  return {
382
386
  resourceId: row.resource_id,
@@ -389,7 +393,7 @@ function createStore(db) {
389
393
  };
390
394
  },
391
395
  saveCloudReplica(replica) {
392
- db.prepare(`
396
+ prepared(`
393
397
  INSERT INTO entity_cloud_replica (
394
398
  singleton, resource_id, device_id, authority_epoch, head_version,
395
399
  state, snapshot_cutoff, updated_at
@@ -413,11 +417,11 @@ function createStore(db) {
413
417
  );
414
418
  },
415
419
  cloudMutationCutoff() {
416
- const row = db.prepare('SELECT COALESCE(MAX(sequence), 0) AS sequence FROM entity_cloud_mutations').get();
420
+ const row = prepared('SELECT COALESCE(MAX(sequence), 0) AS sequence FROM entity_cloud_mutations').get();
417
421
  return Number(row.sequence);
418
422
  },
419
423
  enqueueCloudMutation(mutation) {
420
- db.prepare(`
424
+ prepared(`
421
425
  INSERT INTO entity_cloud_mutations (
422
426
  resource_id, mutation_id, operation_json, mutation_state,
423
427
  created_at, updated_at
@@ -431,7 +435,7 @@ function createStore(db) {
431
435
  );
432
436
  },
433
437
  listCloudMutations() {
434
- return db.prepare('SELECT * FROM entity_cloud_mutations ORDER BY sequence').all();
438
+ return prepared('SELECT * FROM entity_cloud_mutations ORDER BY sequence').all();
435
439
  },
436
440
  };
437
441
  }
@@ -544,6 +544,27 @@ test('register registers the named ground and every file beneath it', async () =
544
544
  assert.equal(nested[0].type, 'file.binary', 'and it distinguishes bytes from text');
545
545
  });
546
546
 
547
+ test('a signed-out installation registers a large tree the same way it registers a small one', async () => {
548
+ // Size routes a SIGNED-IN registration to the atomic cloud replacement;
549
+ // it must never decide whether a signed-out one is allowed at all. The
550
+ // journal already no-ops without an active replica, so a large tree takes
551
+ // the same plain local registration a small tree does.
552
+ const service = makeService('add-large-signed-out');
553
+ const ground = makeGround('add-large-signed-out');
554
+ for (let dir = 0; dir < 30; dir++) {
555
+ const nested = path.join(ground, `dir${dir}`);
556
+ fs.mkdirSync(nested);
557
+ for (let file = 0; file < 20; file++) {
558
+ fs.writeFileSync(path.join(nested, `f${file}.md`), `${dir}/${file}`);
559
+ }
560
+ }
561
+
562
+ const { record } = await service.register(ground);
563
+ assert.ok(record.uuid, 'the large tree registered without demanding a cloud handshake');
564
+ const registry = service.registration.tree(ground).registry;
565
+ assert.equal(registry.children(record.uuid).length, 30, 'every directory is registered');
566
+ });
567
+
547
568
  test('registerMany registers every selected file or folder through the one registration boundary', async () => {
548
569
  const service = makeService('add-many');
549
570
  const folder = makeGround('add-many-folder');