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.
- package/lib/shared-realtime-tunnel.js +22 -14
- package/package.json +1 -1
- package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +68 -20
- package/runtime/scripts/amalgm-mcp/project-context/store.js +87 -19
- package/runtime/scripts/amalgm-mcp/state/attachments.js +40 -12
- package/runtime/scripts/amalgm-mcp/state/db.js +216 -17
- package/runtime/scripts/amalgm-mcp/state/doc-disk.js +550 -0
- package/runtime/scripts/amalgm-mcp/state/docs.js +1101 -301
- package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +99 -0
- package/runtime/scripts/amalgm-mcp/state/mutations.js +507 -130
- package/runtime/scripts/amalgm-mcp/state/promotions.js +21 -1
- package/runtime/scripts/amalgm-mcp/state/replicas.js +134 -13
- package/runtime/scripts/amalgm-mcp/state/shared-rest.js +14 -8
- package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +193 -0
- package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +313 -94
- package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +1 -0
- package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +32 -17
- package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +84 -0
- package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +376 -13
- package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +309 -0
- package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +758 -30
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +72 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +33 -6
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +27 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +334 -191
- package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +24 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +1999 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +364 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +81 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +114 -8
- package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +185 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +174 -269
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +26 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +2060 -407
- package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +119 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +1446 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +5319 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +191 -1
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +3140 -17
- package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +135 -637
- package/runtime/scripts/amalgm-mcp/workspace/access-store.js +5 -1
- package/runtime/scripts/amalgm-mcp/workspace/card.js +7 -7
- package/runtime/scripts/amalgm-mcp/workspace/cards.js +36 -12
- package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +436 -173
- package/runtime/scripts/amalgm-mcp/workspace/content.js +32 -0
- package/runtime/scripts/amalgm-mcp/workspace/durable.js +45 -0
- package/runtime/scripts/amalgm-mcp/workspace/git.js +391 -37
- package/runtime/scripts/amalgm-mcp/workspace/intent.js +535 -0
- package/runtime/scripts/amalgm-mcp/workspace/merge.js +89 -6
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +31 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +72 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +165 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +5 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +2 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +2201 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +19 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +18 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +18 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +33 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +47 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +43 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +114 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +84 -0
- package/runtime/scripts/amalgm-mcp/workspace/objects.js +119 -54
- package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +616 -168
- package/runtime/scripts/amalgm-mcp/workspace/provenance.js +240 -0
- package/runtime/scripts/amalgm-mcp/workspace/reducer.js +1979 -570
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +49 -28
- package/runtime/scripts/amalgm-mcp/workspace/store.js +77 -4
- package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +240 -0
- package/runtime/scripts/amalgm-mcp/workspace/transition.js +1729 -192
- package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +100 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +7 -1
- package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +73 -61
- package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +127 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +12 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +51 -6
- package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +125 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +79 -8
- package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +76 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +3480 -165
- package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +3766 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +899 -71
- 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
|
+
});
|