amalgm 0.1.200 → 0.1.202
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/tests/workspace-checkpoint.test.js +13 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +9 -0
- package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +9 -0
- package/runtime/scripts/amalgm-mcp/workspace/reducer.js +24 -2
- package/runtime/scripts/amalgm-mcp/workspace/wall.js +4 -0
package/package.json
CHANGED
|
@@ -40,6 +40,19 @@ test.after(() => {
|
|
|
40
40
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
test('checkpoint publication waits until the reducer finishes writing both repo layers', async () => {
|
|
44
|
+
const { withMaterialization } = require('../workspace/reducer');
|
|
45
|
+
const repoKey = 'https://example.test/repo.git';
|
|
46
|
+
|
|
47
|
+
assert.equal(_test.checkpointMayPublish(repoKey), true);
|
|
48
|
+
await withMaterialization(repoKey, async () => {
|
|
49
|
+
assert.equal(_test.checkpointMayPublish(repoKey), false);
|
|
50
|
+
await Promise.resolve();
|
|
51
|
+
assert.equal(_test.checkpointMayPublish(repoKey), false);
|
|
52
|
+
});
|
|
53
|
+
assert.equal(_test.checkpointMayPublish(repoKey), true);
|
|
54
|
+
});
|
|
55
|
+
|
|
43
56
|
async function run(cwd, ...args) {
|
|
44
57
|
await execFileAsync(
|
|
45
58
|
'git',
|
|
@@ -326,6 +326,15 @@ test('side branches keep one rail operation while the checked-out card advances'
|
|
|
326
326
|
|
|
327
327
|
await run(repo, 'branch', branch);
|
|
328
328
|
await refreshRepoCards();
|
|
329
|
+
const beforeMaterialization = getDocText(project.file);
|
|
330
|
+
const blocked = await require('../workspace/reducer').withMaterialization(
|
|
331
|
+
repoKey,
|
|
332
|
+
async () => publishWall(repoKey),
|
|
333
|
+
);
|
|
334
|
+
assert.equal(blocked.published, false);
|
|
335
|
+
assert.equal(blocked.reason, 'repo-materializing');
|
|
336
|
+
assert.equal(getDocText(project.file), beforeMaterialization);
|
|
337
|
+
|
|
329
338
|
const created = publishWall(repoKey);
|
|
330
339
|
assert.equal(created.published, true);
|
|
331
340
|
let card = readWall(repoKey)[0].cards[0];
|
|
@@ -188,6 +188,10 @@ function checkpointBaseReady(card, reduced) {
|
|
|
188
188
|
);
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
function checkpointMayPublish(repoKey) {
|
|
192
|
+
return !require('./reducer').isMaterializing(repoKey);
|
|
193
|
+
}
|
|
194
|
+
|
|
191
195
|
async function publishPass(replicaId = null) {
|
|
192
196
|
const machine = machineId();
|
|
193
197
|
const run = { published: 0, skips: 0, waiting: 0, failures: 0 };
|
|
@@ -196,6 +200,10 @@ async function publishPass(replicaId = null) {
|
|
|
196
200
|
if (card.health !== 'healthy' || card.kind !== 'primary' || card.operation !== 'none') continue;
|
|
197
201
|
const repoKey = repoKeyOfCard(card);
|
|
198
202
|
if (!repoKey || !card.head?.oid) continue;
|
|
203
|
+
if (!checkpointMayPublish(repoKey)) {
|
|
204
|
+
run.waiting += 1;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
199
207
|
try {
|
|
200
208
|
const reduced = require('./reducer').readRepoState(repoKey);
|
|
201
209
|
if (!checkpointBaseReady(card, reduced)) {
|
|
@@ -376,6 +384,7 @@ module.exports = {
|
|
|
376
384
|
_test: {
|
|
377
385
|
checkpointBaseReady,
|
|
378
386
|
checkpointForSharedState,
|
|
387
|
+
checkpointMayPublish,
|
|
379
388
|
},
|
|
380
389
|
CHECKPOINT_CONTRACT,
|
|
381
390
|
MAX_INLINE_DIFF_BYTES,
|
|
@@ -50,6 +50,20 @@ const service = {
|
|
|
50
50
|
repos: new Map(),
|
|
51
51
|
lastPass: null,
|
|
52
52
|
};
|
|
53
|
+
const materializing = new Set();
|
|
54
|
+
|
|
55
|
+
function isMaterializing(repoKey) {
|
|
56
|
+
return materializing.has(repoKey);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function withMaterialization(repoKey, work) {
|
|
60
|
+
materializing.add(repoKey);
|
|
61
|
+
try {
|
|
62
|
+
return await work();
|
|
63
|
+
} finally {
|
|
64
|
+
materializing.delete(repoKey);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
53
67
|
|
|
54
68
|
function stateFile(repoKey) {
|
|
55
69
|
const digest = crypto.createHash('sha256').update(repoKey).digest('hex').slice(0, 24);
|
|
@@ -587,7 +601,10 @@ async function runReducer() {
|
|
|
587
601
|
if (!plan.card) continue;
|
|
588
602
|
try {
|
|
589
603
|
if (!local?.head?.oid) {
|
|
590
|
-
const hydrated = await
|
|
604
|
+
const hydrated = await withMaterialization(
|
|
605
|
+
repoKey,
|
|
606
|
+
() => hydrateRepo(wall.projectForRepo(repoKey), plan),
|
|
607
|
+
);
|
|
591
608
|
service.repos.set(repoKey, {
|
|
592
609
|
repoKey,
|
|
593
610
|
path: wall.projectForRepo(repoKey)?.path || local?.path || null,
|
|
@@ -627,7 +644,10 @@ async function runReducer() {
|
|
|
627
644
|
service.counters.waiting += 1;
|
|
628
645
|
continue;
|
|
629
646
|
}
|
|
630
|
-
const outcome = await
|
|
647
|
+
const outcome = await withMaterialization(
|
|
648
|
+
repoKey,
|
|
649
|
+
() => materializeRepo(refreshed, currentPlan),
|
|
650
|
+
);
|
|
631
651
|
service.repos.set(repoKey, { repoKey, path: local.path, ...outcome, at: new Date().toISOString() });
|
|
632
652
|
if (outcome.state === 'waiting') service.counters.waiting += 1;
|
|
633
653
|
if (outcome.moved) {
|
|
@@ -718,6 +738,7 @@ module.exports = {
|
|
|
718
738
|
advanceIndex,
|
|
719
739
|
captureIsOnRail,
|
|
720
740
|
hydrateRepo,
|
|
741
|
+
isMaterializing,
|
|
721
742
|
localCardMayMaterialize,
|
|
722
743
|
materializeRepo,
|
|
723
744
|
readRepoState,
|
|
@@ -727,4 +748,5 @@ module.exports = {
|
|
|
727
748
|
reducerStats,
|
|
728
749
|
startWorkspaceReducer,
|
|
729
750
|
stopWorkspaceReducer,
|
|
751
|
+
withMaterialization,
|
|
730
752
|
};
|
|
@@ -473,6 +473,10 @@ function publishProject(project, rawCards) {
|
|
|
473
473
|
service.counters.publishSkips += 1;
|
|
474
474
|
return { published: false, reason: 'project-not-bound', repoKey: project.repoKey };
|
|
475
475
|
}
|
|
476
|
+
if (require('./reducer').isMaterializing(project.repoKey)) {
|
|
477
|
+
service.counters.publishSkips += 1;
|
|
478
|
+
return { published: false, reason: 'repo-materializing', repoKey: project.repoKey };
|
|
479
|
+
}
|
|
476
480
|
const docs = require('../state/docs');
|
|
477
481
|
let current = docs.getDocText(project.file);
|
|
478
482
|
let previous = null;
|