amalgm 0.1.245 → 0.1.246

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.
Files changed (157) hide show
  1. package/README.md +12 -0
  2. package/lib/cli.js +146 -8
  3. package/lib/layout.js +89 -4
  4. package/lib/shared-realtime-tunnel.js +384 -43
  5. package/lib/supervisor.js +0 -3
  6. package/package.json +2 -2
  7. package/runtime/scripts/amalgm-mcp/adapters/contract.js +197 -0
  8. package/runtime/scripts/amalgm-mcp/adapters/filesystem.js +135 -0
  9. package/runtime/scripts/amalgm-mcp/adapters/git.js +84 -0
  10. package/runtime/scripts/amalgm-mcp/adapters/index.js +43 -0
  11. package/runtime/scripts/amalgm-mcp/adapters/reference.js +31 -0
  12. package/runtime/scripts/amalgm-mcp/adapters/truth.js +19 -0
  13. package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +20 -68
  14. package/runtime/scripts/amalgm-mcp/index.js +4 -0
  15. package/runtime/scripts/amalgm-mcp/lib/layout.js +89 -4
  16. package/runtime/scripts/amalgm-mcp/observer/README.md +259 -0
  17. package/runtime/scripts/amalgm-mcp/observer/apply.js +150 -0
  18. package/runtime/scripts/amalgm-mcp/observer/continuity.js +111 -0
  19. package/runtime/scripts/amalgm-mcp/observer/edges.js +111 -0
  20. package/runtime/scripts/amalgm-mcp/observer/index.js +1146 -0
  21. package/runtime/scripts/amalgm-mcp/observer/scan.js +387 -0
  22. package/runtime/scripts/amalgm-mcp/observer/store.js +262 -0
  23. package/runtime/scripts/amalgm-mcp/observer/verify.js +200 -0
  24. package/runtime/scripts/amalgm-mcp/observer/watch.js +62 -0
  25. package/runtime/scripts/amalgm-mcp/project-context/store.js +19 -87
  26. package/runtime/scripts/amalgm-mcp/registration/classify.js +33 -0
  27. package/runtime/scripts/amalgm-mcp/registration/entity-cloud.js +456 -0
  28. package/runtime/scripts/amalgm-mcp/registration/entity-content.js +345 -0
  29. package/runtime/scripts/amalgm-mcp/registration/index.js +752 -0
  30. package/runtime/scripts/amalgm-mcp/registration/refusal.js +42 -0
  31. package/runtime/scripts/amalgm-mcp/registration/repo-followers.js +127 -0
  32. package/runtime/scripts/amalgm-mcp/registration/repo-states.js +109 -0
  33. package/runtime/scripts/amalgm-mcp/registration/service.js +310 -0
  34. package/runtime/scripts/amalgm-mcp/registration/tree.js +178 -0
  35. package/runtime/scripts/amalgm-mcp/registry/evidence.js +826 -0
  36. package/runtime/scripts/amalgm-mcp/registry/index.js +666 -0
  37. package/runtime/scripts/amalgm-mcp/registry/store.js +290 -0
  38. package/runtime/scripts/amalgm-mcp/repocard/README.md +103 -0
  39. package/runtime/scripts/amalgm-mcp/repocard/apply.js +111 -0
  40. package/runtime/scripts/amalgm-mcp/repocard/capture-worker.js +94 -0
  41. package/runtime/scripts/amalgm-mcp/repocard/capture.js +149 -0
  42. package/runtime/scripts/amalgm-mcp/repocard/follow.js +310 -0
  43. package/runtime/scripts/amalgm-mcp/repocard/git.js +48 -0
  44. package/runtime/scripts/amalgm-mcp/repocard/index.js +48 -0
  45. package/runtime/scripts/amalgm-mcp/server/local-service-router.js +2 -0
  46. package/runtime/scripts/amalgm-mcp/server/routes/entities.js +287 -0
  47. package/runtime/scripts/amalgm-mcp/server/routes/state.js +0 -13
  48. package/runtime/scripts/amalgm-mcp/state/attachments.js +11 -45
  49. package/runtime/scripts/amalgm-mcp/state/db.js +46 -215
  50. package/runtime/scripts/amalgm-mcp/state/docs.js +301 -1101
  51. package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +0 -99
  52. package/runtime/scripts/amalgm-mcp/state/mutations.js +130 -507
  53. package/runtime/scripts/amalgm-mcp/state/promotions.js +1 -21
  54. package/runtime/scripts/amalgm-mcp/state/replicas.js +13 -134
  55. package/runtime/scripts/amalgm-mcp/state/shared-rest.js +3 -46
  56. package/runtime/scripts/amalgm-mcp/tests/adapters.test.js +379 -0
  57. package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +0 -193
  58. package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.life.test.js +449 -0
  59. package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.rig.js +107 -0
  60. package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.watch.test.js +213 -0
  61. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/adapter.js +72 -0
  62. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/assert.js +497 -0
  63. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/bind.js +91 -0
  64. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/graph.js +1031 -0
  65. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/ledger.js +234 -0
  66. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/referee.js +118 -0
  67. package/runtime/scripts/amalgm-mcp/tests/entity.oracle.test.js +1365 -0
  68. package/runtime/scripts/amalgm-mcp/tests/entity.registry.test.js +967 -0
  69. package/runtime/scripts/amalgm-mcp/tests/entity.rig.js +239 -0
  70. package/runtime/scripts/amalgm-mcp/tests/entity.storm.test.js +486 -0
  71. package/runtime/scripts/amalgm-mcp/tests/fake-watch.js +35 -0
  72. package/runtime/scripts/amalgm-mcp/tests/observer.links.test.js +289 -0
  73. package/runtime/scripts/amalgm-mcp/tests/observer.rig.js +103 -0
  74. package/runtime/scripts/amalgm-mcp/tests/observer.roots.test.js +505 -0
  75. package/runtime/scripts/amalgm-mcp/tests/observer.storm.test.js +389 -0
  76. package/runtime/scripts/amalgm-mcp/tests/observer.territory.test.js +845 -0
  77. package/runtime/scripts/amalgm-mcp/tests/observer.test.js +657 -0
  78. package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +17 -32
  79. package/runtime/scripts/amalgm-mcp/tests/registration.service.test.js +256 -0
  80. package/runtime/scripts/amalgm-mcp/tests/registration.test.js +931 -0
  81. package/runtime/scripts/amalgm-mcp/tests/repocard.storm.test.js +284 -0
  82. package/runtime/scripts/amalgm-mcp/tests/repocard.test.js +371 -0
  83. package/runtime/scripts/amalgm-mcp/tests/repofollow.storm.test.js +435 -0
  84. package/runtime/scripts/amalgm-mcp/tests/repofollow.test.js +675 -0
  85. package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +0 -84
  86. package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +13 -376
  87. package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +0 -309
  88. package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +30 -758
  89. package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +3 -72
  90. package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +1 -89
  91. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +24 -55
  92. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +141 -424
  93. package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +3 -171
  94. package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +7 -220
  95. package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +236 -179
  96. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +326 -2228
  97. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +3 -5319
  98. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +1 -191
  99. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +17 -3140
  100. package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +740 -137
  101. package/runtime/scripts/amalgm-mcp/workspace/access-store.js +1 -5
  102. package/runtime/scripts/amalgm-mcp/workspace/card.js +11 -35
  103. package/runtime/scripts/amalgm-mcp/workspace/cards.js +14 -39
  104. package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +147 -587
  105. package/runtime/scripts/amalgm-mcp/workspace/git.js +34 -406
  106. package/runtime/scripts/amalgm-mcp/workspace/merge.js +181 -102
  107. package/runtime/scripts/amalgm-mcp/workspace/objects.js +56 -221
  108. package/runtime/scripts/amalgm-mcp/workspace/reducer.js +538 -1964
  109. package/runtime/scripts/amalgm-mcp/workspace/rest.js +28 -49
  110. package/runtime/scripts/amalgm-mcp/workspace/store.js +4 -77
  111. package/runtime/scripts/amalgm-mcp/workspace/transition.js +192 -1734
  112. package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +1 -7
  113. package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +61 -73
  114. package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +6 -51
  115. package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +8 -79
  116. package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +146 -3461
  117. package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +71 -899
  118. package/runtime/scripts/amalgm-mcp/workspace/wall.js +538 -370
  119. package/runtime/scripts/local-gateway.js +1 -0
  120. package/runtime/scripts/amalgm-mcp/state/doc-disk.js +0 -550
  121. package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +0 -399
  122. package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +0 -1
  123. package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +0 -24
  124. package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +0 -1999
  125. package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +0 -364
  126. package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +0 -185
  127. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +0 -26
  128. package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +0 -119
  129. package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +0 -1446
  130. package/runtime/scripts/amalgm-mcp/workspace/content.js +0 -32
  131. package/runtime/scripts/amalgm-mcp/workspace/durable.js +0 -45
  132. package/runtime/scripts/amalgm-mcp/workspace/intent.js +0 -535
  133. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +0 -31
  134. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
  135. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
  136. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +0 -72
  137. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +0 -165
  138. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +0 -5
  139. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +0 -2
  140. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +0 -2201
  141. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +0 -19
  142. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +0 -18
  143. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +0 -18
  144. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +0 -33
  145. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +0 -47
  146. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +0 -43
  147. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +0 -114
  148. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +0 -84
  149. package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +0 -771
  150. package/runtime/scripts/amalgm-mcp/workspace/provenance.js +0 -240
  151. package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +0 -240
  152. package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +0 -100
  153. package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +0 -127
  154. package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +0 -12
  155. package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +0 -125
  156. package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +0 -76
  157. package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +0 -3766
@@ -0,0 +1,239 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * The shared rig for the entity oracle suites — the sandbox with the full
5
+ * cast (real observer, registry, evidence intake, operation model, ledger,
6
+ * binders) and the generator helpers that perform each real filesystem
7
+ * operation AND record it in the model. The exams live in
8
+ * entity.oracle.test.js; the storms in entity.storm.test.js.
9
+ *
10
+ * This file may require production code — it drives the system under exam.
11
+ * The judge itself (tests/entity-oracle/) may not, and a meta-test holds it
12
+ * to that.
13
+ */
14
+
15
+ const assert = require('node:assert/strict');
16
+ const { execFileSync } = require('child_process');
17
+ const fs = require('fs');
18
+ const os = require('os');
19
+ const path = require('path');
20
+
21
+ const Database = require('better-sqlite3');
22
+ const { createObserver } = require('../observer');
23
+ const { createRegistry } = require('../registry');
24
+ const { createEvidence, createEvidenceMemory } = require('../registry/evidence');
25
+ const { createModel } = require('./entity-oracle/graph');
26
+ const { createLedger } = require('./entity-oracle/ledger');
27
+ const { createBinder, createChannelBinder } = require('./entity-oracle/bind');
28
+ const { assertQuietPoint } = require('./entity-oracle/assert');
29
+
30
+ const defaultPolicy = require('../workspace/tree/inclusion').shouldEnroll;
31
+
32
+ // The RIG's explicit classification policy — injected into the model
33
+ // exactly like the enrollment policy. "NUL byte means binary" is a choice
34
+ // these tests make; the model owns no such rule.
35
+ const classify = (bytes) => (
36
+ (Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes)).includes(0) ? 'file.binary' : 'file.text');
37
+
38
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'amalgm-entity-rig-'));
39
+
40
+ function cleanup() {
41
+ fs.rmSync(tempRoot, { recursive: true, force: true });
42
+ }
43
+
44
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
45
+
46
+ /**
47
+ * Read-only convergence for event-only exams. `act` rings doorbells; the
48
+ * production chain those doorbells drive — the settle scan, then the
49
+ * scheduled registry intake — must finish ON ITS OWN. This waits by
50
+ * looking only (the detector spoke: events grew; the intake drained:
51
+ * dirty is false); it never scans, flushes, or repairs, so a system
52
+ * that needed help still fails — loudly at the deadline, never by
53
+ * stopwatch luck under parallel load. (The settle scan is one
54
+ * synchronous batch and its scheduled reconcile runs before the next
55
+ * timer tick, so "grew and drained" observed from a timer callback
56
+ * means the whole chain completed.)
57
+ */
58
+ async function converged(kit, act, deadlineMs = 5_000) {
59
+ const before = kit.events.length;
60
+ act();
61
+ const deadline = Date.now() + deadlineMs;
62
+ while (kit.events.length === before || kit.evidence.dirty()) {
63
+ assert.ok(Date.now() < deadline,
64
+ 'oracle: the event-driven chain did not converge before the deadline');
65
+ await sleep(5);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * A sandbox with the full cast: real observer, operation model, ledger,
71
+ * binder, and generator helpers that perform each real filesystem operation
72
+ * AND record it in the model.
73
+ */
74
+ function makeKit(name, { policy = defaultPolicy, watch, settleMs } = {}) {
75
+ const dir = fs.realpathSync(fs.mkdtempSync(path.join(tempRoot, `${name}-`)));
76
+ const events = [];
77
+ const regEvents = [];
78
+ let evidence; // wired below; observer events are hints into its intake
79
+ const observer = createObserver({
80
+ db: new Database(':memory:'),
81
+ emit: (event) => {
82
+ events.push(event);
83
+ evidence?.onEvent();
84
+ },
85
+ shouldEnroll: policy,
86
+ ...(watch ? { watch } : {}),
87
+ ...(settleMs !== undefined ? { settleMs } : {}),
88
+ });
89
+ const registryDb = new Database(':memory:');
90
+ const registry = createRegistry({
91
+ db: registryDb,
92
+ ephemeral: true,
93
+ emit: (event) => regEvents.push(event),
94
+ });
95
+ const memory = createEvidenceMemory(registryDb);
96
+ const model = createModel({ shouldEnroll: policy, classify });
97
+ const kit = {
98
+ dir, events, observer, model, policy,
99
+ registry, evidence, regEvents,
100
+ ledger: createLedger(),
101
+ binder: createChannelBinder(),
102
+ // Model uuid ↔ registry uuid, observe-only: records are permanent
103
+ // identity, so a registry-side rebind is an identity break.
104
+ registryBinder: createBinder(),
105
+ cursor: { value: 0 },
106
+ rootPath: dir,
107
+ rootId: null,
108
+
109
+ abs: (rel) => path.join(dir, rel),
110
+ enroll() {
111
+ kit.rootId = observer.enrollRoot(dir).rootId;
112
+ // The lens is keyed by the tree's permanent identity, so it is
113
+ // born here — birth reads store truth, exactly like the boundary.
114
+ evidence = createEvidence({
115
+ observer, registry, memory, rootId: kit.rootId,
116
+ // The evidence layer's injected classification policy reads the
117
+ // ground it is classifying — the same NUL rule the model got.
118
+ classify: (absPath) => classify(fs.readFileSync(absPath)),
119
+ });
120
+ kit.evidence = evidence;
121
+ evidence.reconcile();
122
+ return kit.rootId;
123
+ },
124
+ point(options) {
125
+ return assertQuietPoint(kit, options);
126
+ },
127
+
128
+ // --- generator: real op + model record, always in that order ---------
129
+ write(rel, data) {
130
+ fs.mkdirSync(path.dirname(kit.abs(rel)), { recursive: true });
131
+ fs.writeFileSync(kit.abs(rel), data);
132
+ model.write(rel, data);
133
+ },
134
+ replace(rel, data) {
135
+ const tmp = `${kit.abs(rel)}.tmp`;
136
+ fs.writeFileSync(tmp, data);
137
+ fs.renameSync(tmp, kit.abs(rel));
138
+ model.replace(rel, data);
139
+ },
140
+ touchSame(rel) {
141
+ const future = new Date(Date.now() + 5_000);
142
+ fs.utimesSync(kit.abs(rel), future, future);
143
+ model.touch();
144
+ },
145
+ mv(from, to) {
146
+ fs.mkdirSync(path.dirname(kit.abs(to)), { recursive: true });
147
+ fs.renameSync(kit.abs(from), kit.abs(to));
148
+ model.rename(from, to);
149
+ },
150
+ mvDir(from, to) {
151
+ fs.renameSync(kit.abs(from), kit.abs(to));
152
+ model.renameDir(from, to);
153
+ },
154
+ mkdir(rel) {
155
+ fs.mkdirSync(kit.abs(rel), { recursive: true });
156
+ model.mkdir(rel);
157
+ },
158
+ copy(from, to, data) {
159
+ fs.copyFileSync(kit.abs(from), kit.abs(to));
160
+ model.write(to, data);
161
+ },
162
+ rm(rel) {
163
+ fs.rmSync(kit.abs(rel));
164
+ model.remove(rel);
165
+ },
166
+ rmDir(rel) {
167
+ fs.rmSync(kit.abs(rel), { recursive: true, force: true });
168
+ model.removeDir(rel);
169
+ },
170
+ gitInit(rel) {
171
+ fs.mkdirSync(kit.abs(rel), { recursive: true });
172
+ execFileSync('git', ['init', '--quiet'], { cwd: kit.abs(rel) });
173
+ model.gitInit(rel);
174
+ },
175
+ gitRemove(rel) {
176
+ fs.rmSync(path.join(kit.abs(rel), '.git'), { recursive: true, force: true });
177
+ model.gitRemove(rel);
178
+ },
179
+
180
+ // --- link ground: a pointer's whole payload is its written target ----
181
+ symlink(rel, target) {
182
+ fs.mkdirSync(path.dirname(kit.abs(rel)), { recursive: true });
183
+ fs.symlinkSync(target, kit.abs(rel));
184
+ model.writeLink(rel, target);
185
+ },
186
+ /** Retarget IS unlink + recreate — the address absorbs the churn. */
187
+ retarget(rel, target) {
188
+ fs.rmSync(kit.abs(rel));
189
+ fs.symlinkSync(target, kit.abs(rel));
190
+ model.writeLink(rel, target);
191
+ },
192
+ mvLink(from, to) {
193
+ fs.mkdirSync(path.dirname(kit.abs(to)), { recursive: true });
194
+ fs.renameSync(kit.abs(from), kit.abs(to));
195
+ model.renameLink(from, to);
196
+ },
197
+ rmLink(rel) {
198
+ fs.rmSync(kit.abs(rel));
199
+ model.removeLink(rel);
200
+ },
201
+ };
202
+ return kit;
203
+ }
204
+
205
+ function fakeWatchKit() {
206
+ const bells = new Map();
207
+ return {
208
+ watch: (rootPath, onDirty) => {
209
+ bells.set(rootPath, onDirty);
210
+ return { degraded: false, close: () => bells.delete(rootPath) };
211
+ },
212
+ ring: (rootPath, relPath) => bells.get(rootPath)(relPath),
213
+ /** Every open watcher whose ground covers this absolute path rings,
214
+ * exactly as a real OS event fans out to nested recursive watchers. */
215
+ ringAt(absPath) {
216
+ for (const [watched, onDirty] of bells) {
217
+ if (absPath === watched) onDirty('');
218
+ else if (absPath.startsWith(`${watched}/`)) onDirty(absPath.slice(watched.length + 1));
219
+ }
220
+ },
221
+ };
222
+ }
223
+
224
+ function fileIdAt(kit, rel) {
225
+ const row = kit.observer.filesForRoot(kit.rootId).find((r) => r.relPath === rel);
226
+ assert.ok(row, `expected a store row at ${rel}`);
227
+ return row.fileId;
228
+ }
229
+
230
+ function linkIdAt(kit, rel) {
231
+ const row = kit.observer.linksForRoot(kit.rootId).find((r) => r.relPath === rel);
232
+ assert.ok(row, `expected a link row at ${rel}`);
233
+ return row.linkId;
234
+ }
235
+
236
+ module.exports = {
237
+ cleanup, converged, defaultPolicy, fakeWatchKit, fileIdAt,
238
+ linkIdAt, makeKit, sleep,
239
+ };