amalgm 0.1.235 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amalgm",
3
- "version": "0.1.235",
3
+ "version": "0.1.236",
4
4
  "description": "Amalgm local computer runtime: login, MCP, chat, events, previews, and tunnels.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -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({ repoKey, appliedThrough: version })}\n`);
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,
@@ -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
- return state?.repoKey === repoKey ? state : null;
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 reducedState = readRepoState(repoKey);
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 previous = readRepoState(plan.repoKey);
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));