amalgm 0.1.238 → 0.1.240

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 (86) hide show
  1. package/lib/shared-realtime-tunnel.js +22 -14
  2. package/package.json +1 -1
  3. package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +68 -20
  4. package/runtime/scripts/amalgm-mcp/project-context/store.js +87 -19
  5. package/runtime/scripts/amalgm-mcp/state/attachments.js +40 -12
  6. package/runtime/scripts/amalgm-mcp/state/db.js +216 -17
  7. package/runtime/scripts/amalgm-mcp/state/doc-disk.js +550 -0
  8. package/runtime/scripts/amalgm-mcp/state/docs.js +1101 -301
  9. package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +99 -0
  10. package/runtime/scripts/amalgm-mcp/state/mutations.js +507 -130
  11. package/runtime/scripts/amalgm-mcp/state/promotions.js +21 -1
  12. package/runtime/scripts/amalgm-mcp/state/replicas.js +134 -13
  13. package/runtime/scripts/amalgm-mcp/state/shared-rest.js +14 -8
  14. package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +193 -0
  15. package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +313 -94
  16. package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +1 -0
  17. package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +32 -17
  18. package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +84 -0
  19. package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +376 -13
  20. package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +309 -0
  21. package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +758 -30
  22. package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +72 -3
  23. package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +33 -6
  24. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +27 -0
  25. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +334 -191
  26. package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +24 -0
  27. package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +1999 -0
  28. package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +364 -0
  29. package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +81 -3
  30. package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +114 -8
  31. package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +185 -0
  32. package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +174 -269
  33. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +26 -0
  34. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +2060 -407
  35. package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +119 -0
  36. package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +1446 -0
  37. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +5319 -3
  38. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +191 -1
  39. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +3140 -17
  40. package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +135 -637
  41. package/runtime/scripts/amalgm-mcp/workspace/access-store.js +5 -1
  42. package/runtime/scripts/amalgm-mcp/workspace/card.js +7 -7
  43. package/runtime/scripts/amalgm-mcp/workspace/cards.js +36 -12
  44. package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +436 -173
  45. package/runtime/scripts/amalgm-mcp/workspace/content.js +32 -0
  46. package/runtime/scripts/amalgm-mcp/workspace/durable.js +45 -0
  47. package/runtime/scripts/amalgm-mcp/workspace/git.js +391 -37
  48. package/runtime/scripts/amalgm-mcp/workspace/intent.js +535 -0
  49. package/runtime/scripts/amalgm-mcp/workspace/merge.js +89 -6
  50. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +31 -0
  51. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
  52. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
  53. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +72 -0
  54. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +165 -0
  55. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +5 -0
  56. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +2 -0
  57. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +2201 -0
  58. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +19 -0
  59. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +18 -0
  60. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +18 -0
  61. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +33 -0
  62. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +47 -0
  63. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +43 -0
  64. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +114 -0
  65. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +84 -0
  66. package/runtime/scripts/amalgm-mcp/workspace/objects.js +119 -54
  67. package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +616 -168
  68. package/runtime/scripts/amalgm-mcp/workspace/provenance.js +240 -0
  69. package/runtime/scripts/amalgm-mcp/workspace/reducer.js +1979 -570
  70. package/runtime/scripts/amalgm-mcp/workspace/rest.js +49 -28
  71. package/runtime/scripts/amalgm-mcp/workspace/store.js +77 -4
  72. package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +240 -0
  73. package/runtime/scripts/amalgm-mcp/workspace/transition.js +1729 -192
  74. package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +100 -0
  75. package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +7 -1
  76. package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +73 -61
  77. package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +127 -0
  78. package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +12 -0
  79. package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +51 -6
  80. package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +125 -0
  81. package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +79 -8
  82. package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +76 -0
  83. package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +3480 -165
  84. package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +3766 -0
  85. package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +899 -71
  86. package/runtime/scripts/amalgm-mcp/workspace/wall.js +352 -497
@@ -24,9 +24,14 @@ fs.writeFileSync(
24
24
  const { closeLocalDb } = require('../state/db');
25
25
  const attachments = require('../state/attachments');
26
26
  const promotions = require('../state/promotions');
27
+ const replicas = require('../state/replicas');
28
+ const mutations = require('../state/mutations');
27
29
  const shared = require('../state/shared-rest');
28
30
  const projectStream = require('../workspace/project-stream');
29
31
 
32
+ const PROJECT = '018f0000-0000-7000-8000-00000000c0de';
33
+ const HEAD = '1'.repeat(40);
34
+
30
35
  test.after(() => {
31
36
  closeLocalDb();
32
37
  fs.rmSync(tempRoot, { recursive: true, force: true });
@@ -41,17 +46,62 @@ function call(handler, body) {
41
46
  });
42
47
  }
43
48
 
44
- function record(project, machine, mutationId, marker) {
49
+ function clone(value) {
50
+ return JSON.parse(JSON.stringify(value));
51
+ }
52
+
53
+ function chunks(byte = 'a', bytes = 1) {
54
+ const list = [{ sha256: byte.repeat(64), bytes }];
45
55
  return {
46
- wall: projectStream.WALL_CONTRACT,
47
- project,
48
- machine,
49
- mutationId,
50
- publishedAt: '2026-07-26T12:00:00.000Z',
51
- cards: [{ marker }],
56
+ checksum: crypto.createHash('sha256').update(Buffer.from(JSON.stringify(list))).digest('hex'),
57
+ bytes,
58
+ chunks: list,
59
+ };
60
+ }
61
+
62
+ function bundle(oid = HEAD, byte = 'a') {
63
+ return {
64
+ kind: 'bundle',
65
+ contract: projectStream.BUNDLE_CONTRACT,
66
+ resourceId: PROJECT,
67
+ oid,
68
+ ref: `refs/amalgm/exports/${oid}`,
69
+ ...chunks(byte),
70
+ };
71
+ }
72
+
73
+ function cleanValue() {
74
+ return { revision: null, indexDiff: '', worktreeDiff: '' };
75
+ }
76
+
77
+ function cardSet() {
78
+ return {
79
+ kind: 'card.set',
80
+ card: projectStream.CARD_CONTRACT,
81
+ head: { branch: 'main', oid: HEAD, detached: false },
82
+ objects: bundle(),
83
+ branches: [{ name: 'main', oid: HEAD, objects: bundle() }],
84
+ checkpoint: {
85
+ checkpoint: projectStream.CHECKPOINT_CONTRACT,
86
+ baseOid: HEAD,
87
+ branch: 'main',
88
+ value: cleanValue(),
89
+ },
52
90
  };
53
91
  }
54
92
 
93
+ function operation(delta) {
94
+ return { kind: 'code.publish', project: PROJECT, delta };
95
+ }
96
+
97
+ function official(snapshot, delta, version, machine, mutationId) {
98
+ return projectStream._test.fold(
99
+ snapshot,
100
+ operation(delta),
101
+ { version, machine, mutationId },
102
+ );
103
+ }
104
+
55
105
  function encodedSnapshot(snapshot) {
56
106
  const bytes = Buffer.from(`${JSON.stringify(snapshot)}\n`, 'utf8');
57
107
  return {
@@ -60,23 +110,227 @@ function encodedSnapshot(snapshot) {
60
110
  };
61
111
  }
62
112
 
63
- test('a code project promotes as a compact snapshot, not as a text document', async () => {
64
- const project = 'root:promotion';
65
- const resourceId = 'shr_code_project_promotion_0001';
113
+ test('the shared gateway/runtime fixture folds byte-for-byte to one canonical snapshot', () => {
114
+ const fixture = require('./fixtures/code-project-stream-v2.json');
115
+ const folded = fixture.mutations.reduce((snapshot, mutation) => (
116
+ projectStream._test.fold(
117
+ snapshot,
118
+ mutation.operation,
119
+ {
120
+ version: mutation.version,
121
+ machine: mutation.device_id,
122
+ mutationId: mutation.mutation_id,
123
+ },
124
+ )
125
+ ), fixture.initialSnapshot);
126
+ assert.deepEqual(folded, fixture.expectedSnapshot);
127
+ });
128
+
129
+ test('state-neutral events retain the independently born mutation identity', () => {
130
+ const first = official(
131
+ projectStream.emptySnapshot(PROJECT),
132
+ cardSet(),
133
+ 1,
134
+ 'machine-a',
135
+ 'mut_real_change_0001',
136
+ );
137
+ const repeatedCard = official(
138
+ first,
139
+ cardSet(),
140
+ 2,
141
+ 'machine-b',
142
+ 'mut_independent_card_0002',
143
+ );
144
+ assert.equal(repeatedCard.version, 2);
145
+ assert.equal(repeatedCard.card.mutationId, 'mut_independent_card_0002');
146
+ const repeatedCheckpoint = official(repeatedCard, {
147
+ kind: 'checkpoint.set',
148
+ checkpoint: projectStream.CHECKPOINT_CONTRACT,
149
+ baseOid: HEAD,
150
+ branch: 'main',
151
+ from: cleanValue(),
152
+ to: cleanValue(),
153
+ }, 3, 'machine-c', 'mut_independent_checkpoint_0003');
154
+ assert.equal(repeatedCheckpoint.version, 3);
155
+ assert.equal(
156
+ repeatedCheckpoint.checkpoint.mutationId,
157
+ 'mut_independent_checkpoint_0003',
158
+ );
159
+ });
160
+
161
+ test('checkpoint publication is exact-from compare-and-set', () => {
162
+ const first = official(
163
+ projectStream.emptySnapshot(PROJECT),
164
+ cardSet(),
165
+ 1,
166
+ 'machine-a',
167
+ 'mut_cas_card_0001',
168
+ );
169
+ const staleDiff = 'diff --git a/stale.js b/stale.js\n';
170
+ const stale = {
171
+ revision: require('../workspace/checkpoint').revisionOf(HEAD, '', staleDiff),
172
+ indexDiff: '',
173
+ worktreeDiff: staleDiff,
174
+ };
175
+ assert.throws(
176
+ () => official(first, {
177
+ kind: 'checkpoint.set',
178
+ checkpoint: projectStream.CHECKPOINT_CONTRACT,
179
+ baseOid: HEAD,
180
+ branch: 'main',
181
+ from: stale,
182
+ to: cleanValue(),
183
+ }, 2, 'machine-b', 'mut_stale_checkpoint_0002'),
184
+ (error) => error.code === 'code_project_stale_base',
185
+ );
186
+ });
187
+
188
+ test('all branch fields use one exact Git branch grammar', () => {
189
+ for (const name of ['HEAD', '@', '-bad', 'bad..ref', 'bad//ref', '.hidden/ref', 'bad.lock']) {
190
+ for (const mutate of [
191
+ (delta) => { delta.head.branch = name; },
192
+ (delta) => { delta.branches[0].name = name; },
193
+ (delta) => { delta.checkpoint.branch = name; },
194
+ ]) {
195
+ const delta = clone(cardSet());
196
+ mutate(delta);
197
+ assert.throws(
198
+ () => projectStream._test.normalizeDelta(delta, PROJECT),
199
+ /branch/,
200
+ name,
201
+ );
202
+ }
203
+ }
204
+ });
205
+
206
+ test('checkpoint sections prove their content revision', () => {
207
+ const inline = {
208
+ revision: 'f'.repeat(64),
209
+ sections: {
210
+ index: [],
211
+ worktree: [{ text: 'diff --git a/app.js b/app.js\n' }],
212
+ },
213
+ };
214
+ assert.throws(
215
+ () => projectStream._test.normalizeDelta({
216
+ kind: 'checkpoint.set',
217
+ checkpoint: projectStream.CHECKPOINT_CONTRACT,
218
+ baseOid: HEAD,
219
+ branch: 'main',
220
+ from: cleanValue(),
221
+ to: inline,
222
+ }, PROJECT),
223
+ /revision/,
224
+ );
225
+
226
+ const payload = {
227
+ kind: 'chunks',
228
+ contract: projectStream.PAYLOAD_CONTRACT,
229
+ resourceId: PROJECT,
230
+ ...chunks('c'),
231
+ };
232
+ assert.throws(
233
+ () => projectStream._test.normalizeDelta({
234
+ kind: 'checkpoint.set',
235
+ checkpoint: projectStream.CHECKPOINT_CONTRACT,
236
+ baseOid: HEAD,
237
+ branch: 'main',
238
+ from: cleanValue(),
239
+ to: {
240
+ revision: null,
241
+ sections: {
242
+ index: [],
243
+ worktree: [{ payload }],
244
+ },
245
+ },
246
+ }, PROJECT),
247
+ /revision/,
248
+ );
249
+ });
250
+
251
+ test('inline and external sections have one Merkle revision', () => {
252
+ const text = 'diff --git a/app.js b/app.js\n';
253
+ const { describeBytes, manifestChecksum } = require('../workspace/content');
254
+ const sectionChunks = describeBytes(Buffer.from(text, 'utf8'));
255
+ const revision = require('../workspace/checkpoint').revisionOfValue(HEAD, {
256
+ sections: {
257
+ index: [],
258
+ worktree: [{ text }],
259
+ },
260
+ });
261
+ const inline = {
262
+ revision,
263
+ sections: {
264
+ index: [],
265
+ worktree: [{ text }],
266
+ },
267
+ };
268
+ const external = {
269
+ revision,
270
+ sections: {
271
+ index: [],
272
+ worktree: [{
273
+ payload: {
274
+ kind: 'chunks',
275
+ contract: projectStream.PAYLOAD_CONTRACT,
276
+ resourceId: PROJECT,
277
+ checksum: manifestChecksum(sectionChunks),
278
+ bytes: Buffer.byteLength(text),
279
+ chunks: sectionChunks,
280
+ },
281
+ }],
282
+ },
283
+ };
284
+ for (const to of [inline, external]) {
285
+ assert.doesNotThrow(() => projectStream._test.normalizeDelta({
286
+ kind: 'checkpoint.set',
287
+ checkpoint: projectStream.CHECKPOINT_CONTRACT,
288
+ baseOid: HEAD,
289
+ branch: 'main',
290
+ from: cleanValue(),
291
+ to,
292
+ }, PROJECT));
293
+ }
294
+ });
295
+
296
+ test('one content-addressed chunk cannot claim two sizes', () => {
297
+ const delta = cardSet();
298
+ delta.branches[0].objects = {
299
+ ...delta.branches[0].objects,
300
+ ...chunks('a', 2),
301
+ };
302
+ assert.throws(
303
+ () => projectStream._test.normalizeDelta(delta, PROJECT),
304
+ /two sizes/,
305
+ );
306
+ });
307
+
308
+ test('official mutation ids use the one transport grammar', () => {
309
+ assert.throws(
310
+ () => official(
311
+ projectStream.emptySnapshot(PROJECT),
312
+ cardSet(),
313
+ 1,
314
+ 'machine-a',
315
+ 'short',
316
+ ),
317
+ /mutation id/,
318
+ );
319
+ });
320
+
321
+ test('a code project promotes as a compact v2 snapshot', async () => {
322
+ const resourceId = '018f0000-0000-7000-8000-000000000001';
66
323
  const file = path.join(tempRoot, 'promotion.json');
67
- projectStream.writeSnapshot(file, projectStream._test.emptySnapshot(project));
324
+ projectStream.writeSnapshot(file, projectStream.emptySnapshot(resourceId));
68
325
 
69
326
  const started = await call(shared.handlePromotionStart, {
70
327
  resourceId,
71
328
  path: file,
72
- project,
73
329
  contract: projectStream.STREAM_CONTRACT,
74
330
  });
75
331
  assert.equal(started.status, 202);
76
- assert.equal(started.payload.promotion.contract, projectStream.STREAM_CONTRACT);
77
- assert.equal(started.payload.promotion.localResource, `${projectStream.LOCAL_RESOURCE_PREFIX}${resourceId}`);
78
-
79
332
  const promotion = promotions.getPromotion(started.payload.promotion.promotionId);
333
+ assert.equal(promotion.schemaVersion, 2);
80
334
  promotions.acknowledgePromotion(promotion.promotionId, {
81
335
  resourceId,
82
336
  authorityEpoch: 1,
@@ -85,96 +339,61 @@ test('a code project promotes as a compact snapshot, not as a text document', as
85
339
  snapshotChecksum: promotion.snapshotChecksum,
86
340
  snapshotKey: 'shared/code-project/promotion.bin',
87
341
  });
88
- const replica = require('../state/replicas').getReplicaByResourceId(resourceId);
89
- assert.equal(replica.contract, projectStream.STREAM_CONTRACT);
90
- assert.equal(replica.localPath, file);
342
+ assert.equal(replicas.getReplicaByResourceId(resourceId).contract, projectStream.STREAM_CONTRACT);
91
343
  });
92
344
 
93
- test('empty attachment hydrates through N, then applies only event N+1', async () => {
94
- const project = 'root:hydrate';
95
- const resourceId = 'shr_code_project_attachment_0001';
96
- const file = path.join(tempRoot, 'attachment.json');
97
- const started = await call(shared.handleAttachmentStart, {
345
+ test('local outbox and ack are silent until the official tail folds', async () => {
346
+ const resourceId = '018f0000-0000-7000-8000-000000000002';
347
+ const file = path.join(tempRoot, 'official-tail.json');
348
+ const empty = projectStream.emptySnapshot(resourceId);
349
+ const encoded = encodedSnapshot(empty);
350
+ const attachment = attachments.enqueueAttachment({
351
+ attachmentId: 'att_code_project_official_tail_01',
98
352
  resourceId,
99
- path: file,
353
+ localPath: file,
100
354
  contract: projectStream.STREAM_CONTRACT,
101
355
  });
102
- assert.equal(started.status, 202);
103
-
104
- const mutationA = 'mut_code_project_snapshot_a';
105
- const snapshot = {
106
- schemaVersion: 1,
107
- project,
108
- version: 2,
109
- records: [{ ...record(project, 'machine-a', mutationA, 'snapshot'), version: 2 }],
110
- };
111
- const encoded = encodedSnapshot(snapshot);
112
- await call(shared.handleAttachmentAck, {
113
- attachmentId: started.payload.attachment.attachmentId,
356
+ attachments.acknowledgeAttachment(attachment.attachment.attachmentId, {
114
357
  resourceId,
115
358
  authorityEpoch: 1,
116
- headVersion: 2,
117
- snapshotVersion: 2,
359
+ headVersion: 0,
360
+ snapshotVersion: 0,
118
361
  contract: projectStream.STREAM_CONTRACT,
119
- schemaVersion: 1,
362
+ schemaVersion: 2,
120
363
  ...encoded,
121
364
  });
122
365
 
123
- assert.equal(attachments.getAttachmentByResource(resourceId).state, 'attached');
124
- assert.deepEqual(projectStream.readSnapshot(file, project), snapshot);
125
-
126
- const mutationB = 'mut_code_project_cloud_b';
127
- await call(shared.handleMutationIngest, {
128
- version: 3,
129
- committedAt: '2026-07-26T12:00:03.000Z',
130
- envelope: {
131
- resourceId,
132
- authorityEpoch: 1,
133
- mutationId: mutationB,
134
- deviceId: 'machine-b',
135
- baseVersion: 2,
136
- contract: projectStream.STREAM_CONTRACT,
137
- schemaVersion: 1,
138
- operationKind: 'code.publish',
139
- operation: {
140
- kind: 'code.publish',
141
- record: record(project, 'machine-b', mutationB, 'event-3'),
142
- },
143
- },
144
- });
145
- assert.deepEqual(
146
- projectStream.readSnapshot(file, project).records.map((entry) => [
147
- entry.machine,
148
- entry.version,
149
- entry.cards[0].marker,
150
- ]),
151
- [
152
- ['machine-a', 2, 'snapshot'],
153
- ['machine-b', 3, 'event-3'],
154
- ],
366
+ const project = { resourceId, file };
367
+ const delta = {
368
+ ...cardSet(),
369
+ objects: { ...cardSet().objects, resourceId },
370
+ branches: cardSet().branches.map((branch) => ({
371
+ ...branch,
372
+ objects: { ...branch.objects, resourceId },
373
+ })),
374
+ };
375
+ const published = projectStream.publish(
376
+ project,
377
+ delta,
378
+ 'mut_code_official_tail_01',
155
379
  );
380
+ assert.equal(projectStream.readSnapshot(file, resourceId).version, 0);
381
+ const claimed = mutations.claimCloudMutations().find((entry) => (
382
+ entry.mutation.mutationId === published.mutation.mutationId
383
+ ));
384
+ mutations.acknowledgeCloudMutation(resourceId, published.mutation.mutationId, {
385
+ version: 1,
386
+ committedAt: '2026-07-26T12:00:00.000Z',
387
+ });
388
+ assert.equal(replicas.getReplicaByResourceId(resourceId).appliedVersion, 0);
389
+ assert.equal(projectStream.readSnapshot(file, resourceId).version, 0);
156
390
 
157
- const beforeGap = fs.readFileSync(file, 'utf8');
158
- await assert.rejects(
159
- call(shared.handleMutationIngest, {
160
- version: 5,
161
- committedAt: '2026-07-26T12:00:05.000Z',
162
- envelope: {
163
- resourceId,
164
- authorityEpoch: 1,
165
- mutationId: 'mut_code_project_gap_005',
166
- deviceId: 'machine-c',
167
- baseVersion: 4,
168
- contract: projectStream.STREAM_CONTRACT,
169
- schemaVersion: 1,
170
- operationKind: 'code.publish',
171
- operation: {
172
- kind: 'code.publish',
173
- record: record(project, 'machine-c', 'mut_code_project_gap_005', 'gap'),
174
- },
175
- },
176
- }),
177
- /expected 4, received 5/,
178
- );
179
- assert.equal(fs.readFileSync(file, 'utf8'), beforeGap);
391
+ projectStream.applyCloudMutation({
392
+ version: 1,
393
+ committedAt: '2026-07-26T12:00:00.000Z',
394
+ envelope: claimed.envelope,
395
+ });
396
+ assert.equal(replicas.getReplicaByResourceId(resourceId).appliedVersion, 1);
397
+ assert.equal(projectStream.readSnapshot(file, resourceId).version, 1);
398
+ assert.equal(mutations.listUnappliedOfficialMutations(resourceId).length, 0);
180
399
  });
@@ -0,0 +1 @@
1
+ {"initialSnapshot":{"schemaVersion":2,"project":"018f0000-0000-7000-8000-00000000c0de","version":0,"card":null,"checkpoint":null},"mutations":[{"resource_id":"018f0000-0000-7000-8000-00000000c0de","version":1,"authority_epoch":1,"mutation_id":"mut_fixture_card_0001","actor_user_id":"11111111-1111-4111-8111-111111111111","device_id":"machine-a","base_version":0,"contract":"code-project-stream@2","schema_version":2,"operation_kind":"code.publish","operation":{"kind":"code.publish","project":"018f0000-0000-7000-8000-00000000c0de","delta":{"kind":"card.set","card":"repo-card@3","head":{"branch":"main","oid":"1111111111111111111111111111111111111111","detached":false},"objects":{"kind":"bundle","contract":"git-bundle-chunks@2","resourceId":"018f0000-0000-7000-8000-00000000c0de","oid":"1111111111111111111111111111111111111111","ref":"refs/amalgm/exports/1111111111111111111111111111111111111111","checksum":"c73bdbb6f6997aa08dfa250e66c49dd02aaa826c293bc9221f4b80614b9640ba","bytes":1,"chunks":[{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bytes":1}]},"branches":[{"name":"dev","oid":"2222222222222222222222222222222222222222","objects":{"kind":"bundle","contract":"git-bundle-chunks@2","resourceId":"018f0000-0000-7000-8000-00000000c0de","oid":"2222222222222222222222222222222222222222","ref":"refs/amalgm/exports/2222222222222222222222222222222222222222","checksum":"7bee928eebf88ea25f8c85aad26c2120325d4cee5ed3fbdd2ed7e92b9df46d98","bytes":2,"chunks":[{"sha256":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","bytes":2}]}},{"name":"main","oid":"1111111111111111111111111111111111111111","objects":{"kind":"bundle","contract":"git-bundle-chunks@2","resourceId":"018f0000-0000-7000-8000-00000000c0de","oid":"1111111111111111111111111111111111111111","ref":"refs/amalgm/exports/1111111111111111111111111111111111111111","checksum":"c73bdbb6f6997aa08dfa250e66c49dd02aaa826c293bc9221f4b80614b9640ba","bytes":1,"chunks":[{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bytes":1}]}}],"checkpoint":{"checkpoint":"repo-checkpoint@11","baseOid":"1111111111111111111111111111111111111111","branch":"main","value":{"revision":null,"indexDiff":"","worktreeDiff":""}}}},"committed_at":"2026-07-26T12:00:00.000Z"},{"resource_id":"018f0000-0000-7000-8000-00000000c0de","version":2,"authority_epoch":1,"mutation_id":"mut_fixture_checkpoint_0002","actor_user_id":"22222222-2222-4222-8222-222222222222","device_id":"machine-b","base_version":1,"contract":"code-project-stream@2","schema_version":2,"operation_kind":"code.publish","operation":{"kind":"code.publish","project":"018f0000-0000-7000-8000-00000000c0de","delta":{"kind":"checkpoint.set","checkpoint":"repo-checkpoint@11","baseOid":"1111111111111111111111111111111111111111","branch":"main","from":{"revision":null,"indexDiff":"","worktreeDiff":""},"to":{"revision":"643c62c9879267f7312002ffeb672cce37f9bd1436306430deac344dfa2944e9","indexDiff":"","worktreeDiff":"diff --git a/app.js b/app.js\n"}}},"committed_at":"2026-07-26T12:00:01.000Z"}],"expectedSnapshot":{"schemaVersion":2,"project":"018f0000-0000-7000-8000-00000000c0de","version":2,"card":{"card":"repo-card@3","head":{"branch":"main","oid":"1111111111111111111111111111111111111111","detached":false},"objects":{"kind":"bundle","contract":"git-bundle-chunks@2","resourceId":"018f0000-0000-7000-8000-00000000c0de","oid":"1111111111111111111111111111111111111111","ref":"refs/amalgm/exports/1111111111111111111111111111111111111111","checksum":"c73bdbb6f6997aa08dfa250e66c49dd02aaa826c293bc9221f4b80614b9640ba","bytes":1,"chunks":[{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bytes":1}]},"branches":[{"name":"dev","oid":"2222222222222222222222222222222222222222","objects":{"kind":"bundle","contract":"git-bundle-chunks@2","resourceId":"018f0000-0000-7000-8000-00000000c0de","oid":"2222222222222222222222222222222222222222","ref":"refs/amalgm/exports/2222222222222222222222222222222222222222","checksum":"7bee928eebf88ea25f8c85aad26c2120325d4cee5ed3fbdd2ed7e92b9df46d98","bytes":2,"chunks":[{"sha256":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","bytes":2}]}},{"name":"main","oid":"1111111111111111111111111111111111111111","objects":{"kind":"bundle","contract":"git-bundle-chunks@2","resourceId":"018f0000-0000-7000-8000-00000000c0de","oid":"1111111111111111111111111111111111111111","ref":"refs/amalgm/exports/1111111111111111111111111111111111111111","checksum":"c73bdbb6f6997aa08dfa250e66c49dd02aaa826c293bc9221f4b80614b9640ba","bytes":1,"chunks":[{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bytes":1}]}}],"machine":"machine-a","mutationId":"mut_fixture_card_0001","version":1},"checkpoint":{"checkpoint":"repo-checkpoint@11","baseOid":"1111111111111111111111111111111111111111","branch":"main","value":{"revision":"643c62c9879267f7312002ffeb672cce37f9bd1436306430deac344dfa2944e9","indexDiff":"","worktreeDiff":"diff --git a/app.js b/app.js\n"},"machine":"machine-b","mutationId":"mut_fixture_checkpoint_0002","version":2}}}
@@ -394,27 +394,42 @@ test('workspace removal drops the project_context row', () => {
394
394
  assert.equal(store.getProjectContext(workspace.path, { reconcile: false }), null);
395
395
  });
396
396
 
397
- test('watcher reconciles direct disk edits', async () => {
397
+ test('the bounded watcher schedule reconciles a dropped notification and retries the watcher', async () => {
398
398
  const { projectPath, workspace } = makeProject('watch');
399
- store.ensureProjectContext(workspace);
400
- const before = store.getProjectContext(projectPath, { reconcile: false }).contextRevision;
399
+ const originalWatch = fs.watch;
400
+ let watchAttempts = 0;
401
+ fs.watch = (...args) => {
402
+ watchAttempts += 1;
403
+ if (watchAttempts === 1) {
404
+ const error = new Error('simulated dropped watcher');
405
+ error.code = 'EMFILE';
406
+ throw error;
407
+ }
408
+ return originalWatch.apply(fs, args);
409
+ };
401
410
 
402
- fs.writeFileSync(
403
- path.join(projectPath, '.amalgm/context/project-state.md'),
404
- '# Project State\n\nWatched edit.\n',
405
- );
411
+ try {
412
+ store.ensureProjectContext(workspace);
413
+ const before = store.getProjectContext(projectPath, { reconcile: false }).contextRevision;
406
414
 
407
- // fs.watch + 200ms debounce; poll the row instead of guessing exact timing.
408
- const deadline = Date.now() + 3000;
409
- let row = null;
410
- while (Date.now() < deadline) {
411
- row = store.getProjectContext(projectPath, { reconcile: false });
412
- if (row && row.contextRevision !== before) break;
413
- await new Promise((resolve) => setTimeout(resolve, 100));
415
+ fs.writeFileSync(
416
+ path.join(projectPath, '.amalgm/context/project-state.md'),
417
+ '# Project State\n\nWatched edit.\n',
418
+ );
419
+
420
+ const deadline = Date.now() + 7_000;
421
+ let row = store.getProjectContext(projectPath, { reconcile: false });
422
+ while (row?.contextRevision === before && Date.now() < deadline) {
423
+ await new Promise((resolve) => setTimeout(resolve, 50));
424
+ row = store.getProjectContext(projectPath, { reconcile: false });
425
+ }
426
+ assert.ok(row, 'row exists');
427
+ assert.notEqual(row.contextRevision, before, 'scheduled verification updated the row');
428
+ assert.ok(row.prompt.projectState.includes('Watched edit.'));
429
+ assert.ok(watchAttempts >= 2, 'the scheduled pass retried the failed watcher');
430
+ } finally {
431
+ fs.watch = originalWatch;
414
432
  }
415
- assert.ok(row, 'row exists');
416
- assert.notEqual(row.contextRevision, before, 'watcher updated the row');
417
- assert.ok(row.prompt.projectState.includes('Watched edit.'));
418
433
  });
419
434
 
420
435
  // ---------------------------------------------------------------------------
@@ -213,3 +213,87 @@ test('a pruned-tail join answer triggers rehydration through the local state ser
213
213
  server.close();
214
214
  }
215
215
  });
216
+
217
+ test('an authorized attachment with a local materialization failure stays retryable', async () => {
218
+ const calls = [];
219
+ let attachmentClaimed = false;
220
+ const server = http.createServer((req, res) => {
221
+ let body = '';
222
+ req.on('data', (chunk) => { body += chunk; });
223
+ req.on('end', () => {
224
+ const parsed = body ? JSON.parse(body) : null;
225
+ calls.push({ path: req.url, body: parsed });
226
+ res.setHeader('content-type', 'application/json');
227
+ if (req.url === '/state/shared/replicas') {
228
+ res.end('{"replicas":[]}');
229
+ return;
230
+ }
231
+ if (req.url.startsWith('/state/stream')) {
232
+ res.writeHead(200, { 'content-type': 'text/event-stream' });
233
+ res.write('\n');
234
+ return;
235
+ }
236
+ if (req.url === '/state/shared/attachments/claim') {
237
+ const attachments = attachmentClaimed
238
+ ? []
239
+ : [{
240
+ attachmentId: 'att_local_materialize_retry_01',
241
+ resourceId: 'shr_localretrytest01',
242
+ }];
243
+ attachmentClaimed = true;
244
+ res.end(JSON.stringify({ attachments }));
245
+ return;
246
+ }
247
+ if (req.url === '/state/shared/attachments/ack') {
248
+ res.statusCode = 409;
249
+ res.end('{"error":"local disk changed during snapshot install"}');
250
+ return;
251
+ }
252
+ res.end('{"mutations":[],"promotions":[],"attachments":[]}');
253
+ });
254
+ });
255
+ await new Promise((resolve) => { server.listen(0, '127.0.0.1', resolve); });
256
+ const sent = [];
257
+ const tunnel = createSharedRealtimeTunnel({
258
+ send: (frame) => { sent.push(frame); return true; },
259
+ sendLarge: (frame) => { sent.push(frame); return true; },
260
+ localPort: () => server.address().port,
261
+ localHeaders: (headers) => ({ ...headers }),
262
+ warn: () => {},
263
+ });
264
+ try {
265
+ tunnel.enable(['shared.mutation.v1']);
266
+ await until(
267
+ () => sent.some((frame) => frame.type === 'shared.resource.attach'),
268
+ 'the attachment request',
269
+ );
270
+ const request = sent.find((frame) => frame.type === 'shared.resource.attach');
271
+ tunnel.handleFrame({
272
+ type: 'shared.resource.attached',
273
+ request_id: request.request_id,
274
+ resource_id: request.resource_id,
275
+ authority_epoch: 1,
276
+ head_version: 0,
277
+ snapshot_version: 0,
278
+ snapshot_checksum: 'a'.repeat(64),
279
+ snapshot_b64: 'AA==',
280
+ contract: 'text-yjs@1',
281
+ schema_version: 1,
282
+ });
283
+ await until(
284
+ () => calls.some((call) => call.path === '/state/shared/attachments/fail'),
285
+ 'the retryable local attachment failure',
286
+ );
287
+ assert.deepEqual(
288
+ calls.find((call) => call.path === '/state/shared/attachments/fail').body,
289
+ {
290
+ attachmentId: 'att_local_materialize_retry_01',
291
+ message: 'local disk changed during snapshot install',
292
+ retryable: true,
293
+ },
294
+ );
295
+ } finally {
296
+ tunnel.stop();
297
+ server.close();
298
+ }
299
+ });