amalgm 0.1.239 → 0.1.241

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 (87) hide show
  1. package/lib/shared-realtime-tunnel.js +18 -4
  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/server/routes/state.js +13 -0
  6. package/runtime/scripts/amalgm-mcp/state/attachments.js +40 -12
  7. package/runtime/scripts/amalgm-mcp/state/db.js +216 -17
  8. package/runtime/scripts/amalgm-mcp/state/doc-disk.js +550 -0
  9. package/runtime/scripts/amalgm-mcp/state/docs.js +1101 -301
  10. package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +99 -0
  11. package/runtime/scripts/amalgm-mcp/state/mutations.js +507 -130
  12. package/runtime/scripts/amalgm-mcp/state/promotions.js +21 -1
  13. package/runtime/scripts/amalgm-mcp/state/replicas.js +134 -13
  14. package/runtime/scripts/amalgm-mcp/state/shared-rest.js +14 -8
  15. package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +193 -0
  16. package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +313 -94
  17. package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +1 -0
  18. package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +32 -17
  19. package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +84 -0
  20. package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +376 -13
  21. package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +309 -0
  22. package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +758 -30
  23. package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +72 -3
  24. package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +33 -6
  25. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +27 -0
  26. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +334 -191
  27. package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +24 -0
  28. package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +1999 -0
  29. package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +364 -0
  30. package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +98 -4
  31. package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +156 -8
  32. package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +185 -0
  33. package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +174 -269
  34. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +26 -0
  35. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +2060 -407
  36. package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +119 -0
  37. package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +1446 -0
  38. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +5319 -3
  39. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +191 -1
  40. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +3140 -17
  41. package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +135 -637
  42. package/runtime/scripts/amalgm-mcp/workspace/access-store.js +5 -1
  43. package/runtime/scripts/amalgm-mcp/workspace/card.js +7 -7
  44. package/runtime/scripts/amalgm-mcp/workspace/cards.js +36 -12
  45. package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +436 -173
  46. package/runtime/scripts/amalgm-mcp/workspace/content.js +32 -0
  47. package/runtime/scripts/amalgm-mcp/workspace/durable.js +45 -0
  48. package/runtime/scripts/amalgm-mcp/workspace/git.js +391 -37
  49. package/runtime/scripts/amalgm-mcp/workspace/intent.js +535 -0
  50. package/runtime/scripts/amalgm-mcp/workspace/merge.js +89 -6
  51. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +31 -0
  52. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
  53. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
  54. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +72 -0
  55. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +165 -0
  56. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +5 -0
  57. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +2 -0
  58. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +2201 -0
  59. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +19 -0
  60. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +18 -0
  61. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +18 -0
  62. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +33 -0
  63. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +47 -0
  64. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +43 -0
  65. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +114 -0
  66. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +84 -0
  67. package/runtime/scripts/amalgm-mcp/workspace/objects.js +185 -55
  68. package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +616 -168
  69. package/runtime/scripts/amalgm-mcp/workspace/provenance.js +240 -0
  70. package/runtime/scripts/amalgm-mcp/workspace/reducer.js +1979 -570
  71. package/runtime/scripts/amalgm-mcp/workspace/rest.js +49 -28
  72. package/runtime/scripts/amalgm-mcp/workspace/store.js +77 -4
  73. package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +240 -0
  74. package/runtime/scripts/amalgm-mcp/workspace/transition.js +1729 -192
  75. package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +100 -0
  76. package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +7 -1
  77. package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +73 -61
  78. package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +127 -0
  79. package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +12 -0
  80. package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +51 -6
  81. package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +125 -0
  82. package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +79 -8
  83. package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +76 -0
  84. package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +3480 -165
  85. package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +3766 -0
  86. package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +899 -71
  87. package/runtime/scripts/amalgm-mcp/workspace/wall.js +352 -497
@@ -28,6 +28,8 @@ const {
28
28
  pruneMutationJournal,
29
29
  } = require('../state/mutations');
30
30
  const { getReplicaByResourceId, registerReplica } = require('../state/replicas');
31
+ const { codeProjectOrderIsDurable } = require('../state/mutation-contracts');
32
+ const projectStream = require('../workspace/project-stream');
31
33
 
32
34
  const localInput = {
33
35
  channelId: 'doc:local-journal-test',
@@ -139,6 +141,20 @@ test('a lost local acknowledgement reuses the journal row and event', () => {
139
141
  listMutations({ database: db }).filter((mutation) => mutation.mutationId === localInput.mutationId).length,
140
142
  1,
141
143
  );
144
+ assert.deepEqual(
145
+ listMutations({
146
+ database: db,
147
+ localResource: localInput.localResource,
148
+ }).map((mutation) => mutation.mutationId),
149
+ [localInput.mutationId],
150
+ );
151
+ assert.deepEqual(
152
+ listMutations({
153
+ database: db,
154
+ localResource: 'doc:unrelated-resource',
155
+ }),
156
+ [],
157
+ );
142
158
  assert.equal(
143
159
  listEventsAfter(0).filter((event) => event.mutationId === localInput.mutationId).length,
144
160
  1,
@@ -312,6 +328,169 @@ test('the official tail reconciles a locally materialized mutation after its ack
312
328
  );
313
329
  });
314
330
 
331
+ test('an early acknowledgement cannot advance past an unapplied predecessor', () => {
332
+ const db = openLocalDb();
333
+ const resourceId = 'shr_orderedprojectionabc';
334
+ const localSecond = {
335
+ ...sharedInput,
336
+ channelId: `resource:${resourceId}`,
337
+ sharedResourceId: resourceId,
338
+ localResource: 'doc:ordered-projection-test',
339
+ mutationId: 'mut_ordered_projection_02',
340
+ operation: { kind: 'delta', yjs: 'ERIT' },
341
+ };
342
+ registerReplica({
343
+ resourceId,
344
+ localResource: localSecond.localResource,
345
+ localPath: path.join(tempRoot, 'ordered-projection.md'),
346
+ contract: localSecond.contract,
347
+ schemaVersion: localSecond.schemaVersion,
348
+ authorityEpoch: localSecond.authorityEpoch,
349
+ appliedVersion: 0,
350
+ snapshotVersion: 0,
351
+ }, { database: db });
352
+ commitLocalMutation(localSecond, {
353
+ database: db,
354
+ materialize() {},
355
+ event: eventFor(localSecond),
356
+ });
357
+
358
+ const acknowledged = acknowledgeCloudMutation(
359
+ resourceId,
360
+ localSecond.mutationId,
361
+ { version: 2, committedAt: '2026-07-14T18:00:20.000Z' },
362
+ { database: db },
363
+ );
364
+ assert.equal(acknowledged.mutation.officialCloudVersion, 2);
365
+ assert.equal(acknowledged.mutation.officialProjectedAt, null);
366
+ assert.equal(
367
+ getReplicaByResourceId(resourceId, { database: db }).appliedVersion,
368
+ 0,
369
+ 'an acknowledgement records order but does not claim that order was applied',
370
+ );
371
+
372
+ const firstEnvelope = {
373
+ resourceId,
374
+ authorityEpoch: 1,
375
+ mutationId: 'mut_ordered_projection_01',
376
+ deviceId: 'device-test-peer',
377
+ baseVersion: 0,
378
+ contract: localSecond.contract,
379
+ schemaVersion: localSecond.schemaVersion,
380
+ operationKind: 'delta',
381
+ operation: { kind: 'delta', yjs: 'FBUW' },
382
+ };
383
+ const first = journalCloudMutation({
384
+ localResource: localSecond.localResource,
385
+ version: 1,
386
+ committedAt: '2026-07-14T18:00:19.000Z',
387
+ envelope: firstEnvelope,
388
+ }, { database: db });
389
+ completeMaterializedMutation(
390
+ first.mutation.channelId,
391
+ first.mutation.mutationId,
392
+ eventFor({
393
+ ...localSecond,
394
+ mutationId: firstEnvelope.mutationId,
395
+ operation: firstEnvelope.operation,
396
+ }),
397
+ { database: db },
398
+ );
399
+ assert.equal(
400
+ getReplicaByResourceId(resourceId, { database: db }).appliedVersion,
401
+ 1,
402
+ 'applying N-1 still cannot skip the acknowledged but unapplied N',
403
+ );
404
+
405
+ const second = journalCloudMutation({
406
+ localResource: localSecond.localResource,
407
+ version: 2,
408
+ committedAt: '2026-07-14T18:00:20.000Z',
409
+ envelope: {
410
+ resourceId,
411
+ authorityEpoch: localSecond.authorityEpoch,
412
+ mutationId: localSecond.mutationId,
413
+ deviceId: localSecond.deviceId,
414
+ baseVersion: localSecond.baseCloudVersion,
415
+ contract: localSecond.contract,
416
+ schemaVersion: localSecond.schemaVersion,
417
+ operationKind: localSecond.operationKind,
418
+ operation: localSecond.operation,
419
+ },
420
+ }, { database: db });
421
+ assert.equal(second.alreadyApplied, true);
422
+ assert.ok(second.mutation.officialProjectedAt);
423
+ assert.equal(getReplicaByResourceId(resourceId, { database: db }).appliedVersion, 2);
424
+ assert.equal(
425
+ listEventsAfter(0).filter((event) => (
426
+ event.resource === localSecond.localResource
427
+ && event.mutationId === localSecond.mutationId
428
+ )).length,
429
+ 1,
430
+ 'the official confirmation does not replay an already-local non-code write',
431
+ );
432
+ });
433
+
434
+ test('a code publication is either fully outbox-ready or absent after a SQLite abort', () => {
435
+ const db = openLocalDb();
436
+ const resourceId = '018f0000-0000-7000-8000-000000000061';
437
+ const input = {
438
+ channelId: `resource:${resourceId}`,
439
+ localResource: `${projectStream.LOCAL_RESOURCE_PREFIX}${resourceId}`,
440
+ sharedResourceId: resourceId,
441
+ mutationId: 'mut_code_atomic_commit_01',
442
+ deviceId: 'device-code-atomic',
443
+ authorityEpoch: 1,
444
+ baseCloudVersion: 0,
445
+ contract: projectStream.STREAM_CONTRACT,
446
+ schemaVersion: 2,
447
+ operationKind: 'code.publish',
448
+ operation: {
449
+ kind: 'code.publish',
450
+ project: resourceId,
451
+ delta: { kind: 'test-only' },
452
+ },
453
+ };
454
+ const event = {
455
+ resource: input.localResource,
456
+ op: 'update',
457
+ id: path.join(tempRoot, 'atomic-code.json'),
458
+ source: 'test:code-atomic',
459
+ };
460
+ db.exec(`
461
+ CREATE TRIGGER abort_code_atomic_commit
462
+ BEFORE UPDATE OF mutation_state ON mutation_journal
463
+ WHEN NEW.mutation_id = 'mut_code_atomic_commit_01'
464
+ AND NEW.mutation_state = 'saved-local'
465
+ BEGIN
466
+ SELECT RAISE(ABORT, 'simulated SQLite abort');
467
+ END;
468
+ `);
469
+
470
+ assert.throws(
471
+ () => commitLocalMutation(input, { database: db, event }),
472
+ /simulated SQLite abort/,
473
+ );
474
+ assert.equal(
475
+ getMutation(input.channelId, input.mutationId, { database: db }),
476
+ null,
477
+ 'the journal insert rolls back with the outbox-ready update',
478
+ );
479
+ assert.equal(
480
+ listEventsAfter(0).filter((entry) => entry.mutationId === input.mutationId).length,
481
+ 0,
482
+ );
483
+
484
+ db.exec('DROP TRIGGER abort_code_atomic_commit');
485
+ const retried = commitLocalMutation(input, { database: db, event });
486
+ assert.equal(retried.mutation.state, 'saved-local');
487
+ assert.ok(retried.mutation.eventSeq);
488
+ assert.equal(
489
+ listEventsAfter(0).filter((entry) => entry.mutationId === input.mutationId).length,
490
+ 1,
491
+ );
492
+ });
493
+
315
494
  test('retention deletes only completed rows and preserves unresolved cloud work', () => {
316
495
  const db = openLocalDb();
317
496
  const pendingInput = {
@@ -344,3 +523,133 @@ test('retention deletes only completed rows and preserves unresolved cloud work'
344
523
  assert.equal(getMutation(pendingInput.channelId, pendingInput.mutationId, { database: db }).state, 'saved-local');
345
524
  assert.equal(getMutation(rejectedInput.channelId, rejectedInput.mutationId, { database: db }).state, 'rejected');
346
525
  });
526
+
527
+ test('retention keeps a code mutation until its official fold has another durable recovery fact', () => {
528
+ const db = openLocalDb();
529
+ const resourceId = '018f0000-0000-7000-8000-000000000060';
530
+ const file = path.join(tempRoot, 'code-prune-guard.json');
531
+ const mutationId = 'mut_code_prune_guard_01';
532
+ const deviceId = 'device-code-prune';
533
+ const oid = 'a'.repeat(40);
534
+ const chunks = [{ sha256: 'b'.repeat(64), bytes: 1 }];
535
+ const bundle = {
536
+ kind: 'bundle',
537
+ contract: projectStream.BUNDLE_CONTRACT,
538
+ resourceId,
539
+ oid,
540
+ ref: `refs/amalgm/exports/${oid}`,
541
+ checksum: require('crypto').createHash('sha256').update(JSON.stringify(chunks)).digest('hex'),
542
+ bytes: 1,
543
+ chunks,
544
+ };
545
+ const operation = {
546
+ kind: 'code.publish',
547
+ project: resourceId,
548
+ delta: {
549
+ kind: 'card.set',
550
+ card: projectStream.CARD_CONTRACT,
551
+ head: { branch: 'main', oid, detached: false },
552
+ objects: bundle,
553
+ branches: [{ name: 'main', oid, objects: bundle }],
554
+ checkpoint: {
555
+ checkpoint: projectStream.CHECKPOINT_CONTRACT,
556
+ baseOid: oid,
557
+ branch: 'main',
558
+ value: { revision: null, indexDiff: '', worktreeDiff: '' },
559
+ },
560
+ },
561
+ };
562
+ const input = {
563
+ channelId: `resource:${resourceId}`,
564
+ localResource: `${projectStream.LOCAL_RESOURCE_PREFIX}${resourceId}`,
565
+ sharedResourceId: resourceId,
566
+ mutationId,
567
+ deviceId,
568
+ authorityEpoch: 1,
569
+ baseCloudVersion: 0,
570
+ contract: projectStream.STREAM_CONTRACT,
571
+ schemaVersion: 2,
572
+ operationKind: 'code.publish',
573
+ operation,
574
+ };
575
+ projectStream.writeSnapshot(file, projectStream._test.emptySnapshot(resourceId));
576
+ registerReplica({
577
+ resourceId,
578
+ localResource: input.localResource,
579
+ localPath: file,
580
+ contract: input.contract,
581
+ schemaVersion: input.schemaVersion,
582
+ authorityEpoch: input.authorityEpoch,
583
+ appliedVersion: 0,
584
+ snapshotVersion: 0,
585
+ }, { database: db });
586
+ commitLocalMutation(input, {
587
+ database: db,
588
+ event: {
589
+ resource: input.localResource,
590
+ op: 'update',
591
+ id: file,
592
+ source: 'test:code-project-outbox',
593
+ },
594
+ });
595
+ const claimed = claimCloudMutations({ database: db })
596
+ .find((entry) => entry.mutation.mutationId === mutationId);
597
+ acknowledgeCloudMutation(
598
+ resourceId,
599
+ mutationId,
600
+ { version: 1, committedAt: '2026-07-14T19:00:01.000Z' },
601
+ { database: db },
602
+ );
603
+ db.prepare('UPDATE mutation_journal SET updated_at = ? WHERE mutation_id = ?')
604
+ .run('2026-07-13T00:00:00.000Z', mutationId);
605
+
606
+ pruneMutationJournal({
607
+ database: db,
608
+ cutoffIso: '2026-07-14T00:00:00.000Z',
609
+ batchSize: 100,
610
+ });
611
+ assert.ok(
612
+ getMutation(input.channelId, mutationId, { database: db }),
613
+ 'the journal remains while it is the only durable copy of the official number',
614
+ );
615
+ assert.equal(projectStream.readSnapshot(file, resourceId).version, 0);
616
+
617
+ projectStream.applyCloudMutation({
618
+ version: 1,
619
+ committedAt: '2026-07-14T19:00:01.000Z',
620
+ envelope: claimed.envelope,
621
+ }, { database: db });
622
+ const journalRow = db.prepare(
623
+ 'SELECT * FROM mutation_journal WHERE mutation_id = ?',
624
+ ).get(mutationId);
625
+ const canonicalSnapshot = projectStream.readSnapshot(file, resourceId);
626
+ assert.equal(
627
+ codeProjectOrderIsDurable(journalRow, { database: db, snapshotCache: new Map() }),
628
+ true,
629
+ );
630
+ fs.writeFileSync(file, `${JSON.stringify({
631
+ schemaVersion: 2,
632
+ project: resourceId,
633
+ version: 1,
634
+ card: null,
635
+ checkpoint: null,
636
+ })}\n`);
637
+ assert.equal(
638
+ codeProjectOrderIsDurable(journalRow, { database: db, snapshotCache: new Map() }),
639
+ false,
640
+ 'snapshot-shaped JSON is not a canonical verified official snapshot',
641
+ );
642
+ projectStream.writeSnapshot(file, canonicalSnapshot);
643
+ db.prepare('UPDATE mutation_journal SET updated_at = ? WHERE mutation_id = ?')
644
+ .run('2026-07-13T00:00:00.000Z', mutationId);
645
+ pruneMutationJournal({
646
+ database: db,
647
+ cutoffIso: '2026-07-14T00:00:00.000Z',
648
+ batchSize: 100,
649
+ });
650
+ assert.ok(
651
+ getMutation(input.channelId, mutationId, { database: db }),
652
+ 'an unbound snapshot alone cannot replace the local folded-intent recovery fact',
653
+ );
654
+ assert.equal(projectStream.readSnapshot(file, resourceId).version, 1);
655
+ });