amalgm 0.1.234 → 0.1.236
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-cards.test.js +16 -2
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +15 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +6 -1
- package/runtime/scripts/amalgm-mcp/workspace/card.js +8 -5
- package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +4 -1
- package/runtime/scripts/amalgm-mcp/workspace/reducer.js +11 -6
- package/runtime/scripts/amalgm-mcp/workspace/wall.js +2 -2
package/package.json
CHANGED
|
@@ -440,8 +440,22 @@ test('unchanged commits reuse their card object source and one new oid is resolv
|
|
|
440
440
|
|
|
441
441
|
test('only complete object sources are reusable', () => {
|
|
442
442
|
assert.deepEqual(
|
|
443
|
-
cardTest.reusableObjectSource(
|
|
444
|
-
|
|
443
|
+
cardTest.reusableObjectSource(
|
|
444
|
+
'a',
|
|
445
|
+
'a',
|
|
446
|
+
{ kind: 'bundle', resourceId: 'shr_project_a', checksum: 'ready' },
|
|
447
|
+
'shr_project_a',
|
|
448
|
+
),
|
|
449
|
+
{ kind: 'bundle', resourceId: 'shr_project_a', checksum: 'ready' },
|
|
450
|
+
);
|
|
451
|
+
assert.equal(
|
|
452
|
+
cardTest.reusableObjectSource(
|
|
453
|
+
'a',
|
|
454
|
+
'a',
|
|
455
|
+
{ kind: 'bundle', resourceId: 'shr_project_old', checksum: 'ready' },
|
|
456
|
+
'shr_project_new',
|
|
457
|
+
),
|
|
458
|
+
null,
|
|
445
459
|
);
|
|
446
460
|
assert.equal(cardTest.reusableObjectSource('a', 'b', { kind: 'remote' }), null);
|
|
447
461
|
assert.equal(cardTest.reusableObjectSource('a', 'a', { kind: 'pending' }), null);
|
|
@@ -59,6 +59,21 @@ test('a hydration failure still has a project path before a local card exists',
|
|
|
59
59
|
assert.equal(pathForRepo({ path: '/projects/local' }, { path: '/projects/fresh' }), '/projects/local');
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
test('a reducer cursor belongs only to the rail that created it', () => {
|
|
63
|
+
const repoKey = 'root:rail-scoped-cursor';
|
|
64
|
+
const digest = require('crypto').createHash('sha256').update(repoKey).digest('hex').slice(0, 24);
|
|
65
|
+
const file = path.join(process.env.AMALGM_DIR, 'state', 'repo-reducer', `${digest}.json`);
|
|
66
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
67
|
+
fs.writeFileSync(file, `${JSON.stringify({
|
|
68
|
+
repoKey,
|
|
69
|
+
resourceId: 'shr_old_project_rail',
|
|
70
|
+
appliedThrough: 2764,
|
|
71
|
+
})}\n`);
|
|
72
|
+
|
|
73
|
+
assert.equal(readRepoState(repoKey, 'shr_old_project_rail').appliedThrough, 2764);
|
|
74
|
+
assert.equal(readRepoState(repoKey, 'shr_new_project_rail'), null);
|
|
75
|
+
});
|
|
76
|
+
|
|
62
77
|
test.after(() => {
|
|
63
78
|
closeLocalDb();
|
|
64
79
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
@@ -59,7 +59,11 @@ function markReducerApplied(version) {
|
|
|
59
59
|
const digest = crypto.createHash('sha256').update(repoKey).digest('hex').slice(0, 24);
|
|
60
60
|
const file = path.join(process.env.AMALGM_DIR, 'state', 'repo-reducer', `${digest}.json`);
|
|
61
61
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
62
|
-
fs.writeFileSync(file, `${JSON.stringify({
|
|
62
|
+
fs.writeFileSync(file, `${JSON.stringify({
|
|
63
|
+
repoKey,
|
|
64
|
+
resourceId: project.resourceId,
|
|
65
|
+
appliedThrough: version,
|
|
66
|
+
})}\n`);
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
function settleProject() {
|
|
@@ -450,6 +454,7 @@ test('a checkpoint base replaces history already covered by the applied rail', a
|
|
|
450
454
|
);
|
|
451
455
|
fs.writeFileSync(reducerFile, `${JSON.stringify({
|
|
452
456
|
repoKey,
|
|
457
|
+
resourceId: project.resourceId,
|
|
453
458
|
baseOid: card.head.oid,
|
|
454
459
|
branch: card.head.branch,
|
|
455
460
|
appliedThrough: 2,
|
|
@@ -60,10 +60,10 @@ function cardKind(classified) {
|
|
|
60
60
|
return classified.linked ? 'linked-worktree' : 'primary';
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function reusableObjectSource(previousOid, currentOid, source) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
function reusableObjectSource(previousOid, currentOid, source, resourceId) {
|
|
64
|
+
if (previousOid !== currentOid) return null;
|
|
65
|
+
if (source?.kind === 'remote') return source;
|
|
66
|
+
return source?.kind === 'bundle' && source.resourceId === resourceId ? source : null;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
async function observeWorktree(classified, previousCard = null) {
|
|
@@ -83,6 +83,9 @@ async function observeWorktree(classified, previousCard = null) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
const repoIdentity = remote === null ? null : remoteIdentity(remote);
|
|
86
|
+
const resourceId = repoIdentity
|
|
87
|
+
? require('./wall').projectForRepo(repoIdentity)?.resourceId
|
|
88
|
+
: null;
|
|
86
89
|
const previous = previousCard?.replica === replica.replicaId
|
|
87
90
|
&& previousCard.repo?.remote === repoIdentity
|
|
88
91
|
? previousCard
|
|
@@ -92,7 +95,7 @@ async function observeWorktree(classified, previousCard = null) {
|
|
|
92
95
|
const sourceFor = (oid, priorOid, priorSource) => {
|
|
93
96
|
if (!oid || !repoIdentity) return null;
|
|
94
97
|
if (objectSources.has(oid)) return objectSources.get(oid);
|
|
95
|
-
const source = reusableObjectSource(priorOid, oid, priorSource)
|
|
98
|
+
const source = reusableObjectSource(priorOid, oid, priorSource, resourceId)
|
|
96
99
|
|| require('./objects').objectSourceFor(worktreeRoot, repoIdentity, oid);
|
|
97
100
|
objectSources.set(oid, source);
|
|
98
101
|
return source;
|
|
@@ -383,8 +383,11 @@ async function publishPass(replicaId = null) {
|
|
|
383
383
|
continue;
|
|
384
384
|
}
|
|
385
385
|
try {
|
|
386
|
-
const reduced = require('./reducer').readRepoState(repoKey);
|
|
387
386
|
const wall = require('./wall');
|
|
387
|
+
const reduced = require('./reducer').readRepoState(
|
|
388
|
+
repoKey,
|
|
389
|
+
wall.projectForRepo(repoKey)?.resourceId,
|
|
390
|
+
);
|
|
388
391
|
if (!checkpointBaseReady(card, reduced, wall.publicationStatus(repoKey, reduced))) {
|
|
389
392
|
run.waiting += 1;
|
|
390
393
|
continue;
|
|
@@ -72,10 +72,12 @@ function stateFile(repoKey) {
|
|
|
72
72
|
return path.join(STATE_DIR, `${digest}.json`);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function readRepoState(repoKey) {
|
|
75
|
+
function readRepoState(repoKey, resourceId = null) {
|
|
76
76
|
try {
|
|
77
77
|
const state = JSON.parse(fs.readFileSync(stateFile(repoKey), 'utf8'));
|
|
78
|
-
|
|
78
|
+
if (state?.repoKey !== repoKey) return null;
|
|
79
|
+
if (resourceId && state.resourceId !== resourceId) return null;
|
|
80
|
+
return state;
|
|
79
81
|
} catch {
|
|
80
82
|
return null;
|
|
81
83
|
}
|
|
@@ -90,7 +92,7 @@ function repoStateIdentity(state) {
|
|
|
90
92
|
function persistRepoState(repoKey, state) {
|
|
91
93
|
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
92
94
|
const next = { repoKey, ...state, updatedAt: new Date().toISOString() };
|
|
93
|
-
const previous = readRepoState(repoKey);
|
|
95
|
+
const previous = readRepoState(repoKey, state?.resourceId || null);
|
|
94
96
|
if (repoStateIdentity(previous) === repoStateIdentity(next)) {
|
|
95
97
|
return { state: previous, changed: false };
|
|
96
98
|
}
|
|
@@ -349,7 +351,8 @@ async function moveToWinningCard(localCard, targetCard) {
|
|
|
349
351
|
return { state: 'waiting', reason: 'local-state-moving' };
|
|
350
352
|
}
|
|
351
353
|
const repoKey = repoKeyOfCard(localCard);
|
|
352
|
-
const
|
|
354
|
+
const resourceId = require('./wall').projectForRepo(repoKey)?.resourceId;
|
|
355
|
+
const reducedState = readRepoState(repoKey, resourceId);
|
|
353
356
|
const capturedOnRail = reducedState?.baseOid === current.head.oid
|
|
354
357
|
&& reducedState.checkpointRevision === revisionOf(
|
|
355
358
|
current.head.oid,
|
|
@@ -590,7 +593,8 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
590
593
|
const localCard = cardResult.moved
|
|
591
594
|
? await refreshRepoCard(refreshed.replica) || refreshed
|
|
592
595
|
: refreshed;
|
|
593
|
-
const
|
|
596
|
+
const project = require('./wall').projectForRepo(plan.repoKey);
|
|
597
|
+
const previous = readRepoState(plan.repoKey, project?.resourceId);
|
|
594
598
|
const parked = parkedForState(
|
|
595
599
|
previous?.parked,
|
|
596
600
|
plan.parked,
|
|
@@ -604,6 +608,7 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
604
608
|
// bundle, so the next pass cannot replace unnumbered local work.
|
|
605
609
|
const persisted = persistRepoState(plan.repoKey, {
|
|
606
610
|
...(previous || {}),
|
|
611
|
+
resourceId: project?.resourceId || null,
|
|
607
612
|
path: localCard.path,
|
|
608
613
|
replica: localCard.replica,
|
|
609
614
|
parked,
|
|
@@ -681,7 +686,6 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
681
686
|
const captured = await captureCheckpoint(localCard.path);
|
|
682
687
|
const wall = require('./wall');
|
|
683
688
|
const localCheckpoint = readCheckpoint(machineId(), plan.repoKey);
|
|
684
|
-
const project = wall.projectForRepo(plan.repoKey);
|
|
685
689
|
const acknowledged = localCheckpoint && {
|
|
686
690
|
...localCheckpoint,
|
|
687
691
|
version: wall.mutationVersion(
|
|
@@ -716,6 +720,7 @@ async function materializeRepo(localCardInput, plan, appliedThrough = null) {
|
|
|
716
720
|
const indexResult = await advanceIndex(localCard.path, currentIndex, reducedLocalIndex.tree);
|
|
717
721
|
if (indexResult.state !== 'ready') return indexResult;
|
|
718
722
|
const persisted = persistRepoState(plan.repoKey, {
|
|
723
|
+
resourceId: project?.resourceId || null,
|
|
719
724
|
path: localCard.path,
|
|
720
725
|
replica: localCard.replica,
|
|
721
726
|
branch: stateBranch,
|
|
@@ -138,7 +138,7 @@ function publicationStatus(repoKey, reduced = undefined) {
|
|
|
138
138
|
const replica = project ? wallReplica(project) : null;
|
|
139
139
|
const receivedThrough = replica?.appliedVersion ?? null;
|
|
140
140
|
const baseline = reduced === undefined
|
|
141
|
-
? require('./reducer').readRepoState(repoKey)
|
|
141
|
+
? require('./reducer').readRepoState(repoKey, project?.resourceId)
|
|
142
142
|
: reduced;
|
|
143
143
|
const appliedThrough = Number.isInteger(baseline?.appliedThrough)
|
|
144
144
|
? baseline.appliedThrough
|
|
@@ -559,7 +559,7 @@ function publishProject(project, rawCards) {
|
|
|
559
559
|
|
|
560
560
|
const reducer = require('./reducer');
|
|
561
561
|
const sharedPlan = reducer.reduceWall(readProjectWall(project)).get(project.repoKey);
|
|
562
|
-
const baseline = reducer.readRepoState(project.repoKey);
|
|
562
|
+
const baseline = reducer.readRepoState(project.repoKey, project.resourceId);
|
|
563
563
|
const previousCards = (previousEntry?.cards || [])
|
|
564
564
|
.map((card) => sealCardVersions(project, card, previousEntry));
|
|
565
565
|
const previousReplicas = new Set(previousCards.map((card) => card.replica));
|