amalgm 0.1.187 → 0.1.189
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/package.json +1 -1
- package/runtime/scripts/amalgm-mcp/state/docs.js +1 -1
- package/runtime/scripts/amalgm-mcp/state/promotions.js +1 -1
- package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +6 -1
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +31 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +3 -2
- package/runtime/scripts/amalgm-mcp/workspace/cards.js +38 -5
- package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +1 -1
- package/runtime/scripts/amalgm-mcp/workspace/wall.js +2 -19
package/package.json
CHANGED
|
@@ -341,7 +341,7 @@ function commitDocMutation(Y, entry, { mutationId, update, bytes }, source) {
|
|
|
341
341
|
localResource: entry.resource,
|
|
342
342
|
sharedResourceId: replica?.resourceId,
|
|
343
343
|
mutationId,
|
|
344
|
-
deviceId: replica ?
|
|
344
|
+
deviceId: replica ? require('../workspace/identity').machineId() : null,
|
|
345
345
|
authorityEpoch: replica?.authorityEpoch || 0,
|
|
346
346
|
baseCloudVersion: replica?.appliedVersion || 0,
|
|
347
347
|
contract: replica?.contract || 'text-yjs@1',
|
|
@@ -248,7 +248,7 @@ function acknowledgePromotion(promotionIdInput, acknowledgement, options = {}) {
|
|
|
248
248
|
`).run(
|
|
249
249
|
`resource:${replica.resourceId}`,
|
|
250
250
|
replica.resourceId,
|
|
251
|
-
|
|
251
|
+
require('../workspace/identity').machineId(),
|
|
252
252
|
replica.authorityEpoch,
|
|
253
253
|
replica.snapshotVersion,
|
|
254
254
|
now,
|
|
@@ -8,6 +8,7 @@ const path = require('path');
|
|
|
8
8
|
|
|
9
9
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'amalgm-shared-replica-test-'));
|
|
10
10
|
process.env.AMALGM_DIR = path.join(tempRoot, '.amalgm');
|
|
11
|
+
process.env.AMALGM_COMPUTER_ID = 'computer-shared-replica-test';
|
|
11
12
|
process.env.AMALGM_DEVICE_ID = 'device-shared-replica-test';
|
|
12
13
|
process.env.AMALGM_EVENT_LOG_PRUNE_INTERVAL_MS = '0';
|
|
13
14
|
|
|
@@ -142,6 +143,10 @@ test('batched surface edits retain separate IDs/versions and recipient file prec
|
|
|
142
143
|
'mut_shared_batch_001',
|
|
143
144
|
'mut_shared_batch_002',
|
|
144
145
|
]);
|
|
146
|
+
assert.deepEqual(claims.map((claim) => claim.envelope.deviceId), [
|
|
147
|
+
process.env.AMALGM_COMPUTER_ID,
|
|
148
|
+
process.env.AMALGM_COMPUTER_ID,
|
|
149
|
+
]);
|
|
145
150
|
acknowledgeCloudMutation(resourceId, 'mut_shared_batch_001', {
|
|
146
151
|
version: 1,
|
|
147
152
|
committedAt: '2026-07-14T20:00:00.000Z',
|
|
@@ -378,7 +383,7 @@ test('edits journaled during the promotion window are re-homed into the outbox',
|
|
|
378
383
|
assert.equal(rehomed.sharedResourceId, fenceResourceId);
|
|
379
384
|
assert.equal(rehomed.authorityEpoch, 1);
|
|
380
385
|
assert.equal(rehomed.baseCloudVersion, 0);
|
|
381
|
-
assert.equal(rehomed.deviceId, process.env.
|
|
386
|
+
assert.equal(rehomed.deviceId, process.env.AMALGM_COMPUTER_ID);
|
|
382
387
|
const claims = claimCloudMutations().filter((claim) => claim.envelope.resourceId === fenceResourceId);
|
|
383
388
|
assert.deepEqual(claims.map((claim) => claim.envelope.mutationId), ['mut_during_promotion1']);
|
|
384
389
|
});
|
|
@@ -341,6 +341,37 @@ test('the live service hears a commit through the git-dir doorbell', async () =>
|
|
|
341
341
|
assert.ok(repoCardServiceStats().doorbellRings >= 1);
|
|
342
342
|
});
|
|
343
343
|
|
|
344
|
+
test('the live service hears nested branch creation and deletion', async () => {
|
|
345
|
+
const repo = await makeRepo('projects/belled-refs');
|
|
346
|
+
const projects = path.join(tempRoot, 'projects');
|
|
347
|
+
upsertWorkspace({ path: projects, name: 'Projects', source: 'computer' });
|
|
348
|
+
startRepoCardService();
|
|
349
|
+
await refreshRepoCards();
|
|
350
|
+
const branch = 'storm/nested-short';
|
|
351
|
+
const before = findCard(listRepoCards(), repo);
|
|
352
|
+
|
|
353
|
+
await run(repo, 'branch', branch);
|
|
354
|
+
let deadline = Date.now() + 5_000;
|
|
355
|
+
let created = findCard(listRepoCards(), repo);
|
|
356
|
+
while (!created.refs.some((ref) => ref.name === branch) && Date.now() < deadline) {
|
|
357
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
358
|
+
created = findCard(listRepoCards(), repo);
|
|
359
|
+
}
|
|
360
|
+
assert.ok(created.refs.some((ref) => ref.name === branch));
|
|
361
|
+
|
|
362
|
+
await run(repo, 'branch', '-D', branch);
|
|
363
|
+
deadline = Date.now() + 5_000;
|
|
364
|
+
let deleted = findCard(listRepoCards(), repo);
|
|
365
|
+
while (deleted.refs.some((ref) => ref.name === branch) && Date.now() < deadline) {
|
|
366
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
367
|
+
deleted = findCard(listRepoCards(), repo);
|
|
368
|
+
}
|
|
369
|
+
stopRepoCardService();
|
|
370
|
+
|
|
371
|
+
assert.equal(deleted.refs.some((ref) => ref.name === branch), false);
|
|
372
|
+
assert.equal(deleted.generation, before.generation + 2);
|
|
373
|
+
});
|
|
374
|
+
|
|
344
375
|
test('the live service hears an uncommitted nested edit without a polling pass', async () => {
|
|
345
376
|
const repo = await makeRepo('projects/live-dirty');
|
|
346
377
|
const projects = path.join(tempRoot, 'projects');
|
|
@@ -128,7 +128,8 @@ test('a repo project carries exactly one line per machine, replaced in place', a
|
|
|
128
128
|
committedAt: '2026-07-23T00:00:00.000Z',
|
|
129
129
|
});
|
|
130
130
|
const sealed = publishWall(repoKey);
|
|
131
|
-
assert.equal(sealed.published,
|
|
131
|
+
assert.equal(sealed.published, false);
|
|
132
|
+
assert.equal(sealed.reason, 'unchanged');
|
|
132
133
|
const sealedCard = readWall(repoKey)[0].cards[0];
|
|
133
134
|
assert.equal(sealedCard.positionMutationId, first.mutationId);
|
|
134
135
|
assert.equal(sealedCard.positionVersion, 1);
|
|
@@ -176,7 +177,7 @@ test('checkpoint transitions append until the next card and carry their official
|
|
|
176
177
|
committedAt: '2026-07-23T00:00:02.000Z',
|
|
177
178
|
});
|
|
178
179
|
assert.equal(readWall(repoKey)[0].cards[0].checkpoints[0].version, 2);
|
|
179
|
-
assert.equal(publishWall(repoKey).published,
|
|
180
|
+
assert.equal(publishWall(repoKey).published, false);
|
|
180
181
|
|
|
181
182
|
fs.writeFileSync(file, `${JSON.stringify({
|
|
182
183
|
...firstCheckpoint,
|
|
@@ -413,24 +413,49 @@ function ringDoorbell(replicaId) {
|
|
|
413
413
|
bell.timer = setTimeout(() => {
|
|
414
414
|
bell.timer = null;
|
|
415
415
|
refreshRepoCard(replicaId)
|
|
416
|
-
.then(() =>
|
|
416
|
+
.then((card) => {
|
|
417
|
+
if (card) rebuildDoorbell(replicaId, card);
|
|
418
|
+
return require('./checkpoint').publishCheckpoints(replicaId);
|
|
419
|
+
})
|
|
417
420
|
.catch((error) => console.warn('[RepoCards] doorbell refresh failed:', error.message));
|
|
418
421
|
}, DOORBELL_QUIET_MS);
|
|
419
422
|
bell.timer.unref?.();
|
|
420
423
|
}
|
|
421
424
|
|
|
425
|
+
function nestedDirectories(root) {
|
|
426
|
+
const directories = [];
|
|
427
|
+
const pending = [root];
|
|
428
|
+
while (pending.length > 0) {
|
|
429
|
+
const current = pending.pop();
|
|
430
|
+
let entries;
|
|
431
|
+
try {
|
|
432
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
433
|
+
} catch {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
directories.push(current);
|
|
437
|
+
for (const entry of entries) {
|
|
438
|
+
if (entry.isDirectory()) pending.push(path.join(current, entry.name));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return directories;
|
|
442
|
+
}
|
|
443
|
+
|
|
422
444
|
function watchGitDir(card) {
|
|
423
445
|
const watchers = [];
|
|
424
446
|
const ring = () => ringDoorbell(card.replica);
|
|
425
447
|
// The git dir itself carries HEAD, index, and operation markers; logs/
|
|
426
448
|
// carries the reflog, which moves on every commit, checkout, and reset.
|
|
427
449
|
const common = card.commonGitDir || card.gitDir;
|
|
428
|
-
|
|
450
|
+
const direct = [
|
|
429
451
|
card.gitDir,
|
|
430
452
|
path.join(card.gitDir, 'logs'),
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
453
|
+
];
|
|
454
|
+
const refDirectories = [
|
|
455
|
+
...nestedDirectories(path.join(common, 'refs', 'heads')),
|
|
456
|
+
...nestedDirectories(path.join(common, 'logs', 'refs', 'heads')),
|
|
457
|
+
];
|
|
458
|
+
for (const dir of new Set([...direct, ...refDirectories])) {
|
|
434
459
|
try {
|
|
435
460
|
const watcher = fs.watch(dir, ring);
|
|
436
461
|
watcher.on('error', () => watcher.close());
|
|
@@ -455,6 +480,14 @@ function watchGitDir(card) {
|
|
|
455
480
|
return watchers;
|
|
456
481
|
}
|
|
457
482
|
|
|
483
|
+
function rebuildDoorbell(replicaId, card) {
|
|
484
|
+
const bell = service.doorbells.get(replicaId);
|
|
485
|
+
if (!bell) return;
|
|
486
|
+
const previous = bell.watchers;
|
|
487
|
+
bell.watchers = watchGitDir(card);
|
|
488
|
+
for (const watcher of previous) watcher.close();
|
|
489
|
+
}
|
|
490
|
+
|
|
458
491
|
function dropDoorbell(replicaId) {
|
|
459
492
|
const bell = service.doorbells.get(replicaId);
|
|
460
493
|
if (!bell) return;
|
|
@@ -372,7 +372,7 @@ function commitTreeOperation(input, options = {}) {
|
|
|
372
372
|
localResource,
|
|
373
373
|
sharedResourceId: replica?.resourceId,
|
|
374
374
|
mutationId,
|
|
375
|
-
deviceId: replica ?
|
|
375
|
+
deviceId: replica ? require('./identity').machineId() : null,
|
|
376
376
|
authorityEpoch: replica?.authorityEpoch || 0,
|
|
377
377
|
baseCloudVersion: replica?.appliedVersion || 0,
|
|
378
378
|
contract: 'workspace-tree@1',
|
|
@@ -249,15 +249,11 @@ function stampBranches(refs, previousBranches, mutationId, resourceId) {
|
|
|
249
249
|
&& before.mutationId
|
|
250
250
|
&& (validVersion(before.version) || knownMutation(resourceId, before.mutationId));
|
|
251
251
|
const mutation = reusable ? before.mutationId : mutationId;
|
|
252
|
-
const version = reusable
|
|
253
|
-
? (validVersion(before.version) ? before.version : officialVersion(resourceId, mutation))
|
|
254
|
-
: null;
|
|
255
252
|
return {
|
|
256
253
|
name,
|
|
257
254
|
oid,
|
|
258
255
|
...(observed?.objects ? { objects: observed.objects } : {}),
|
|
259
256
|
mutationId: mutation,
|
|
260
|
-
...(validVersion(version) ? { version } : {}),
|
|
261
257
|
};
|
|
262
258
|
});
|
|
263
259
|
}
|
|
@@ -294,15 +290,8 @@ function stampCards(cards, previousCards, mutationId, project) {
|
|
|
294
290
|
&& (
|
|
295
291
|
validVersion(previous.positionVersion)
|
|
296
292
|
|| knownMutation(project.resourceId, previous.positionMutationId)
|
|
297
|
-
|
|
293
|
+
);
|
|
298
294
|
const positionMutationId = reusablePosition ? previous.positionMutationId : mutationId;
|
|
299
|
-
const positionVersion = reusablePosition
|
|
300
|
-
? (
|
|
301
|
-
validVersion(previous.positionVersion)
|
|
302
|
-
? previous.positionVersion
|
|
303
|
-
: officialVersion(project.resourceId, positionMutationId)
|
|
304
|
-
)
|
|
305
|
-
: null;
|
|
306
295
|
const checkpoint = readCheckpoint(machineId(), project.repoKey);
|
|
307
296
|
const currentCheckpoint = checkpoint
|
|
308
297
|
&& checkpoint.baseOid === card.head?.oid
|
|
@@ -312,12 +301,7 @@ function stampCards(cards, previousCards, mutationId, project) {
|
|
|
312
301
|
const sameBase = previous?.head?.oid === card.head?.oid
|
|
313
302
|
&& previous?.head?.branch === card.head?.branch;
|
|
314
303
|
const history = sameBase && Array.isArray(previous?.checkpoints)
|
|
315
|
-
? previous.checkpoints.map((entry) =>
|
|
316
|
-
const version = validVersion(entry.version)
|
|
317
|
-
? entry.version
|
|
318
|
-
: officialVersion(project.resourceId, entry.mutationId);
|
|
319
|
-
return validVersion(version) ? { ...entry, version } : entry;
|
|
320
|
-
})
|
|
304
|
+
? previous.checkpoints.map(({ version: _version, ...entry }) => entry)
|
|
321
305
|
: [];
|
|
322
306
|
const last = history.at(-1);
|
|
323
307
|
const checkpoints = currentCheckpoint
|
|
@@ -328,7 +312,6 @@ function stampCards(cards, previousCards, mutationId, project) {
|
|
|
328
312
|
return {
|
|
329
313
|
...sharedCard(card),
|
|
330
314
|
positionMutationId,
|
|
331
|
-
...(validVersion(positionVersion) ? { positionVersion } : {}),
|
|
332
315
|
branches: stampBranches(
|
|
333
316
|
card.refs,
|
|
334
317
|
sameBase ? previous?.branches : [],
|