amalgm 0.1.185 → 0.1.186

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.185",
3
+ "version": "0.1.186",
4
4
  "description": "Amalgm local computer runtime: login, MCP, chat, events, previews, and tunnels.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -27,6 +27,8 @@ const { captureCheckpoint, captureWorktreeDiff } = require('../workspace/git');
27
27
  const merge = require('../workspace/merge');
28
28
  const { applyPatch } = require('../workspace/transition');
29
29
  const {
30
+ _test,
31
+ CHECKPOINT_CONTRACT,
30
32
  checkpointFile,
31
33
  checkpointValueReady,
32
34
  readCheckpointValue,
@@ -180,3 +182,37 @@ test('an uploading checkpoint payload is not published as a transition', () => {
180
182
  assert.equal(readCheckpointValue('c'.repeat(40), pending).state, 'payload-pending');
181
183
  assert.equal(checkpointValueReady({ revision: null, indexDiff: '', worktreeDiff: '' }), true);
182
184
  });
185
+
186
+ test('a clean losing commit travels as parked metadata on a no-op checkpoint', () => {
187
+ const sharedValue = { revision: null, indexDiff: '', worktreeDiff: '' };
188
+ const parked = [{
189
+ oid: 'a'.repeat(40),
190
+ ref: `refs/amalgm/parked/${'a'.repeat(40)}`,
191
+ objects: { kind: 'bundle', checksum: 'b'.repeat(64) },
192
+ }];
193
+ const checkpoint = _test.checkpointForSharedState({
194
+ checkpoint: CHECKPOINT_CONTRACT,
195
+ machine: 'machine-a',
196
+ repoKey: 'root:test',
197
+ branch: 'old',
198
+ baseOid: 'c'.repeat(40),
199
+ from: sharedValue,
200
+ to: sharedValue,
201
+ parked: [],
202
+ }, {
203
+ machine: 'machine-a',
204
+ repoKey: 'root:test',
205
+ branch: 'main',
206
+ baseOid: 'd'.repeat(40),
207
+ sharedValue,
208
+ parked,
209
+ capturedAt: '2026-07-24T00:00:00.000Z',
210
+ });
211
+
212
+ assert.equal(checkpoint.checkpoint, CHECKPOINT_CONTRACT);
213
+ assert.equal(checkpoint.baseOid, 'd'.repeat(40));
214
+ assert.equal(checkpoint.branch, 'main');
215
+ assert.deepEqual(checkpoint.from, sharedValue);
216
+ assert.deepEqual(checkpoint.to, sharedValue);
217
+ assert.deepEqual(checkpoint.parked, parked);
218
+ });
@@ -450,6 +450,42 @@ test('a losing commit is parked, the winning card lands exactly, and losing work
450
450
  assert.deepEqual(readRepoState('root:test').parked.map((entry) => entry.oid), [losingOid]);
451
451
  });
452
452
 
453
+ test('a local commit made after the verified card snapshot is never parked or overwritten', async () => {
454
+ const { repo, oid: baseOid } = await makeRepo('card-snapshot-race');
455
+ const verifiedCard = card(repo, baseOid, 1);
456
+
457
+ await git(repo, 'switch', '--detach', baseOid);
458
+ fs.writeFileSync(path.join(repo, 'winner.txt'), 'winning commit work\n');
459
+ await git(repo, 'add', '.');
460
+ await git(repo, 'commit', '-m', 'remote winning commit');
461
+ const winningOid = (await git(repo, 'rev-parse', 'HEAD')).trim();
462
+ await git(repo, 'switch', '-C', 'main', baseOid);
463
+
464
+ fs.writeFileSync(path.join(repo, 'late-local.txt'), 'created after verification\n');
465
+ await git(repo, 'add', '.');
466
+ await git(repo, 'commit', '-m', 'late local commit');
467
+ const lateLocalOid = (await git(repo, 'rev-parse', 'HEAD')).trim();
468
+
469
+ const outcome = await materializeRepo(verifiedCard, {
470
+ repoKey: 'root:test',
471
+ card: {
472
+ ...verifiedCard,
473
+ head: { branch: 'main', oid: winningOid, detached: false },
474
+ positionVersion: 2,
475
+ positionMutationId: 'mut_card_snapshot_race',
476
+ },
477
+ transitions: [],
478
+ parked: [],
479
+ });
480
+
481
+ assert.equal(outcome.state, 'waiting');
482
+ assert.equal(outcome.reason, 'local-card-changed-after-verification');
483
+ assert.equal((await git(repo, 'rev-parse', 'HEAD')).trim(), lateLocalOid);
484
+ await assert.rejects(
485
+ git(repo, 'rev-parse', `refs/amalgm/parked/${lateLocalOid}`),
486
+ );
487
+ });
488
+
453
489
  test('a new card folds checkpoint work already on the rail instead of replaying it as local work', async () => {
454
490
  const { repo, oid: baseOid } = await makeRepo('card-folds-shared-checkpoint');
455
491
  const sharedPatch = await patchFor(repo, { 'a.txt': 'shared checkpoint\n' });
@@ -123,6 +123,32 @@ function checkpointValueReady(value) {
123
123
  return !value?.payload || value.payload.kind === 'chunks';
124
124
  }
125
125
 
126
+ function checkpointForSharedState(existing, {
127
+ machine,
128
+ repoKey,
129
+ branch,
130
+ baseOid,
131
+ sharedValue,
132
+ parked,
133
+ capturedAt = new Date().toISOString(),
134
+ }) {
135
+ if (existing?.baseOid === baseOid && existing.branch === branch) {
136
+ return { ...existing, parked };
137
+ }
138
+ if (parked.length === 0) return null;
139
+ return {
140
+ checkpoint: CHECKPOINT_CONTRACT,
141
+ machine,
142
+ repoKey,
143
+ branch,
144
+ baseOid,
145
+ from: sharedValue,
146
+ to: sharedValue,
147
+ parked,
148
+ capturedAt,
149
+ };
150
+ }
151
+
126
152
  function readCheckpointValue(baseOid, value) {
127
153
  let indexDiff = typeof value?.indexDiff === 'string' ? value.indexDiff : '';
128
154
  let worktreeDiff = typeof value?.worktreeDiff === 'string' ? value.worktreeDiff : '';
@@ -191,9 +217,23 @@ async function publishPass(replicaId = null) {
191
217
 
192
218
  // Exact equality means the working tree merely materialized the shared
193
219
  // checkpoint. Keep the writer's last transition because it remains part
194
- // of the reducer input, but do not invent an echo operation.
220
+ // of the reducer input. A newly parked commit still rides as metadata on
221
+ // a no-op transition; the working tree itself is never echoed.
195
222
  if (sharedRevision === localRevision) {
196
- const refreshed = existing ? { ...existing, parked } : null;
223
+ const refreshed = checkpointForSharedState(existing, {
224
+ machine,
225
+ repoKey,
226
+ branch: card.head.branch,
227
+ baseOid: card.head.oid,
228
+ sharedValue: checkpointValue(
229
+ machine,
230
+ repoKey,
231
+ card.head.oid,
232
+ reduced.indexDiff,
233
+ reduced.worktreeDiff,
234
+ ),
235
+ parked,
236
+ });
197
237
  if (refreshed && !sameTransition(existing, refreshed)) {
198
238
  fs.writeFileSync(file, `${JSON.stringify(refreshed)}\n`);
199
239
  appendStateEvent({
@@ -331,6 +371,9 @@ function checkpointStats() {
331
371
  }
332
372
 
333
373
  module.exports = {
374
+ _test: {
375
+ checkpointForSharedState,
376
+ },
334
377
  CHECKPOINT_CONTRACT,
335
378
  MAX_INLINE_DIFF_BYTES,
336
379
  checkpointFile,
@@ -206,6 +206,12 @@ async function moveToWinningCard(localCard, targetCard) {
206
206
  const current = await headAndDirty(localCard.path);
207
207
  const operation = await operationState(localCard.path);
208
208
  if (operation !== 'none') return { state: 'waiting', reason: 'local-operation-in-flight' };
209
+ if (
210
+ current.head.oid !== localCard.head?.oid
211
+ || current.head.branch !== localCard.head?.branch
212
+ ) {
213
+ return { state: 'waiting', reason: 'local-card-changed-after-verification' };
214
+ }
209
215
  if (
210
216
  current.head.oid === targetCard.head?.oid
211
217
  && current.head.branch === targetCard.head?.branch
@@ -448,7 +454,7 @@ function captureIsOnRail(plan, captured, localMachine = machineId()) {
448
454
  }
449
455
 
450
456
  async function materializeRepo(localCardInput, plan) {
451
- const refreshed = await refreshRepoCard(localCardInput.replica) || localCardInput;
457
+ const refreshed = localCardInput;
452
458
  const cardResult = await moveToWinningCard(refreshed, plan.card);
453
459
  if (cardResult.state !== 'ready') return cardResult;
454
460