@ricsam/r5d-worker 0.0.136 → 0.0.137

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.
@@ -18,34 +18,49 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var workspace_hydration_ledger_exports = {};
20
20
  __export(workspace_hydration_ledger_exports, {
21
+ WorkspaceHydrationLedger: () => WorkspaceHydrationLedger,
21
22
  WorkspaceHydrationPreBlobLedger: () => WorkspaceHydrationPreBlobLedger
22
23
  });
23
24
  module.exports = __toCommonJS(workspace_hydration_ledger_exports);
24
- class WorkspaceHydrationPreBlobLedger {
25
+ class WorkspaceHydrationLedger {
25
26
  mounts = /* @__PURE__ */ new Map();
26
27
  static key(mount) {
27
28
  return `${mount.hydrationIncarnationKey}\0${mount.id}`;
28
29
  }
29
- /** Blob ids by mount-relative path for the mount, or an empty map. */
30
+ /** Entries by mount-relative path for the mount, or an empty map. */
31
+ entries(mount) {
32
+ return this.mounts.get(WorkspaceHydrationLedger.key(mount)) ?? /* @__PURE__ */ new Map();
33
+ }
34
+ /** Blob ids by mount-relative path for the mount (the §3.3 detector's input), or an empty map. */
30
35
  preBlobs(mount) {
31
- return this.mounts.get(WorkspaceHydrationPreBlobLedger.key(mount)) ?? /* @__PURE__ */ new Map();
36
+ return new Map([...this.entries(mount)].map(([relativePath, entry]) => [relativePath, entry.preBlob]));
32
37
  }
33
- /** Record the pre-hydration blob of every path a merge hydration replaced; earlier entries for other paths are kept. */
34
- record(mount, preBlobs) {
35
- const entries = preBlobs instanceof Map ? [...preBlobs] : Object.entries(preBlobs);
36
- if (entries.length === 0) return;
37
- const key = WorkspaceHydrationPreBlobLedger.key(mount);
38
+ /**
39
+ * Record what a merge hydration replaced; earlier entries for other paths
40
+ * are kept. A path that already carries a live entry keeps its `preBlob`
41
+ * while the new hydration also ran across a live process: the process's
42
+ * read predates both hydrations, so what it read is still the first blob.
43
+ */
44
+ record(mount, entries, options = {}) {
45
+ const incoming = entries instanceof Map ? [...entries] : Object.entries(entries);
46
+ if (incoming.length === 0) return;
47
+ const replace = new Set(options.replacePreBlobFor ?? []);
48
+ const key = WorkspaceHydrationLedger.key(mount);
38
49
  const current = this.mounts.get(key) ?? /* @__PURE__ */ new Map();
39
- for (const [relativePath, blob] of entries) current.set(relativePath, blob);
50
+ for (const [relativePath, entry] of incoming) {
51
+ const previous = current.get(relativePath);
52
+ const preBlob = previous && previous.processLiveAcrossHydration && entry.processLiveAcrossHydration && !replace.has(relativePath) ? previous.preBlob : entry.preBlob;
53
+ current.set(relativePath, { ...entry, preBlob });
54
+ }
40
55
  this.mounts.set(key, current);
41
56
  }
42
57
  /** Forget the mount: its checkout was rewritten by something this ledger did not observe. */
43
58
  clearMount(mount) {
44
- this.mounts.delete(WorkspaceHydrationPreBlobLedger.key(mount));
59
+ this.mounts.delete(WorkspaceHydrationLedger.key(mount));
45
60
  }
46
61
  /** Forget paths whose checkout content moved on from the hydrated bytes: a later return to the old bytes is a deliberate edit. */
47
62
  forget(mount, paths) {
48
- const key = WorkspaceHydrationPreBlobLedger.key(mount);
63
+ const key = WorkspaceHydrationLedger.key(mount);
49
64
  const current = this.mounts.get(key);
50
65
  if (!current) return;
51
66
  for (const relativePath of paths) current.delete(relativePath);
@@ -60,7 +75,9 @@ class WorkspaceHydrationPreBlobLedger {
60
75
  return total;
61
76
  }
62
77
  }
78
+ const WorkspaceHydrationPreBlobLedger = WorkspaceHydrationLedger;
63
79
  // Annotate the CommonJS export names for ESM import in node:
64
80
  0 && (module.exports = {
81
+ WorkspaceHydrationLedger,
65
82
  WorkspaceHydrationPreBlobLedger
66
83
  });
@@ -120,6 +120,8 @@ function decideWorkspaceHydrationMerge(input) {
120
120
  const overrides = /* @__PURE__ */ new Map();
121
121
  const expectations = /* @__PURE__ */ new Map();
122
122
  const preBlobs = /* @__PURE__ */ new Map();
123
+ const postBlobs = /* @__PURE__ */ new Map();
124
+ const blobsToStore = /* @__PURE__ */ new Map();
123
125
  const mergedPaths = [];
124
126
  const keptPaths = /* @__PURE__ */ new Set();
125
127
  const desiredForIndex = new Map(desired);
@@ -172,13 +174,17 @@ function decideWorkspaceHydrationMerge(input) {
172
174
  remove.add(relativePath);
173
175
  expectations.set(relativePath, oursStat);
174
176
  }
175
- if (change.base && isBlobMode(change.base.mode)) preBlobs.set(relativePath, change.base.objectId);
177
+ if (change.base && isBlobMode(change.base.mode)) {
178
+ preBlobs.set(relativePath, change.base.objectId);
179
+ postBlobs.set(relativePath, null);
180
+ }
176
181
  continue;
177
182
  }
178
183
  write.add(relativePath);
179
184
  expectations.set(relativePath, oursStat ?? currentTargetStat(targetRoot, relativePath));
180
185
  if (change.base && isBlobMode(change.base.mode) && change.base.objectId !== change.theirs.objectId) {
181
186
  preBlobs.set(relativePath, change.base.objectId);
187
+ postBlobs.set(relativePath, isBlobMode(change.theirs.mode) ? change.theirs.objectId : null);
182
188
  }
183
189
  continue;
184
190
  }
@@ -292,7 +298,14 @@ function decideWorkspaceHydrationMerge(input) {
292
298
  overrides.set(relativePath, { content: merged.content, mode });
293
299
  write.add(relativePath);
294
300
  expectations.set(relativePath, candidate.oursStat);
295
- if (!contentUnchanged) preBlobs.set(relativePath, hashOf(oursBytes));
301
+ if (!contentUnchanged) {
302
+ const preBlob = hashOf(oursBytes);
303
+ const postBlob = hashOf(merged.content);
304
+ preBlobs.set(relativePath, preBlob);
305
+ postBlobs.set(relativePath, postBlob);
306
+ blobsToStore.set(preBlob, oursBytes);
307
+ blobsToStore.set(postBlob, merged.content);
308
+ }
296
309
  mergedPaths.push(relativePath);
297
310
  }
298
311
  }
@@ -366,6 +379,8 @@ function decideWorkspaceHydrationMerge(input) {
366
379
  overrides,
367
380
  expectations,
368
381
  preBlobs,
382
+ postBlobs,
383
+ blobsToStore,
369
384
  mergedPaths: mergedPaths.sort(),
370
385
  keptPaths: [...keptPaths].sort()
371
386
  };
@@ -251,9 +251,12 @@ function synthesizeMountTree(input) {
251
251
  removeTemporaryIndex(indexPath);
252
252
  }
253
253
  }
254
- function staleRewritePaths(input) {
255
- const entries = Object.entries(input.staleRewriteBlobs ?? {});
256
- if (entries.length === 0) return { stale: [], cleared: [] };
254
+ function ledgerStatMatches(recorded, projected) {
255
+ return recorded !== null && projected?.kind === "file" && projected.ino === recorded.ino && projected.size === recorded.size && projected.mode === recorded.mode && projected.mtimeMs === recorded.mtimeMs && projected.ctimeMs === recorded.ctimeMs;
256
+ }
257
+ function classifyHydrationLedger(input) {
258
+ const entries = Object.entries(input.hydrationLedger ?? {});
259
+ if (entries.length === 0) return { stale: [], merge: [], cleared: [] };
257
260
  const probed = gitText(input.workspacePath, ["cat-file", "--batch-check"], "resolve workspace projection basis subtree", {
258
261
  stdin: Buffer.from(`${input.basisHead}:${input.workspaceRelativePath}
259
262
  `)
@@ -274,21 +277,116 @@ function staleRewritePaths(input) {
274
277
  for (let index = 0; index + 1 < records.length; index += 2) changed.add(records[index + 1].toString());
275
278
  }
276
279
  const stale = [];
280
+ const merge = [];
277
281
  const cleared = [];
278
- for (const [relativePath, blob] of entries) {
282
+ for (const [relativePath, entry] of entries) {
279
283
  const synthesized = input.blobs.get(relativePath);
280
284
  if (synthesized === void 0) {
285
+ if (entry.postBlob === null) continue;
286
+ cleared.push(relativePath);
287
+ continue;
288
+ }
289
+ if (ledgerStatMatches(entry.stat, input.projectedFiles[relativePath]) || synthesized === entry.postBlob) continue;
290
+ if (!changed.has(relativePath)) {
281
291
  cleared.push(relativePath);
282
292
  continue;
283
293
  }
284
- if (synthesized === blob) {
285
- if (changed.has(relativePath)) stale.push(relativePath);
286
- else cleared.push(relativePath);
294
+ if (entry.processLiveAcrossHydration) {
295
+ merge.push({ relativePath, preBlob: entry.preBlob, postBlob: entry.postBlob });
287
296
  continue;
288
297
  }
289
- if (changed.has(relativePath)) cleared.push(relativePath);
298
+ if (synthesized === entry.preBlob) {
299
+ stale.push(relativePath);
300
+ continue;
301
+ }
302
+ cleared.push(relativePath);
303
+ }
304
+ if (merge.length > 0) {
305
+ const ids = [...new Set(merge.flatMap(({ preBlob, postBlob }) => postBlob ? [preBlob, postBlob] : [preBlob]))];
306
+ const checked = gitText(input.workspacePath, ["cat-file", "--batch-check"], "verify hydration ledger blobs", {
307
+ stdin: Buffer.from(`${ids.join("\n")}
308
+ `)
309
+ });
310
+ const missing = new Set(
311
+ checked.split("\n").filter((line) => / missing$/u.test(line)).map((line) => line.split(" ")[0])
312
+ );
313
+ if (missing.size > 0) {
314
+ for (let index = merge.length - 1; index >= 0; index -= 1) {
315
+ const { relativePath, preBlob, postBlob } = merge[index];
316
+ if (missing.has(preBlob) || postBlob !== null && missing.has(postBlob)) {
317
+ merge.splice(index, 1);
318
+ cleared.push(relativePath);
319
+ }
320
+ }
321
+ }
322
+ }
323
+ return { stale: stale.sort(), merge: merge.sort((left, right) => left.relativePath.localeCompare(right.relativePath)), cleared: cleared.sort() };
324
+ }
325
+ function hydrationLedgerMergeBase(input) {
326
+ if (input.merges.length === 0) return input.basisHead;
327
+ return commitWithLedgerPaths({
328
+ workspacePath: input.workspacePath,
329
+ workspaceRelativePath: input.workspaceRelativePath,
330
+ parent: input.basisHead,
331
+ paths: input.merges.map(({ relativePath, preBlob }) => ({ relativePath, blob: preBlob })),
332
+ message: JSON.stringify({
333
+ type: "workspace_hydration_ledger_basis",
334
+ mountId: input.mountId,
335
+ attemptId: input.attemptId,
336
+ paths: input.merges.map(({ relativePath }) => relativePath)
337
+ }),
338
+ action: "hydration ledger merge base"
339
+ });
340
+ }
341
+ function hydrationLedgerTheirs(input) {
342
+ const paths = input.merges.flatMap(({ relativePath, postBlob }) => postBlob ? [{ relativePath, blob: postBlob }] : []);
343
+ if (paths.length === 0) return input.currentHead;
344
+ return commitWithLedgerPaths({
345
+ workspacePath: input.workspacePath,
346
+ workspaceRelativePath: input.workspaceRelativePath,
347
+ parent: input.currentHead,
348
+ paths,
349
+ message: JSON.stringify({
350
+ type: "workspace_hydration_ledger_theirs",
351
+ mountId: input.mountId,
352
+ attemptId: input.attemptId,
353
+ paths: paths.map(({ relativePath }) => relativePath)
354
+ }),
355
+ action: "hydration ledger theirs"
356
+ });
357
+ }
358
+ function commitWithLedgerPaths(input) {
359
+ const indexPath = temporaryIndexPath(input.workspacePath);
360
+ const environment = temporaryIndexEnvironment(indexPath);
361
+ try {
362
+ git(input.workspacePath, ["read-tree", input.parent], `read workspace tree for the ${input.action}`, { environment });
363
+ const paths = input.paths.map(({ relativePath }) => `${input.workspaceRelativePath}/${relativePath}`);
364
+ const listed = git(
365
+ input.workspacePath,
366
+ ["ls-files", "-z", "--stage", "--", ...paths.map((entry) => `:(literal)${entry}`)],
367
+ `inspect modes for the ${input.action}`,
368
+ { environment }
369
+ );
370
+ const modes = /* @__PURE__ */ new Map();
371
+ for (const record of nulRecords(listed)) {
372
+ const match = /^([0-7]{6}) [0-9a-f]{40,64} [0-3]\t(.+)$/su.exec(record.toString());
373
+ if (match) modes.set(match[2], match[1]);
374
+ }
375
+ const records = input.paths.map(
376
+ ({ relativePath, blob }, index) => indexInfoRecord(modes.get(paths[index]) ?? "100644", requireObjectId(blob, `hydration ledger blob for ${relativePath}`), paths[index])
377
+ );
378
+ git(input.workspacePath, ["update-index", "-z", "--index-info"], `rewrite paths for the ${input.action}`, {
379
+ environment,
380
+ stdin: Buffer.concat(records)
381
+ });
382
+ const tree = requireObjectId(gitText(input.workspacePath, ["write-tree"], `write the ${input.action}`, { environment }), `${input.action} tree`);
383
+ return requireObjectId(
384
+ gitText(input.workspacePath, ["commit-tree", tree, "-p", input.parent, "-m", input.message], `commit the ${input.action}`),
385
+ `${input.action} commit`
386
+ );
387
+ } finally {
388
+ removeTemporaryIndex(indexPath);
290
389
  }
291
- return { stale: stale.sort(), cleared: cleared.sort() };
292
390
  }
293
391
  function nulRecords(content) {
294
392
  const records = [];
@@ -322,12 +420,14 @@ function graftMountTree(input) {
322
420
  stdin: removedPaths
323
421
  });
324
422
  }
325
- git(
326
- input.workspacePath,
327
- ["read-tree", "-i", `--prefix=${input.workspaceRelativePath}/`, input.mountTree],
328
- "graft workspace projection mount tree",
329
- { environment }
330
- );
423
+ if (input.mountTree !== null) {
424
+ git(
425
+ input.workspacePath,
426
+ ["read-tree", "-i", `--prefix=${input.workspaceRelativePath}/`, input.mountTree],
427
+ "graft workspace projection mount tree",
428
+ { environment }
429
+ );
430
+ }
331
431
  return requireObjectId(
332
432
  gitText(input.workspacePath, ["write-tree"], "write grafted workspace projection tree", { environment }),
333
433
  "Grafted workspace projection tree"
@@ -336,25 +436,41 @@ function graftMountTree(input) {
336
436
  removeTemporaryIndex(indexPath);
337
437
  }
338
438
  }
439
+ function mountSubtreeObjectId(workspacePath, revision, workspaceRelativePath, label) {
440
+ const spec = `${revision}:${workspaceRelativePath}`;
441
+ if (gitResult(workspacePath, ["cat-file", "-e", spec]).exitCode !== 0) return null;
442
+ const type = gitText(workspacePath, ["cat-file", "-t", spec], `inspect ${label}`);
443
+ if (type !== "tree") throw new Error(`${label} is not a directory at ${workspaceRelativePath}`);
444
+ return requireObjectId(gitText(workspacePath, ["rev-parse", spec], `resolve ${label}`), label);
445
+ }
339
446
  function synthesizeOursCommit(input) {
340
447
  const synthesized = synthesizeMountTree({
341
448
  workspacePath: input.workspacePath,
342
449
  sourcePath: input.mount.sourcePath,
343
450
  sourceMode: input.mount.sourceMode
344
451
  });
345
- const detected = staleRewritePaths({
452
+ const classified = classifyHydrationLedger({
346
453
  workspacePath: input.workspacePath,
347
454
  workspaceRelativePath: input.workspaceRelativePath,
348
455
  basisHead: input.basisHead,
349
456
  mountTree: synthesized.tree,
350
457
  blobs: synthesized.blobs,
351
- staleRewriteBlobs: input.staleRewriteBlobs
458
+ projectedFiles: synthesized.projectedFiles,
459
+ hydrationLedger: input.hydrationLedger
352
460
  });
353
- if (detected.stale.length > 0) return { staleRewritePaths: detected.stale, staleRewriteCleared: detected.cleared };
354
- const rootTree = graftMountTree({
461
+ if (classified.stale.length > 0) return { staleRewritePaths: classified.stale, staleRewriteCleared: classified.cleared };
462
+ const mergeBase = hydrationLedgerMergeBase({
355
463
  workspacePath: input.workspacePath,
356
464
  workspaceRelativePath: input.workspaceRelativePath,
357
465
  basisHead: input.basisHead,
466
+ merges: classified.merge,
467
+ mountId: input.mount.id,
468
+ attemptId: input.attemptId
469
+ });
470
+ const rootTree = graftMountTree({
471
+ workspacePath: input.workspacePath,
472
+ workspaceRelativePath: input.workspaceRelativePath,
473
+ basisHead: mergeBase,
358
474
  mountTree: synthesized.tree
359
475
  });
360
476
  const message = JSON.stringify({
@@ -365,12 +481,18 @@ function synthesizeOursCommit(input) {
365
481
  const oursCommit = requireObjectId(
366
482
  gitText(
367
483
  input.workspacePath,
368
- ["commit-tree", rootTree, "-p", input.basisHead, "-m", message],
484
+ ["commit-tree", rootTree, "-p", mergeBase, "-m", message],
369
485
  `commit synthesized workspace projection for mount ${input.mount.id}`
370
486
  ),
371
487
  `Synthesized workspace projection commit for mount ${input.mount.id}`
372
488
  );
373
- return { oursCommit, projectedFiles: synthesized.projectedFiles, staleRewriteCleared: detected.cleared };
489
+ return {
490
+ oursCommit,
491
+ mergeBase,
492
+ projectedFiles: synthesized.projectedFiles,
493
+ staleRewriteCleared: classified.cleared,
494
+ ledgerMerges: classified.merge
495
+ };
374
496
  }
375
497
  function mergeWorkspaceProjectionMount(input) {
376
498
  const support = workspaceMergeProjectionSupport();
@@ -385,31 +507,69 @@ function mergeWorkspaceProjectionMount(input) {
385
507
  workspaceRelativePath,
386
508
  basisHead,
387
509
  attemptId: input.attemptId,
388
- ...input.staleRewriteBlobs ? { staleRewriteBlobs: input.staleRewriteBlobs } : {}
510
+ ...input.hydrationLedger ? { hydrationLedger: input.hydrationLedger } : {}
389
511
  });
390
512
  if ("staleRewritePaths" in synthesized) {
391
513
  return { kind: "stale_rewrite", paths: synthesized.staleRewritePaths, staleRewriteCleared: synthesized.staleRewriteCleared };
392
514
  }
393
- const { oursCommit, projectedFiles, staleRewriteCleared } = synthesized;
515
+ const { oursCommit, mergeBase, projectedFiles, staleRewriteCleared, ledgerMerges } = synthesized;
516
+ const ledgerMergedPaths = ledgerMerges.map(({ relativePath }) => relativePath);
517
+ const theirsHead = hydrationLedgerTheirs({
518
+ workspacePath,
519
+ workspaceRelativePath,
520
+ currentHead,
521
+ merges: ledgerMerges,
522
+ mountId: input.mount.id,
523
+ attemptId: input.attemptId
524
+ });
394
525
  const readAtMs = Date.now();
526
+ const inboundMountTree = mountSubtreeObjectId(
527
+ workspacePath,
528
+ theirsHead,
529
+ workspaceRelativePath,
530
+ `current workspace head subtree for mount ${input.mount.id}`
531
+ );
532
+ const inboundCommit = requireObjectId(
533
+ gitText(
534
+ workspacePath,
535
+ [
536
+ "commit-tree",
537
+ graftMountTree({ workspacePath, workspaceRelativePath, basisHead: mergeBase, mountTree: inboundMountTree }),
538
+ "-p",
539
+ mergeBase,
540
+ "-m",
541
+ JSON.stringify({ type: "workspace_projection_inbound", mountId: input.mount.id, attemptId: input.attemptId })
542
+ ],
543
+ `commit inbound workspace projection for mount ${input.mount.id}`
544
+ ),
545
+ `Inbound workspace projection commit for mount ${input.mount.id}`
546
+ );
395
547
  const merged = gitResult(workspacePath, [
396
548
  "merge-tree",
397
549
  "--write-tree",
398
- `--merge-base=${basisHead}`,
550
+ `--merge-base=${mergeBase}`,
399
551
  "--name-only",
400
552
  "-z",
401
553
  "--no-messages",
402
554
  oursCommit,
403
- currentHead
555
+ inboundCommit
404
556
  ]);
405
557
  if (merged.exitCode !== 0 && merged.exitCode !== 1) {
406
558
  const detail = merged.stderr.toString().trim() || merged.stdout.toString().trim() || `git exited ${merged.exitCode}`;
407
559
  throw new Error(`Merge workspace projection for mount ${input.mount.id}: ${detail}`);
408
560
  }
409
561
  const records = nulRecords(merged.stdout);
410
- const resultTree = records.shift()?.toString() ?? "";
411
- requireObjectId(resultTree, `Merged workspace projection tree for mount ${input.mount.id}`);
412
- if (merged.exitCode === 0) return { kind: "clean", resultTree, oursCommit, projectedFiles, readAtMs, staleRewriteCleared };
562
+ const mergedTree = records.shift()?.toString() ?? "";
563
+ requireObjectId(mergedTree, `Merged workspace projection tree for mount ${input.mount.id}`);
564
+ if (merged.exitCode === 0) {
565
+ const resultTree = graftMountTree({
566
+ workspacePath,
567
+ workspaceRelativePath,
568
+ basisHead: currentHead,
569
+ mountTree: mountSubtreeObjectId(workspacePath, mergedTree, workspaceRelativePath, `merged subtree for mount ${input.mount.id}`)
570
+ });
571
+ return { kind: "clean", resultTree, oursCommit, projectedFiles, readAtMs, staleRewriteCleared, ledgerMergedPaths };
572
+ }
413
573
  const conflictPaths = records.map((record) => record.toString()).sort();
414
574
  return {
415
575
  kind: "conflict",
@@ -418,7 +578,8 @@ function mergeWorkspaceProjectionMount(input) {
418
578
  error: `Workspace projection for mount ${input.mount.id} conflicted with the current workspace head`,
419
579
  projectedFiles,
420
580
  readAtMs,
421
- staleRewriteCleared
581
+ staleRewriteCleared,
582
+ ledgerMergedPaths
422
583
  };
423
584
  }
424
585
  function materializeWorkspaceProjectionTree(input) {
package/dist/mjs/main.mjs CHANGED
@@ -4330,6 +4330,8 @@ async function startWorker(options, projectRuntime = {
4330
4330
  });
4331
4331
  };
4332
4332
  const projectBranchMountBusy = (projectId, branchName, branchPath) => !readyProjectIds.has(projectId) || projectBranchMountActivityBusy(projectId, branchName, branchPath);
4333
+ const projectBranchMountFenced = (projectId, branchName, branchPath) => !readyProjectIds.has(projectId) || projectConfigById.get(projectId)?.executionDisabled === true || hasProjectWorktree(branchPath) && projectWorktreeOperationInProgress(branchPath) || canonicalWorkspaceMutationIsActive(activeWorkspaceMutationTargets());
4334
+ const projectBranchMountProcessLive = (projectId, branchName) => projectBranchHasActiveWorkspaceTarget(projectId, branchName, activeWorkspaceMutationTargets());
4333
4335
  const inventoryConfiguredProjectCheckouts = () => {
4334
4336
  const inventory = { projects: [], missingCheckouts: [] };
4335
4337
  for (const project of projectConfigById.values()) {
@@ -4415,7 +4417,11 @@ async function startWorker(options, projectRuntime = {
4415
4417
  ].filter((id) => typeof id === "string")
4416
4418
  )
4417
4419
  ].join(", ") || "no live declared holder",
4418
- busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
4420
+ // A running job no longer pins its checkout: merge hydration folds
4421
+ // inbound changes into it. The plan mount below keeps the old fence
4422
+ // because its all-mode hydration is a plain mirror.
4423
+ busy: () => projectBranchMountFenced(project.projectId, branch.branchName, branchPath),
4424
+ processLive: () => projectBranchMountProcessLive(project.projectId, branch.branchName),
4419
4425
  mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
4420
4426
  busyForRecovery: () => projectBranchMountExclusiveSyncBusy(project.projectId, branch.branchName, branchPath)
4421
4427
  });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.136",
3
+ "version": "0.0.137",
4
4
  "type": "module"
5
5
  }