@ricsam/r5d-worker 0.0.59 → 0.0.60

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/dist/mjs/main.mjs CHANGED
@@ -13,27 +13,19 @@ import {
13
13
  } from "./heartbeat.mjs";
14
14
  import { terminateProcessTree } from "./process-tree.mjs";
15
15
  import { openWorkerPortForwardRelay } from "./port-forward-client.mjs";
16
- import {
17
- applyWorkspaceIncidentUpdate,
18
- releasePendingWorkspaceHead,
19
- WorkspaceIncidentOrderingFence
20
- } from "./workspace-incident-state.mjs";
16
+ import { applyWorkspaceIncidentUpdate, releasePendingWorkspaceHead, WorkspaceIncidentOrderingFence } from "./workspace-incident-state.mjs";
21
17
  import { acquireWorkspaceCommandMutation, runWorkspaceCommand } from "./workspace-command-sync-policy.mjs";
22
18
  import {
23
- WORKSPACE_CHECKPOINT_QUIET_MS,
19
+ WORKSPACE_PUBLICATION_QUIET_MS,
24
20
  WORKSPACE_IDLE_SAFETY_SCAN_MS,
25
- ExplicitWorkspaceCheckpointQueue,
21
+ ExplicitWorkspacePublicationQueue,
26
22
  WorkspaceManifestRevisionFence,
27
23
  processWorkspaceConvergenceState,
28
- processWorkspaceFilesystemWriterGate,
29
- waitForWorkspaceCheckpointOnShutdown,
30
- workspaceCheckpointIncidentAction,
31
- workspaceCheckpointTelemetry
24
+ waitForWorkspacePublicationOnShutdown,
25
+ workspacePublicationIncidentAction,
26
+ workspacePublicationTelemetry
32
27
  } from "./workspace-convergence.mjs";
33
- import {
34
- acquireWorkspaceManifestVisibleLease,
35
- inspectWorkspaceManifestAdmission
36
- } from "./workspace-manifest-admission.mjs";
28
+ import { inspectWorkspaceManifestFilesystem } from "./workspace-manifest-admission.mjs";
37
29
  import {
38
30
  isRetryableWorkerServerStatus,
39
31
  superviseWorkerRuntime,
@@ -64,7 +56,6 @@ const cancelledProcessRuns = /* @__PURE__ */ new Set();
64
56
  const activePtys = /* @__PURE__ */ new Map();
65
57
  let currentWorkerSocket = null;
66
58
  const workspaceSyncSingleFlight = new WorkspaceSyncSingleFlight();
67
- const workspaceFilesystemWriters = processWorkspaceFilesystemWriterGate();
68
59
  let githubCredential = null;
69
60
  let visibleGitIdentity = null;
70
61
  function defaultConfigPath() {
@@ -786,25 +777,13 @@ function fallbackInternalRemoteUrl(baseUrl, projectId) {
786
777
  }
787
778
  function visibleCheckoutRemoteFor(input) {
788
779
  const canonicalCheckout = input.manifest?.canonicalCheckouts?.find((checkout) => checkout.branchName === input.branchName);
789
- const isCanonicalManifestBranch = Boolean(canonicalCheckout);
790
- const usesCanonicalRemote = isCanonicalManifestBranch || !input.manifest?.repoHttpUrl;
791
- if (usesCanonicalRemote) {
792
- return {
793
- remoteUrl: fallbackInternalRemoteUrl(input.baseUrl, input.projectId),
794
- authHeader: `Authorization: Bearer ${input.token}`,
795
- persistAuth: false,
796
- reconcileExistingOrigin: isCanonicalManifestBranch,
797
- requireRemoteBranch: isCanonicalManifestBranch,
798
- requiredBaseCommit: canonicalCheckout?.scaffoldCommitHash ?? null
799
- };
800
- }
801
780
  return {
802
- remoteUrl: input.manifest?.repoHttpUrl ?? fallbackInternalRemoteUrl(input.baseUrl, input.projectId),
803
- authHeader: input.manifest?.repoAuthHeader ?? null,
804
- persistAuth: true,
805
- reconcileExistingOrigin: Boolean(input.manifest?.repoHttpUrl),
806
- requireRemoteBranch: false,
807
- requiredBaseCommit: null
781
+ remoteUrl: fallbackInternalRemoteUrl(input.baseUrl, input.projectId),
782
+ authHeader: `Authorization: Bearer ${input.token}`,
783
+ persistAuth: false,
784
+ reconcileExistingOrigin: true,
785
+ requireRemoteBranch: true,
786
+ requiredBaseCommit: canonicalCheckout?.scaffoldCommitHash ?? null
808
787
  };
809
788
  }
810
789
  function gitExtraHeaderUrlForRemote(remoteUrl) {
@@ -1025,9 +1004,11 @@ function checkoutVisibleBranch(input) {
1025
1004
  }
1026
1005
  function migrateExistingCheckoutToRequiredRemote(input) {
1027
1006
  if (originRemoteUrl(input.branchPath) === input.remoteUrl) return;
1028
- if (hasWorktreeChanges(input.branchPath)) {
1007
+ const dirtyWorkingTree = hasWorktreeChanges(input.branchPath);
1008
+ if (dirtyWorkingTree && input.requiredBaseCommit) {
1029
1009
  throw new Error(`Cannot migrate dirty checkout for ${input.branchName} to its canonical remote`);
1030
1010
  }
1011
+ const preserveWorkingTree = dirtyWorkingTree;
1031
1012
  runGit(
1032
1013
  visibleCheckoutFetchArgs("--no-tags", input.remoteUrl, `+refs/heads/${input.branchName}:refs/remotes/origin/${input.branchName}`),
1033
1014
  {
@@ -1042,11 +1023,12 @@ function migrateExistingCheckoutToRequiredRemote(input) {
1042
1023
  if (input.requiredBaseCommit && (!tryGit(["cat-file", "-e", `${input.requiredBaseCommit}^{commit}`], { cwd: input.branchPath }) || !tryGit(["merge-base", "--is-ancestor", input.requiredBaseCommit, canonicalRemoteRef], { cwd: input.branchPath }))) {
1043
1024
  throw new Error(`Canonical branch ${input.branchName} is not derived from required scaffold ${input.requiredBaseCommit}`);
1044
1025
  }
1026
+ ensureOriginRemote(input.branchPath, input.remoteUrl);
1027
+ if (preserveWorkingTree) return;
1045
1028
  if (tryGit(["rev-parse", "--verify", "HEAD"], { cwd: input.branchPath })) {
1046
1029
  const previousHead = runGit(["rev-parse", "HEAD"], { cwd: input.branchPath });
1047
1030
  runGit(["update-ref", `refs/r5d/pre-canonical-checkout/${previousHead}`, previousHead], { cwd: input.branchPath });
1048
1031
  }
1049
- ensureOriginRemote(input.branchPath, input.remoteUrl);
1050
1032
  runGit(["checkout", "--no-recurse-submodules", "-B", input.branchName, `origin/${input.branchName}`], {
1051
1033
  cwd: input.branchPath
1052
1034
  });
@@ -1139,7 +1121,7 @@ function ensureVisibleGitCheckout(input) {
1139
1121
  configureVisibleGitIdentity(branchPath, visibleGitIdentity, runGit);
1140
1122
  return branchPath;
1141
1123
  }
1142
- function ensureBranchWorkspace(input) {
1124
+ function ensureBranchWorktree(input) {
1143
1125
  const defaultBranch = input.manifest?.defaultBranch || "main";
1144
1126
  const remote = visibleCheckoutRemoteFor(input);
1145
1127
  return {
@@ -1172,7 +1154,7 @@ function resolveWorkerSessionTarget(input) {
1172
1154
  }
1173
1155
  const manifest = input.manifestByProjectId.get(input.target.projectId);
1174
1156
  const projectRoot = projectRootFor(input.projectsRoot, input.target.projectId, input.manifestByProjectId);
1175
- const workspace = ensureBranchWorkspace({
1157
+ const worktree = ensureBranchWorktree({
1176
1158
  projectId: input.target.projectId,
1177
1159
  baseUrl: input.baseUrl,
1178
1160
  token: input.token,
@@ -1181,7 +1163,7 @@ function resolveWorkerSessionTarget(input) {
1181
1163
  branchName: input.target.branchName,
1182
1164
  manifest
1183
1165
  });
1184
- return { target: input.target, rootPath: workspace.branchPath, manifest };
1166
+ return { target: input.target, rootPath: worktree.branchPath, manifest };
1185
1167
  }
1186
1168
  function workspaceSyncCheckoutTargets(input) {
1187
1169
  const selected = /* @__PURE__ */ new Map();
@@ -2386,15 +2368,15 @@ async function startWorker(options) {
2386
2368
  const workspaceConvergence = processWorkspaceConvergenceState(path.join(syncRoot, "workspace-convergence.json"));
2387
2369
  const workspaceManifestRevision = new WorkspaceManifestRevisionFence();
2388
2370
  let workspaceReady = false;
2389
- let workspaceCheckpointTimer;
2371
+ let workspacePublicationTimer;
2390
2372
  let workspaceSafetyScan;
2391
2373
  let workspaceManifestHydrationTimer;
2392
2374
  let workspaceManifestHydrationInFlight = false;
2393
2375
  let workspaceSafetyScanInFlight = false;
2394
2376
  let workspaceHeadConvergenceInFlight = false;
2395
2377
  let pendingWorkspaceHead;
2396
- let pendingCheckpoint;
2397
- const explicitWorkspaceCheckpoints = new ExplicitWorkspaceCheckpointQueue();
2378
+ let pendingPublication;
2379
+ const explicitWorkspacePublications = new ExplicitWorkspacePublicationQueue();
2398
2380
  let terminalReplayTimer;
2399
2381
  let workspaceSyncRequestsInFlight = 0;
2400
2382
  let sessionArtifactSyncRequestsInFlight = 0;
@@ -2441,121 +2423,110 @@ async function startWorker(options) {
2441
2423
  })
2442
2424
  );
2443
2425
  } catch (error) {
2444
- process.stderr.write(
2445
- `[r5d-worker] failed to send workspace readiness: ${error instanceof Error ? error.message : String(error)}
2446
- `
2447
- );
2426
+ process.stderr.write(`[r5d-worker] failed to send workspace readiness: ${error instanceof Error ? error.message : String(error)}
2427
+ `);
2448
2428
  }
2449
2429
  };
2450
- const workerHasActiveWriter = () => activeProcesses.size > 0 || activePtys.size > 0 || cliUpdateInProgress;
2451
2430
  const targetMayMutateVisibleWorkspace = (target) => target.type === "project" || target.rootProfile === "visible_projects";
2452
- const armWorkspaceCheckpoint = (trigger, options2 = { delayMs: WORKSPACE_CHECKPOINT_QUIET_MS }) => {
2431
+ const armWorkspacePublication = (trigger, options2 = { delayMs: WORKSPACE_PUBLICATION_QUIET_MS }) => {
2453
2432
  workspaceConvergence.schedule();
2454
2433
  if (currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) return;
2455
2434
  sendWorkspaceReady();
2456
- if (options2.requestId && !explicitWorkspaceCheckpoints.schedule({ requestId: options2.requestId, trigger }, pendingCheckpoint?.requestId)) {
2435
+ if (options2.requestId && !explicitWorkspacePublications.schedule({ requestId: options2.requestId, trigger }, pendingPublication?.requestId)) {
2457
2436
  return;
2458
2437
  }
2459
- if (!options2.requestId && explicitWorkspaceCheckpoints.hasScheduled()) return;
2460
- if (workspaceCheckpointTimer) clearTimeout(workspaceCheckpointTimer);
2461
- workspaceCheckpointTimer = setTimeout(() => {
2462
- workspaceCheckpointTimer = void 0;
2463
- if (options2.requestId) explicitWorkspaceCheckpoints.consumeScheduled(options2.requestId);
2464
- if (currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) return;
2465
- if (!workspaceReady || !workspaceRemoteUrl) {
2466
- armWorkspaceCheckpoint(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
2467
- return;
2468
- }
2469
- if (activeWorkspaceIncidentId) {
2470
- workspaceConvergence.block();
2471
- sendWorkspaceReady();
2472
- if (workspaceCheckpointIncidentAction(options2.requestId) === "defer_automatic") return;
2473
- if (pendingCheckpoint) {
2474
- explicitWorkspaceCheckpoints.enqueue({ requestId: options2.requestId, trigger }, pendingCheckpoint.requestId);
2438
+ if (!options2.requestId && explicitWorkspacePublications.hasScheduled()) return;
2439
+ if (workspacePublicationTimer) clearTimeout(workspacePublicationTimer);
2440
+ workspacePublicationTimer = setTimeout(
2441
+ () => {
2442
+ workspacePublicationTimer = void 0;
2443
+ if (options2.requestId) explicitWorkspacePublications.consumeScheduled(options2.requestId);
2444
+ if (currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) return;
2445
+ if (!workspaceReady || !workspaceRemoteUrl) {
2446
+ armWorkspacePublication(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
2447
+ return;
2448
+ }
2449
+ if (activeWorkspaceIncidentId) {
2450
+ workspaceConvergence.block();
2451
+ sendWorkspaceReady();
2452
+ if (workspacePublicationIncidentAction(options2.requestId) === "defer_automatic") return;
2453
+ if (pendingPublication) {
2454
+ explicitWorkspacePublications.enqueue({ requestId: options2.requestId, trigger }, pendingPublication.requestId);
2455
+ return;
2456
+ }
2457
+ const snapshot2 = workspaceConvergence.snapshot();
2458
+ pendingPublication = {
2459
+ requestId: options2.requestId,
2460
+ trigger,
2461
+ dirtyGeneration: snapshot2.dirtyGeneration,
2462
+ explicit: true,
2463
+ workInFlight: false,
2464
+ requestedAt: Date.now()
2465
+ };
2466
+ try {
2467
+ ws.send(
2468
+ JSON.stringify({
2469
+ type: "workspace_publication_request",
2470
+ requestId: options2.requestId,
2471
+ trigger,
2472
+ dirtyGeneration: snapshot2.dirtyGeneration,
2473
+ localHead: snapshot2.localHead,
2474
+ desiredCanonicalHead: snapshot2.desiredCanonicalHead
2475
+ })
2476
+ );
2477
+ } catch (error) {
2478
+ pendingPublication = void 0;
2479
+ workspaceConvergence.defer();
2480
+ process.stderr.write(
2481
+ `[r5d-worker] failed to request blocked workspace publication authority: ${error instanceof Error ? error.message : String(error)}
2482
+ `
2483
+ );
2484
+ armWorkspacePublication(trigger, { delayMs: 1e3, requestId: options2.requestId });
2485
+ }
2475
2486
  return;
2476
2487
  }
2477
- const snapshot2 = workspaceConvergence.snapshot();
2478
- pendingCheckpoint = {
2479
- requestId: options2.requestId,
2488
+ if (pendingPublication) {
2489
+ armWorkspacePublication(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
2490
+ return;
2491
+ }
2492
+ const snapshot = workspaceConvergence.snapshot();
2493
+ const requestId = options2.requestId ?? crypto.randomUUID();
2494
+ pendingPublication = {
2495
+ requestId,
2480
2496
  trigger,
2481
- dirtyGeneration: snapshot2.dirtyGeneration,
2482
- explicit: true,
2483
- admissionHeld: false,
2497
+ dirtyGeneration: snapshot.dirtyGeneration,
2498
+ explicit: options2.requestId !== void 0,
2484
2499
  workInFlight: false,
2485
- requestedAt: Date.now(),
2486
- releaseCheckpointAdmission: () => {
2487
- }
2500
+ requestedAt: Date.now()
2488
2501
  };
2489
2502
  try {
2490
2503
  ws.send(
2491
2504
  JSON.stringify({
2492
- type: "workspace_checkpoint_request",
2493
- requestId: options2.requestId,
2505
+ type: "workspace_publication_request",
2506
+ requestId,
2494
2507
  trigger,
2495
- dirtyGeneration: snapshot2.dirtyGeneration,
2496
- localHead: snapshot2.localHead,
2497
- desiredCanonicalHead: snapshot2.desiredCanonicalHead
2508
+ dirtyGeneration: snapshot.dirtyGeneration,
2509
+ localHead: snapshot.localHead,
2510
+ desiredCanonicalHead: snapshot.desiredCanonicalHead
2498
2511
  })
2499
2512
  );
2500
2513
  } catch (error) {
2501
- pendingCheckpoint = void 0;
2514
+ pendingPublication = void 0;
2502
2515
  workspaceConvergence.defer();
2503
2516
  process.stderr.write(
2504
- `[r5d-worker] failed to request blocked workspace checkpoint authority: ${error instanceof Error ? error.message : String(error)}
2517
+ `[r5d-worker] failed to request workspace publication: ${error instanceof Error ? error.message : String(error)}
2505
2518
  `
2506
2519
  );
2507
- armWorkspaceCheckpoint(trigger, { delayMs: 1e3, requestId: options2.requestId });
2520
+ armWorkspacePublication(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
2508
2521
  }
2509
- return;
2510
- }
2511
- if (pendingCheckpoint || workerHasActiveWriter()) {
2512
- armWorkspaceCheckpoint(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
2513
- return;
2514
- }
2515
- const releaseCheckpointAdmission = workspaceFilesystemWriters.tryAcquireCheckpoint();
2516
- if (!releaseCheckpointAdmission) {
2517
- armWorkspaceCheckpoint(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
2518
- return;
2519
- }
2520
- const snapshot = workspaceConvergence.snapshot();
2521
- const requestId = options2.requestId ?? crypto.randomUUID();
2522
- pendingCheckpoint = {
2523
- requestId,
2524
- trigger,
2525
- dirtyGeneration: snapshot.dirtyGeneration,
2526
- explicit: options2.requestId !== void 0,
2527
- admissionHeld: true,
2528
- workInFlight: false,
2529
- requestedAt: Date.now(),
2530
- releaseCheckpointAdmission
2531
- };
2532
- try {
2533
- ws.send(
2534
- JSON.stringify({
2535
- type: "workspace_checkpoint_request",
2536
- requestId,
2537
- trigger,
2538
- dirtyGeneration: snapshot.dirtyGeneration,
2539
- localHead: snapshot.localHead,
2540
- desiredCanonicalHead: snapshot.desiredCanonicalHead
2541
- })
2542
- );
2543
- } catch (error) {
2544
- releaseCheckpointAdmission();
2545
- pendingCheckpoint = void 0;
2546
- workspaceConvergence.defer();
2547
- process.stderr.write(
2548
- `[r5d-worker] failed to request workspace checkpoint: ${error instanceof Error ? error.message : String(error)}
2549
- `
2550
- );
2551
- armWorkspaceCheckpoint(trigger, { delayMs: 1e3, ...options2.requestId ? { requestId: options2.requestId } : {} });
2552
- }
2553
- }, Math.max(0, options2.delayMs));
2554
- workspaceCheckpointTimer.unref();
2522
+ },
2523
+ Math.max(0, options2.delayMs)
2524
+ );
2525
+ workspacePublicationTimer.unref();
2555
2526
  };
2556
2527
  const markWorkspaceDirty = (trigger, force = false) => {
2557
2528
  workspaceConvergence.markDirty();
2558
- armWorkspaceCheckpoint(trigger, { delayMs: force ? 0 : WORKSPACE_CHECKPOINT_QUIET_MS });
2529
+ armWorkspacePublication(trigger, { delayMs: force ? 0 : WORKSPACE_PUBLICATION_QUIET_MS });
2559
2530
  };
2560
2531
  const convergeAvailableWorkspaceHead = async () => {
2561
2532
  if (pendingWorkspaceHead === void 0 || !workspaceReady || !workspaceRemoteUrl || activeWorkspaceIncidentId || workspaceHeadConvergenceInFlight) {
@@ -2565,12 +2536,9 @@ async function startWorker(options) {
2565
2536
  pendingWorkspaceHead = void 0;
2566
2537
  workspaceConvergence.observeCanonicalHead(advertisedHead);
2567
2538
  workspaceHeadConvergenceInFlight = true;
2568
- let releaseVisibleHydration = null;
2569
2539
  try {
2570
2540
  const snapshot = workspaceConvergence.snapshot();
2571
- const mayHydrateVisible = !workerHasActiveWriter() && !pendingCheckpoint && snapshot.dirtyGeneration === snapshot.publishedGeneration;
2572
- releaseVisibleHydration = mayHydrateVisible ? workspaceFilesystemWriters.tryAcquireCheckpoint() : null;
2573
- const hydrateVisible = releaseVisibleHydration !== null;
2541
+ const hydrateVisible = !pendingPublication && snapshot.dirtyGeneration === snapshot.publishedGeneration;
2574
2542
  const converged = await workspaceSyncSingleFlight.runExclusive(
2575
2543
  () => convergeWorkspaceHead(workspaceSyncInput({ type: "inbound_head", detail: advertisedHead ?? "unborn" }), {
2576
2544
  hydrateVisible
@@ -2579,7 +2547,7 @@ async function startWorker(options) {
2579
2547
  workspaceConvergence.observeCanonicalHead(converged.canonicalHead);
2580
2548
  if (converged.hydrated) workspaceConvergence.setLocalHead(converged.localHead);
2581
2549
  if (hydrateVisible && converged.localChanges) {
2582
- markWorkspaceDirty({ type: "periodic", detail: "inbound overlap retained for next checkpoint" });
2550
+ markWorkspaceDirty({ type: "periodic", detail: "inbound overlap retained for next publication" });
2583
2551
  }
2584
2552
  sendWorkspaceReady();
2585
2553
  } catch (error) {
@@ -2589,7 +2557,6 @@ async function startWorker(options) {
2589
2557
  `
2590
2558
  );
2591
2559
  } finally {
2592
- releaseVisibleHydration?.();
2593
2560
  workspaceHeadConvergenceInFlight = false;
2594
2561
  heartbeatBusyGrace = grantWorkerHeartbeatBusyGrace(lastServerHeartbeatAt, heartbeatBusyGrace);
2595
2562
  if (pendingWorkspaceHead !== void 0) {
@@ -2610,7 +2577,7 @@ async function startWorker(options) {
2610
2577
  workspaceShadowRoot,
2611
2578
  manifestByProjectId
2612
2579
  });
2613
- const ensureVisibleWorkspaceCheckouts = (targets, options2 = {}) => {
2580
+ const ensureVisibleWorktrees = (targets, options2 = {}) => {
2614
2581
  const created = [];
2615
2582
  for (const target of targets) {
2616
2583
  const manifest = manifestByProjectId.get(target.projectId);
@@ -2639,7 +2606,7 @@ async function startWorker(options) {
2639
2606
  reconcileExistingOrigin: remote.reconcileExistingOrigin,
2640
2607
  requireRemoteBranch: remote.requireRemoteBranch,
2641
2608
  requiredBaseCommit: remote.requiredBaseCommit,
2642
- allowRequiredRemoteMigration: !(options2.strictCanonicalRevalidation && remote.requireRemoteBranch),
2609
+ allowRequiredRemoteMigration: !(options2.strictCanonicalRevalidation && remote.requiredBaseCommit),
2643
2610
  clearPersistentAuth: !remote.persistAuth
2644
2611
  });
2645
2612
  if (!existed) created.push(target);
@@ -2650,46 +2617,43 @@ async function startWorker(options) {
2650
2617
  if (workspaceManifestHydrationTimer || workspaceManifestHydrationInFlight || pendingManifestCheckouts.size === 0 || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
2651
2618
  return;
2652
2619
  }
2653
- workspaceManifestHydrationTimer = setTimeout(() => {
2654
- workspaceManifestHydrationTimer = void 0;
2655
- if (!workspaceReady || !workspaceRemoteUrl || activeWorkspaceIncidentId || pendingCheckpoint || pendingManifestCheckouts.size === 0 || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
2656
- if (pendingManifestCheckouts.size > 0 && currentWorkerSocket === ws && ws.readyState === WebSocket.OPEN) {
2657
- schedulePendingManifestCheckoutHydration(1e3);
2658
- }
2659
- return;
2660
- }
2661
- const releaseHydration = workspaceFilesystemWriters.tryAcquireCheckpoint();
2662
- if (!releaseHydration) {
2663
- schedulePendingManifestCheckoutHydration(1e3);
2664
- return;
2665
- }
2666
- workspaceManifestHydrationInFlight = true;
2667
- void workspaceSyncSingleFlight.runExclusive(async () => {
2668
- if (!workspaceReady || activeWorkspaceIncidentId || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
2620
+ workspaceManifestHydrationTimer = setTimeout(
2621
+ () => {
2622
+ workspaceManifestHydrationTimer = void 0;
2623
+ if (!workspaceReady || !workspaceRemoteUrl || activeWorkspaceIncidentId || pendingPublication || pendingManifestCheckouts.size === 0 || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
2624
+ if (pendingManifestCheckouts.size > 0 && currentWorkerSocket === ws && ws.readyState === WebSocket.OPEN) {
2625
+ schedulePendingManifestCheckoutHydration(1e3);
2626
+ }
2669
2627
  return;
2670
2628
  }
2671
- const targets = [...pendingManifestCheckouts.values()];
2672
- ensureVisibleWorkspaceCheckouts(targets, { strictCanonicalRevalidation: true });
2673
- for (const target of targets) {
2674
- const manifest = manifestByProjectId.get(target.projectId);
2675
- if (!manifest || !manifest.branches.includes(target.branchName)) continue;
2676
- const branchPath = path.join(projectRootFor(projectsRoot, target.projectId, manifestByProjectId), target.branchName);
2677
- if (hasNormalVisibleGitDir(branchPath)) {
2678
- pendingManifestCheckouts.delete(manifestCheckoutKey(target.projectId, target.branchName));
2629
+ workspaceManifestHydrationInFlight = true;
2630
+ void workspaceSyncSingleFlight.runExclusive(async () => {
2631
+ if (!workspaceReady || activeWorkspaceIncidentId || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
2632
+ return;
2679
2633
  }
2680
- }
2681
- }).then(() => sendWorkspaceReady()).catch((error) => {
2682
- process.stderr.write(
2683
- `[r5d-worker] pending manifest checkout hydration failed: ${error instanceof Error ? error.message : String(error)}
2634
+ const targets = [...pendingManifestCheckouts.values()];
2635
+ ensureVisibleWorktrees(targets, { strictCanonicalRevalidation: true });
2636
+ for (const target of targets) {
2637
+ const manifest = manifestByProjectId.get(target.projectId);
2638
+ if (!manifest || !manifest.branches.includes(target.branchName)) continue;
2639
+ const branchPath = path.join(projectRootFor(projectsRoot, target.projectId, manifestByProjectId), target.branchName);
2640
+ if (hasNormalVisibleGitDir(branchPath)) {
2641
+ pendingManifestCheckouts.delete(manifestCheckoutKey(target.projectId, target.branchName));
2642
+ }
2643
+ }
2644
+ }).then(() => sendWorkspaceReady()).catch((error) => {
2645
+ process.stderr.write(
2646
+ `[r5d-worker] pending manifest checkout hydration failed: ${error instanceof Error ? error.message : String(error)}
2684
2647
  `
2685
- );
2686
- }).finally(() => {
2687
- releaseHydration();
2688
- workspaceManifestHydrationInFlight = false;
2689
- heartbeatBusyGrace = grantWorkerHeartbeatBusyGrace(lastServerHeartbeatAt, heartbeatBusyGrace);
2690
- if (pendingManifestCheckouts.size > 0) schedulePendingManifestCheckoutHydration(1e3);
2691
- });
2692
- }, Math.max(0, delayMs));
2648
+ );
2649
+ }).finally(() => {
2650
+ workspaceManifestHydrationInFlight = false;
2651
+ heartbeatBusyGrace = grantWorkerHeartbeatBusyGrace(lastServerHeartbeatAt, heartbeatBusyGrace);
2652
+ if (pendingManifestCheckouts.size > 0) schedulePendingManifestCheckoutHydration(1e3);
2653
+ });
2654
+ },
2655
+ Math.max(0, delayMs)
2656
+ );
2693
2657
  workspaceManifestHydrationTimer.unref();
2694
2658
  };
2695
2659
  const runRequestedWorkspaceSync = async (authority, trigger, overrides = {}) => {
@@ -2723,7 +2687,7 @@ async function startWorker(options) {
2723
2687
  });
2724
2688
  }
2725
2689
  pendingTargets = shouldEnsureCheckouts ? scopedPendingTargets : [];
2726
- const createdCheckouts = ensureVisibleWorkspaceCheckouts(checkoutTargets, {
2690
+ const createdCheckouts = ensureVisibleWorktrees(checkoutTargets, {
2727
2691
  strictCanonicalRevalidation: true
2728
2692
  });
2729
2693
  const newVisibleCheckoutByKey = /* @__PURE__ */ new Map();
@@ -2805,11 +2769,11 @@ async function startWorker(options) {
2805
2769
  const handleGracefulShutdown = () => {
2806
2770
  if (shutdownAfterClose) return;
2807
2771
  shutdownAfterClose = true;
2808
- void waitForWorkspaceCheckpointOnShutdown({
2772
+ void waitForWorkspacePublicationOnShutdown({
2809
2773
  snapshot: () => workspaceConvergence.snapshot(),
2810
- forceCheckpoint: () => armWorkspaceCheckpoint({ type: "manual", detail: "graceful worker shutdown" }, { delayMs: 0 })
2774
+ forcePublication: () => armWorkspacePublication({ type: "manual", detail: "graceful worker shutdown" }, { delayMs: 0 })
2811
2775
  }).then((outcome) => {
2812
- process.stdout.write(`[r5d-worker] graceful workspace checkpoint ${outcome}
2776
+ process.stdout.write(`[r5d-worker] graceful workspace publication ${outcome}
2813
2777
  `);
2814
2778
  if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
2815
2779
  ws.close(1e3, "Worker shutting down");
@@ -2880,105 +2844,84 @@ async function startWorker(options) {
2880
2844
  if (message.type === "workspace_manifest") {
2881
2845
  const revisionToken = workspaceManifestRevision.begin(message.revision);
2882
2846
  workspaceReady = false;
2883
- const manifestAdmission = inspectWorkspaceManifestAdmission({
2847
+ const manifestFilesystem = inspectWorkspaceManifestFilesystem({
2884
2848
  projectsRoot,
2885
2849
  workspaceShadowRoot,
2886
2850
  projects: message.projects
2887
2851
  });
2888
- const releaseManifestHydration = await acquireWorkspaceManifestVisibleLease(
2889
- workspaceFilesystemWriters,
2890
- manifestAdmission
2891
- );
2892
- let completed = false;
2893
- try {
2894
- completed = await workspaceSyncSingleFlight.runExclusive(async () => {
2895
- if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
2896
- const firstBootstrap = manifestAdmission.firstBootstrap;
2897
- configureGitHubAuth(message.githubCredential);
2898
- visibleGitIdentity = message.gitIdentity;
2899
- workspaceRemoteUrl = message.workspaceRemoteUrl;
2900
- const migratedProjectIds = releaseManifestHydration ? migrateLegacyProjectRoots(projectsRoot, message.projects) : [];
2901
- manifestByProjectId.clear();
2902
- for (const project of message.projects) manifestByProjectId.set(project.projectId, project);
2903
- const currentCheckouts = allManifestCheckouts();
2904
- const currentCheckoutKeys = new Set(
2905
- currentCheckouts.map(({ projectId, branchName }) => manifestCheckoutKey(projectId, branchName))
2906
- );
2907
- for (const key of pendingManifestCheckouts.keys()) {
2908
- if (!currentCheckoutKeys.has(key)) pendingManifestCheckouts.delete(key);
2909
- }
2910
- for (const checkout of currentCheckouts) {
2911
- const key = manifestCheckoutKey(checkout.projectId, checkout.branchName);
2912
- const branchPath = path.join(
2913
- projectRootFor(projectsRoot, checkout.projectId, manifestByProjectId),
2914
- checkout.branchName
2915
- );
2916
- if (!hasNormalVisibleGitDir(branchPath)) pendingManifestCheckouts.set(key, checkout);
2917
- }
2918
- if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
2919
- const created = releaseManifestHydration ? ensureVisibleWorkspaceCheckouts(currentCheckouts, { strictCanonicalRevalidation: true }) : [];
2920
- if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
2921
- const bootstrapped = await convergeWorkspaceHead(
2922
- workspaceSyncInput({ type: "connect" }, { newVisibleCheckouts: created }),
2923
- {
2924
- // An existing checkout becomes routable after a fetch. Its
2925
- // visible tree converges asynchronously; first bootstrap alone
2926
- // must materialize the canonical snapshot before readiness.
2927
- // Re-check the incident immediately before convergence so a
2928
- // concurrently delivered fence forces fetch-only behavior.
2929
- hydrateVisible: firstBootstrap && !activeWorkspaceIncidentId
2930
- }
2931
- );
2932
- if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
2933
- workspaceConvergence.setLocalHead(bootstrapped.localHead);
2934
- workspaceConvergence.observeCanonicalHead(bootstrapped.canonicalHead);
2935
- for (const checkout of currentCheckouts) {
2936
- const manifest = manifestByProjectId.get(checkout.projectId);
2937
- if (!manifest) continue;
2938
- const branchPath = path.join(projectRootFor(projectsRoot, checkout.projectId, manifestByProjectId), checkout.branchName);
2939
- if (hasNormalVisibleGitDir(branchPath)) {
2940
- pendingManifestCheckouts.delete(manifestCheckoutKey(checkout.projectId, checkout.branchName));
2941
- }
2852
+ const completed = await workspaceSyncSingleFlight.runExclusive(async () => {
2853
+ if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
2854
+ const firstBootstrap = manifestFilesystem.firstBootstrap;
2855
+ configureGitHubAuth(message.githubCredential);
2856
+ visibleGitIdentity = message.gitIdentity;
2857
+ workspaceRemoteUrl = message.workspaceRemoteUrl;
2858
+ const migratedProjectIds = migrateLegacyProjectRoots(projectsRoot, message.projects);
2859
+ manifestByProjectId.clear();
2860
+ for (const project of message.projects) manifestByProjectId.set(project.projectId, project);
2861
+ const currentCheckouts = allManifestCheckouts();
2862
+ const currentCheckoutKeys = new Set(
2863
+ currentCheckouts.map(({ projectId, branchName }) => manifestCheckoutKey(projectId, branchName))
2864
+ );
2865
+ for (const key of pendingManifestCheckouts.keys()) {
2866
+ if (!currentCheckoutKeys.has(key)) pendingManifestCheckouts.delete(key);
2867
+ }
2868
+ for (const checkout of currentCheckouts) {
2869
+ const key = manifestCheckoutKey(checkout.projectId, checkout.branchName);
2870
+ const branchPath = path.join(projectRootFor(projectsRoot, checkout.projectId, manifestByProjectId), checkout.branchName);
2871
+ if (!hasNormalVisibleGitDir(branchPath)) pendingManifestCheckouts.set(key, checkout);
2872
+ }
2873
+ if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
2874
+ const created = firstBootstrap || migratedProjectIds.length > 0 ? ensureVisibleWorktrees(currentCheckouts, { strictCanonicalRevalidation: true }) : [];
2875
+ if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
2876
+ const bootstrapped = await convergeWorkspaceHead(workspaceSyncInput({ type: "connect" }, { newVisibleCheckouts: created }), {
2877
+ // An existing checkout becomes routable after a fetch. Its
2878
+ // visible tree converges asynchronously; first bootstrap alone
2879
+ // must materialize the canonical snapshot before readiness.
2880
+ // Re-check the incident immediately before convergence so a
2881
+ // concurrently delivered fence forces fetch-only behavior.
2882
+ hydrateVisible: firstBootstrap && !activeWorkspaceIncidentId
2883
+ });
2884
+ if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
2885
+ workspaceConvergence.setLocalHead(bootstrapped.localHead);
2886
+ workspaceConvergence.observeCanonicalHead(bootstrapped.canonicalHead);
2887
+ for (const checkout of currentCheckouts) {
2888
+ const manifest = manifestByProjectId.get(checkout.projectId);
2889
+ if (!manifest) continue;
2890
+ const branchPath = path.join(projectRootFor(projectsRoot, checkout.projectId, manifestByProjectId), checkout.branchName);
2891
+ if (hasNormalVisibleGitDir(branchPath)) {
2892
+ pendingManifestCheckouts.delete(manifestCheckoutKey(checkout.projectId, checkout.branchName));
2942
2893
  }
2943
- process.stdout.write(
2944
- `[r5d-worker] workspace manifest: ${message.projects.length} projects${migratedProjectIds.length > 0 ? `; migrated ${migratedProjectIds.length} project checkout root(s)` : ""}
2894
+ }
2895
+ process.stdout.write(
2896
+ `[r5d-worker] workspace manifest: ${message.projects.length} projects${migratedProjectIds.length > 0 ? `; migrated ${migratedProjectIds.length} project checkout root(s)` : ""}
2945
2897
  `
2946
- );
2947
- return true;
2948
- });
2949
- } finally {
2950
- releaseManifestHydration?.();
2951
- }
2898
+ );
2899
+ return true;
2900
+ });
2952
2901
  if (!completed || !workspaceManifestRevision.complete(revisionToken)) return;
2953
2902
  workspaceReady = true;
2954
2903
  sendWorkspaceReady();
2955
2904
  schedulePendingManifestCheckoutHydration();
2956
2905
  if (!workspaceSafetyScan) {
2957
2906
  workspaceSafetyScan = setInterval(() => {
2958
- if (!workspaceReady || !workspaceRemoteUrl || activeWorkspaceIncidentId || pendingCheckpoint || workspaceSafetyScanInFlight) {
2907
+ if (!workspaceReady || !workspaceRemoteUrl || activeWorkspaceIncidentId || pendingPublication || workspaceSafetyScanInFlight) {
2959
2908
  return;
2960
2909
  }
2961
2910
  workspaceSafetyScanInFlight = true;
2962
2911
  void (async () => {
2963
2912
  const before = workspaceConvergence.snapshot();
2964
- const mayHydrateVisible = !workerHasActiveWriter() && before.dirtyGeneration === before.publishedGeneration;
2965
- const releaseVisibleHydration = mayHydrateVisible ? workspaceFilesystemWriters.tryAcquireCheckpoint() : null;
2966
- const hydrateVisible = releaseVisibleHydration !== null;
2967
- try {
2968
- const converged = await workspaceSyncSingleFlight.runExclusive(
2969
- () => convergeWorkspaceHead(workspaceSyncInput({ type: "periodic", detail: "idle convergence scan" }), {
2970
- hydrateVisible
2971
- })
2972
- );
2973
- workspaceConvergence.observeCanonicalHead(converged.canonicalHead);
2974
- if (converged.hydrated) workspaceConvergence.setLocalHead(converged.localHead);
2975
- if (converged.localChanges && hydrateVisible) {
2976
- markWorkspaceDirty({ type: "periodic", detail: "idle safety scan" });
2977
- }
2978
- sendWorkspaceReady();
2979
- } finally {
2980
- releaseVisibleHydration?.();
2913
+ const hydrateVisible = before.dirtyGeneration === before.publishedGeneration;
2914
+ const converged = await workspaceSyncSingleFlight.runExclusive(
2915
+ () => convergeWorkspaceHead(workspaceSyncInput({ type: "periodic", detail: "idle convergence scan" }), {
2916
+ hydrateVisible
2917
+ })
2918
+ );
2919
+ workspaceConvergence.observeCanonicalHead(converged.canonicalHead);
2920
+ if (converged.hydrated) workspaceConvergence.setLocalHead(converged.localHead);
2921
+ if (converged.localChanges && hydrateVisible) {
2922
+ markWorkspaceDirty({ type: "periodic", detail: "idle safety scan" });
2981
2923
  }
2924
+ sendWorkspaceReady();
2982
2925
  })().catch((error) => {
2983
2926
  process.stderr.write(
2984
2927
  `[r5d-worker] idle workspace safety scan failed: ${error instanceof Error ? error.message : String(error)}
@@ -2994,7 +2937,7 @@ async function startWorker(options) {
2994
2937
  pendingWorkspaceHead = workspaceConvergence.snapshot().desiredCanonicalHead;
2995
2938
  void convergeAvailableWorkspaceHead();
2996
2939
  if (workspaceConvergence.snapshot().dirtyGeneration > workspaceConvergence.snapshot().publishedGeneration) {
2997
- armWorkspaceCheckpoint({ type: "periodic", detail: "resume dirty generation after reconnect" }, { delayMs: 0 });
2940
+ armWorkspacePublication({ type: "periodic", detail: "resume dirty generation after reconnect" }, { delayMs: 0 });
2998
2941
  }
2999
2942
  return;
3000
2943
  }
@@ -3025,59 +2968,52 @@ async function startWorker(options) {
3025
2968
  void convergeAvailableWorkspaceHead();
3026
2969
  return;
3027
2970
  }
3028
- if (message.type === "workspace_checkpoint_now") {
3029
- armWorkspaceCheckpoint(message.trigger, { delayMs: 0, requestId: message.requestId });
2971
+ if (message.type === "workspace_publication_now") {
2972
+ armWorkspacePublication(message.trigger, { delayMs: 0, requestId: message.requestId });
3030
2973
  return;
3031
2974
  }
3032
- if (message.type === "workspace_checkpoint_authority") {
3033
- const checkpoint = pendingCheckpoint;
3034
- if (!checkpoint || checkpoint.requestId !== message.requestId) {
3035
- process.stderr.write(`[r5d-worker] ignoring checkpoint authority for unknown request ${message.requestId}
2975
+ if (message.type === "workspace_publication_authority") {
2976
+ const publication = pendingPublication;
2977
+ if (!publication || publication.requestId !== message.requestId) {
2978
+ process.stderr.write(`[r5d-worker] ignoring publication authority for unknown request ${message.requestId}
3036
2979
  `);
3037
2980
  return;
3038
2981
  }
3039
2982
  if (message.status !== "granted") {
3040
- checkpoint.releaseCheckpointAdmission();
3041
- pendingCheckpoint = void 0;
2983
+ pendingPublication = void 0;
3042
2984
  workspaceConvergence.observeCanonicalHead(message.canonicalHead);
3043
2985
  const applyBlockedResponse = message.status === "blocked" && workspaceIncidentOrdering.permitsBlockedResponse(message.incidentId);
3044
2986
  if (applyBlockedResponse) {
3045
2987
  workspaceConvergence.block();
3046
2988
  if (message.incidentId) activeWorkspaceIncidentId = message.incidentId;
3047
2989
  } else {
3048
- workspaceConvergence.resetTransientCheckpointState();
2990
+ workspaceConvergence.resetTransientPublicationState();
3049
2991
  workspaceConvergence.observeIncidentState(Boolean(activeWorkspaceIncidentId));
3050
2992
  if (message.status === "busy") {
3051
- armWorkspaceCheckpoint(checkpoint.trigger, {
2993
+ armWorkspacePublication(publication.trigger, {
3052
2994
  delayMs: message.retryAfterMs ?? 1e3,
3053
- requestId: checkpoint.requestId
2995
+ requestId: publication.requestId
3054
2996
  });
3055
2997
  } else if (!activeWorkspaceIncidentId) {
3056
- const explicit = explicitWorkspaceCheckpoints.next();
2998
+ const explicit = explicitWorkspacePublications.next();
3057
2999
  if (explicit) {
3058
- armWorkspaceCheckpoint(explicit.trigger, { delayMs: 0, requestId: explicit.requestId });
3000
+ armWorkspacePublication(explicit.trigger, { delayMs: 0, requestId: explicit.requestId });
3059
3001
  } else if (workspaceConvergence.snapshot().dirtyGeneration > workspaceConvergence.snapshot().publishedGeneration) {
3060
- armWorkspaceCheckpoint(checkpoint.trigger, { delayMs: 0 });
3002
+ armWorkspacePublication(publication.trigger, { delayMs: 0 });
3061
3003
  }
3062
3004
  }
3063
3005
  }
3064
3006
  sendWorkspaceReady();
3065
3007
  return;
3066
3008
  }
3067
- checkpoint.attemptId = message.attemptId;
3068
- checkpoint.authorityToken = message.authorityToken;
3069
- checkpoint.workInFlight = true;
3070
- pendingCheckpoint = checkpoint;
3071
- if (!checkpoint.admissionHeld) {
3072
- const releaseCheckpointAdmission = await workspaceFilesystemWriters.acquireCheckpoint();
3073
- if (currentWorkerSocket !== ws || pendingCheckpoint?.requestId !== checkpoint.requestId || pendingCheckpoint.attemptId !== message.attemptId) {
3074
- releaseCheckpointAdmission();
3075
- checkpoint.workInFlight = false;
3076
- workspaceConvergence.resetTransientCheckpointState();
3077
- return;
3078
- }
3079
- checkpoint.admissionHeld = true;
3080
- checkpoint.releaseCheckpointAdmission = releaseCheckpointAdmission;
3009
+ publication.attemptId = message.attemptId;
3010
+ publication.authorityToken = message.authorityToken;
3011
+ publication.workInFlight = true;
3012
+ pendingPublication = publication;
3013
+ if (currentWorkerSocket !== ws) {
3014
+ publication.workInFlight = false;
3015
+ workspaceConvergence.resetTransientPublicationState();
3016
+ return;
3081
3017
  }
3082
3018
  workspaceConvergence.beginPreparing();
3083
3019
  sendWorkspaceReady();
@@ -3089,11 +3025,11 @@ async function startWorker(options) {
3089
3025
  result = await workspaceSyncSingleFlight.runExclusive(async () => {
3090
3026
  prepareStartedAt = Date.now();
3091
3027
  const pendingTargets = [...pendingManifestCheckouts.values()];
3092
- const createdCheckouts = ensureVisibleWorkspaceCheckouts(pendingTargets, { strictCanonicalRevalidation: true });
3028
+ const createdCheckouts = ensureVisibleWorktrees(pendingTargets, { strictCanonicalRevalidation: true });
3093
3029
  synchronizeStartedAt = Date.now();
3094
3030
  synchronizeStarted = true;
3095
3031
  const prepared = await synchronizeWorkspace(
3096
- workspaceSyncInput(checkpoint.trigger, {
3032
+ workspaceSyncInput(publication.trigger, {
3097
3033
  attemptId: message.attemptId,
3098
3034
  quarantineRef: message.quarantineRef,
3099
3035
  expectedCanonicalHead: message.expectedHead,
@@ -3111,7 +3047,7 @@ async function startWorker(options) {
3111
3047
  type: "workspace_sync",
3112
3048
  attemptId: message.attemptId,
3113
3049
  workerLabel: label,
3114
- trigger: checkpoint.trigger,
3050
+ trigger: publication.trigger,
3115
3051
  outcome: "failed",
3116
3052
  startingHead: message.expectedHead,
3117
3053
  expectedHead: message.expectedHead,
@@ -3128,9 +3064,9 @@ async function startWorker(options) {
3128
3064
  const finishedAt = Date.now();
3129
3065
  result = {
3130
3066
  ...result,
3131
- dirtyGeneration: checkpoint.dirtyGeneration,
3132
- telemetry: workspaceCheckpointTelemetry({
3133
- requestedAt: checkpoint.requestedAt,
3067
+ dirtyGeneration: publication.dirtyGeneration,
3068
+ telemetry: workspacePublicationTelemetry({
3069
+ requestedAt: publication.requestedAt,
3134
3070
  prepareStartedAt,
3135
3071
  synchronizeStartedAt,
3136
3072
  finishedAt
@@ -3141,36 +3077,35 @@ async function startWorker(options) {
3141
3077
  try {
3142
3078
  ws.send(
3143
3079
  JSON.stringify({
3144
- type: "workspace_checkpoint_result",
3145
- requestId: checkpoint.requestId,
3080
+ type: "workspace_publication_result",
3081
+ requestId: publication.requestId,
3146
3082
  attemptId: message.attemptId,
3147
3083
  authorityToken: message.authorityToken,
3148
- dirtyGeneration: checkpoint.dirtyGeneration,
3084
+ dirtyGeneration: publication.dirtyGeneration,
3149
3085
  result
3150
3086
  })
3151
3087
  );
3152
3088
  } catch (error) {
3153
- workspaceConvergence.resetTransientCheckpointState();
3089
+ workspaceConvergence.resetTransientPublicationState();
3154
3090
  throw error;
3155
3091
  } finally {
3156
- checkpoint.workInFlight = false;
3157
- checkpoint.releaseCheckpointAdmission();
3158
- if (currentWorkerSocket !== ws) workspaceConvergence.resetTransientCheckpointState();
3092
+ publication.workInFlight = false;
3093
+ if (currentWorkerSocket !== ws) workspaceConvergence.resetTransientPublicationState();
3159
3094
  }
3160
3095
  return;
3161
3096
  }
3162
- if (message.type === "workspace_checkpoint_completed") {
3163
- const checkpoint = pendingCheckpoint;
3164
- if (!checkpoint || checkpoint.requestId !== message.requestId || checkpoint.attemptId !== message.attemptId) {
3165
- process.stderr.write(`[r5d-worker] ignoring completion for unknown checkpoint ${message.requestId}
3097
+ if (message.type === "workspace_publication_completed") {
3098
+ const publication = pendingPublication;
3099
+ if (!publication || publication.requestId !== message.requestId || publication.attemptId !== message.attemptId) {
3100
+ process.stderr.write(`[r5d-worker] ignoring completion for unknown publication ${message.requestId}
3166
3101
  `);
3167
3102
  return;
3168
3103
  }
3169
- pendingCheckpoint = void 0;
3104
+ pendingPublication = void 0;
3170
3105
  workspaceConvergence.observeCanonicalHead(message.canonicalHead);
3171
3106
  if (message.status === "published" || message.status === "no_change") {
3172
3107
  workspaceConvergence.complete({
3173
- generation: Math.min(message.publishedGeneration ?? checkpoint.dirtyGeneration, checkpoint.dirtyGeneration),
3108
+ generation: Math.min(message.publishedGeneration ?? publication.dirtyGeneration, publication.dirtyGeneration),
3174
3109
  canonicalHead: message.canonicalHead,
3175
3110
  published: true
3176
3111
  });
@@ -3178,18 +3113,18 @@ async function startWorker(options) {
3178
3113
  workspaceConvergence.block();
3179
3114
  if (message.incidentId) activeWorkspaceIncidentId = message.incidentId;
3180
3115
  } else {
3181
- workspaceConvergence.resetTransientCheckpointState();
3116
+ workspaceConvergence.resetTransientPublicationState();
3182
3117
  workspaceConvergence.observeIncidentState(Boolean(activeWorkspaceIncidentId));
3183
3118
  }
3184
3119
  sendWorkspaceReady();
3185
- const explicit = message.status === "retry" && checkpoint.explicit ? { requestId: checkpoint.requestId, trigger: checkpoint.trigger } : explicitWorkspaceCheckpoints.next();
3120
+ const explicit = message.status === "retry" && publication.explicit ? { requestId: publication.requestId, trigger: publication.trigger } : explicitWorkspacePublications.next();
3186
3121
  if (explicit) {
3187
- armWorkspaceCheckpoint(explicit.trigger, {
3122
+ armWorkspacePublication(explicit.trigger, {
3188
3123
  delayMs: message.retryAfterMs ?? 0,
3189
3124
  requestId: explicit.requestId
3190
3125
  });
3191
3126
  } else if (message.status === "retry" || message.status === "failed" || workspaceConvergence.snapshot().dirtyGeneration > workspaceConvergence.snapshot().publishedGeneration) {
3192
- armWorkspaceCheckpoint(checkpoint.trigger, { delayMs: message.retryAfterMs ?? 1e3 });
3127
+ armWorkspacePublication(publication.trigger, { delayMs: message.retryAfterMs ?? 1e3 });
3193
3128
  }
3194
3129
  if (message.canonicalHead !== workspaceConvergence.snapshot().localHead) {
3195
3130
  pendingWorkspaceHead = message.canonicalHead;
@@ -3237,11 +3172,11 @@ async function startWorker(options) {
3237
3172
  pendingWorkspaceHead = workspaceConvergence.snapshot().desiredCanonicalHead;
3238
3173
  void convergeAvailableWorkspaceHead();
3239
3174
  }
3240
- const explicit = !activeWorkspaceIncidentId ? explicitWorkspaceCheckpoints.next() : void 0;
3175
+ const explicit = !activeWorkspaceIncidentId ? explicitWorkspacePublications.next() : void 0;
3241
3176
  if (explicit) {
3242
- armWorkspaceCheckpoint(explicit.trigger, { delayMs: 0, requestId: explicit.requestId });
3177
+ armWorkspacePublication(explicit.trigger, { delayMs: 0, requestId: explicit.requestId });
3243
3178
  } else if (!activeWorkspaceIncidentId && workspaceConvergence.snapshot().dirtyGeneration > workspaceConvergence.snapshot().publishedGeneration) {
3244
- armWorkspaceCheckpoint({ type: "periodic", detail: "resume after workspace incident" }, { delayMs: 0 });
3179
+ armWorkspacePublication({ type: "periodic", detail: "resume after workspace incident" }, { delayMs: 0 });
3245
3180
  }
3246
3181
  if (!activeWorkspaceIncidentId) schedulePendingManifestCheckoutHydration();
3247
3182
  sendWorkspaceReady();
@@ -3342,10 +3277,8 @@ async function startWorker(options) {
3342
3277
  });
3343
3278
  return;
3344
3279
  }
3345
- const releaseWorkspaceWriter = targetMayMutateVisibleWorkspace(message.target) ? await workspaceFilesystemWriters.acquireWriter() : void 0;
3346
3280
  const releaseWorkspaceMutation = await acquireWorkspaceCommandMutation(message.target, workspaceSyncSingleFlight);
3347
3281
  let mutationLeaseTransferred = false;
3348
- let writerLeaseTransferred = false;
3349
3282
  try {
3350
3283
  const resolvedTarget = resolveMessageTarget(message.target);
3351
3284
  process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${describeWorkerSessionTarget(message.target)}
@@ -3358,16 +3291,11 @@ async function startWorker(options) {
3358
3291
  ...releaseWorkspaceMutation ? { releaseWorkspaceMutation } : {},
3359
3292
  ...targetMayMutateVisibleWorkspace(message.target) ? {
3360
3293
  onTerminal: () => {
3361
- markWorkspaceDirty(
3362
- { type: "process_terminal", detail: `pty ${message.ptyId} completed` },
3363
- true
3364
- );
3365
- releaseWorkspaceWriter?.();
3294
+ markWorkspaceDirty({ type: "process_terminal", detail: `pty ${message.ptyId} completed` }, true);
3366
3295
  }
3367
3296
  } : {}
3368
3297
  });
3369
3298
  mutationLeaseTransferred = releaseWorkspaceMutation !== void 0;
3370
- writerLeaseTransferred = releaseWorkspaceWriter !== void 0;
3371
3299
  } catch (error) {
3372
3300
  sendWorkerMessage(ws, {
3373
3301
  type: "pty_error",
@@ -3377,7 +3305,6 @@ async function startWorker(options) {
3377
3305
  });
3378
3306
  } finally {
3379
3307
  if (!mutationLeaseTransferred) releaseWorkspaceMutation?.();
3380
- if (!writerLeaseTransferred) releaseWorkspaceWriter?.();
3381
3308
  }
3382
3309
  return;
3383
3310
  }
@@ -3397,7 +3324,7 @@ async function startWorker(options) {
3397
3324
  const activePty = activePtys.get(message.ptyId);
3398
3325
  closePty(message);
3399
3326
  if (activePty && targetMayMutateVisibleWorkspace(activePty.target)) {
3400
- armWorkspaceCheckpoint({ type: "process_terminal", detail: `pty ${message.ptyId} closed` }, { delayMs: 0 });
3327
+ armWorkspacePublication({ type: "process_terminal", detail: `pty ${message.ptyId} closed` }, { delayMs: 0 });
3401
3328
  }
3402
3329
  return;
3403
3330
  }
@@ -3412,36 +3339,31 @@ async function startWorker(options) {
3412
3339
  });
3413
3340
  return;
3414
3341
  }
3415
- const releaseWorkspaceWriter = targetMayMutateVisibleWorkspace(message.target) ? await workspaceFilesystemWriters.acquireWriter() : void 0;
3342
+ let result;
3416
3343
  try {
3417
- let result;
3418
- try {
3419
- const runCommand = async () => {
3420
- const resolvedTarget = resolveMessageTarget(message.target);
3421
- process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
3344
+ const runCommand = async () => {
3345
+ const resolvedTarget = resolveMessageTarget(message.target);
3346
+ process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
3422
3347
  `);
3423
- return await executeCommand({ message, resolvedTarget, baseUrl, token, artifactRoot, planRoot });
3424
- };
3425
- result = await runWorkspaceCommand(message.target, workspaceSyncSingleFlight, runCommand);
3426
- } catch (error) {
3427
- result = {
3428
- type: "exec_result",
3429
- requestId: message.requestId,
3430
- stdout: "",
3431
- stderr: "",
3432
- exitCode: 1,
3433
- error: error instanceof Error ? error.message : String(error)
3434
- };
3435
- }
3436
- ws.send(JSON.stringify(result));
3437
- if (targetMayMutateVisibleWorkspace(message.target)) {
3438
- markWorkspaceDirty(
3439
- { type: "shell_inline", sessionId: message.sessionId, processRunId: message.runId, detail: "foreground command completed" },
3440
- true
3441
- );
3442
- }
3443
- } finally {
3444
- releaseWorkspaceWriter?.();
3348
+ return await executeCommand({ message, resolvedTarget, baseUrl, token, artifactRoot, planRoot });
3349
+ };
3350
+ result = await runWorkspaceCommand(message.target, workspaceSyncSingleFlight, runCommand);
3351
+ } catch (error) {
3352
+ result = {
3353
+ type: "exec_result",
3354
+ requestId: message.requestId,
3355
+ stdout: "",
3356
+ stderr: "",
3357
+ exitCode: 1,
3358
+ error: error instanceof Error ? error.message : String(error)
3359
+ };
3360
+ }
3361
+ ws.send(JSON.stringify(result));
3362
+ if (targetMayMutateVisibleWorkspace(message.target)) {
3363
+ markWorkspaceDirty(
3364
+ { type: "shell_inline", sessionId: message.sessionId, processRunId: message.runId, detail: "foreground command completed" },
3365
+ true
3366
+ );
3445
3367
  }
3446
3368
  return;
3447
3369
  }
@@ -3461,7 +3383,6 @@ async function startWorker(options) {
3461
3383
  runId: message.runId
3462
3384
  });
3463
3385
  const runCommand = async () => {
3464
- const releaseWorkspaceWriter = targetMayMutateVisibleWorkspace(message.target) ? await workspaceFilesystemWriters.acquireWriter() : void 0;
3465
3386
  try {
3466
3387
  const resolvedTarget = resolveMessageTarget(message.target);
3467
3388
  process.stdout.write(`[r5d-worker] exec_start ${message.runId}: ${message.argv.join(" ")}
@@ -3487,7 +3408,6 @@ async function startWorker(options) {
3487
3408
  true
3488
3409
  );
3489
3410
  }
3490
- releaseWorkspaceWriter?.();
3491
3411
  }
3492
3412
  };
3493
3413
  const execution = runWorkspaceCommand(message.target, workspaceSyncSingleFlight, runCommand);
@@ -3502,46 +3422,41 @@ async function startWorker(options) {
3502
3422
  return;
3503
3423
  }
3504
3424
  if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes") {
3505
- const releaseWorkspaceWriter = (message.type === "write" || message.type === "edit") && targetMayMutateVisibleWorkspace(message.target) ? await workspaceFilesystemWriters.acquireWriter() : void 0;
3506
3425
  try {
3507
- try {
3508
- const result = await workspaceSyncSingleFlight.runMutation(async () => {
3509
- const resolvedTarget = resolveMessageTarget(message.target);
3510
- return await executeOperation({
3511
- message,
3512
- resolvedTarget,
3513
- baseUrl,
3514
- token,
3515
- artifactRoot,
3516
- planRoot
3517
- });
3426
+ const result = await runWorkspaceCommand(message.target, workspaceSyncSingleFlight, async () => {
3427
+ const resolvedTarget = resolveMessageTarget(message.target);
3428
+ return await executeOperation({
3429
+ message,
3430
+ resolvedTarget,
3431
+ baseUrl,
3432
+ token,
3433
+ artifactRoot,
3434
+ planRoot
3435
+ });
3436
+ });
3437
+ ws.send(
3438
+ JSON.stringify({
3439
+ type: "operation_result",
3440
+ requestId: message.requestId,
3441
+ result
3442
+ })
3443
+ );
3444
+ if ((message.type === "write" || message.type === "edit") && targetMayMutateVisibleWorkspace(message.target)) {
3445
+ markWorkspaceDirty({
3446
+ type: message.type,
3447
+ sessionId: message.sessionId,
3448
+ toolCallId: message.requestId,
3449
+ ...message.target.type === "project" ? { projectId: message.target.projectId, branchName: message.target.branchName } : {}
3518
3450
  });
3519
- ws.send(
3520
- JSON.stringify({
3521
- type: "operation_result",
3522
- requestId: message.requestId,
3523
- result
3524
- })
3525
- );
3526
- if ((message.type === "write" || message.type === "edit") && targetMayMutateVisibleWorkspace(message.target)) {
3527
- markWorkspaceDirty({
3528
- type: message.type,
3529
- sessionId: message.sessionId,
3530
- toolCallId: message.requestId,
3531
- ...message.target.type === "project" ? { projectId: message.target.projectId, branchName: message.target.branchName } : {}
3532
- });
3533
- }
3534
- } catch (error) {
3535
- ws.send(
3536
- JSON.stringify({
3537
- type: "operation_result",
3538
- requestId: message.requestId,
3539
- error: error instanceof Error ? error.message : String(error)
3540
- })
3541
- );
3542
3451
  }
3543
- } finally {
3544
- releaseWorkspaceWriter?.();
3452
+ } catch (error) {
3453
+ ws.send(
3454
+ JSON.stringify({
3455
+ type: "operation_result",
3456
+ requestId: message.requestId,
3457
+ error: error instanceof Error ? error.message : String(error)
3458
+ })
3459
+ );
3545
3460
  }
3546
3461
  return;
3547
3462
  }
@@ -3566,13 +3481,12 @@ async function startWorker(options) {
3566
3481
  process.off("SIGTERM", handleGracefulShutdown);
3567
3482
  process.off("SIGINT", handleGracefulShutdown);
3568
3483
  stopHeartbeatWatchdog();
3569
- if (workspaceCheckpointTimer) {
3570
- clearTimeout(workspaceCheckpointTimer);
3571
- workspaceCheckpointTimer = void 0;
3484
+ if (workspacePublicationTimer) {
3485
+ clearTimeout(workspacePublicationTimer);
3486
+ workspacePublicationTimer = void 0;
3572
3487
  }
3573
- if (!pendingCheckpoint?.workInFlight) {
3574
- pendingCheckpoint?.releaseCheckpointAdmission();
3575
- workspaceConvergence.resetTransientCheckpointState();
3488
+ if (!pendingPublication?.workInFlight) {
3489
+ workspaceConvergence.resetTransientPublicationState();
3576
3490
  }
3577
3491
  if (workspaceSafetyScan) {
3578
3492
  clearInterval(workspaceSafetyScan);