@ricsam/r5d-worker 0.0.83 → 0.0.87

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/cjs/main.cjs CHANGED
@@ -839,6 +839,33 @@ function tryGit(args, options = {}) {
839
839
  return false;
840
840
  }
841
841
  }
842
+ async function runGitAsync(args, options = {}) {
843
+ const command = workerGitCommand.commandArgs(args);
844
+ const subprocess = Bun.spawn(command, {
845
+ cwd: options.cwd,
846
+ stdin: "ignore",
847
+ stdout: "pipe",
848
+ stderr: "pipe",
849
+ env: (0, import_git_process_environment.workerGitProcessEnvironment)()
850
+ });
851
+ const [stdout, stderr, exitCode] = await Promise.all([
852
+ new Response(subprocess.stdout).text(),
853
+ new Response(subprocess.stderr).text(),
854
+ subprocess.exited
855
+ ]);
856
+ if (exitCode !== 0) {
857
+ throw new Error(`git ${args.join(" ")} failed: ${stderr.trim() || stdout.trim() || `exit ${exitCode}`}`);
858
+ }
859
+ return stdout.trim();
860
+ }
861
+ async function tryGitAsync(args, options = {}) {
862
+ try {
863
+ await runGitAsync(args, options);
864
+ return true;
865
+ } catch {
866
+ return false;
867
+ }
868
+ }
842
869
  function getGitBlobHashForContent(content) {
843
870
  const buffer = typeof content === "string" ? Buffer.from(content, "utf8") : content;
844
871
  return (0, import_node_crypto.createHash)("sha1").update(`blob ${buffer.length}\0`).update(buffer).digest("hex");
@@ -1535,10 +1562,20 @@ function workspaceConfigurationIncidentDeferral(input) {
1535
1562
  return { incidentId };
1536
1563
  }
1537
1564
  function workspaceIncidentTerminalClearDisposition(input) {
1538
- if (!input.deferredConfiguration) return "ordinary_resume";
1539
- void input.clearedIncidentId;
1565
+ if (!input.deferredConfiguration) return "await_server_refresh";
1566
+ if (input.deferredConfiguration.incidentId !== input.clearedIncidentId) return "reconnect_for_refresh";
1540
1567
  return input.deferredConfiguration.serverRefreshExpected ? "await_server_refresh" : "reconnect_for_refresh";
1541
1568
  }
1569
+ function workspaceIncidentTerminalRefreshFence(input) {
1570
+ const disposition = workspaceIncidentTerminalClearDisposition(input);
1571
+ return {
1572
+ disposition,
1573
+ deferredConfiguration: input.deferredConfiguration ?? { incidentId: input.clearedIncidentId, serverRefreshExpected: true }
1574
+ };
1575
+ }
1576
+ function workspaceAutomaticSyncIsAdmitted(input) {
1577
+ return input.workspaceConfigured && input.activeIncidentId === null && input.deferredConfiguration === null;
1578
+ }
1542
1579
  function deferredWorkspaceTargetIsAllowed(deferredConfiguration, activeIncidentId, target) {
1543
1580
  const canonicalTarget = target.type === "workspace" && target.rootProfile === "canonical_sync";
1544
1581
  if (deferredConfiguration) return activeIncidentId === deferredConfiguration.incidentId && canonicalTarget;
@@ -1560,6 +1597,9 @@ function deferredCredentialTransitionMustWait(input) {
1560
1597
  function workspaceConfigurationIncidentSnapshotIsCurrent(capturedIncidentId, activeIncidentId) {
1561
1598
  return capturedIncidentId === null ? activeIncidentId === null : activeIncidentId === null || activeIncidentId === capturedIncidentId;
1562
1599
  }
1600
+ function workspaceConfigurationResetToCanonicalIsAllowed(requested, incidentId) {
1601
+ return requested === true && incidentId === null;
1602
+ }
1563
1603
  function deferredWorkspaceRefreshWatchdogIsCurrent(input) {
1564
1604
  return input.deferredConfiguration?.incidentId === input.capturedIncidentId && input.activeIncidentId === null;
1565
1605
  }
@@ -1632,12 +1672,15 @@ const workerGitSecurityTestHarness = {
1632
1672
  fenceUnsafeWorkspaceSyncFailure,
1633
1673
  workspaceConfigurationIncidentDeferral,
1634
1674
  workspaceIncidentTerminalClearDisposition,
1675
+ workspaceIncidentTerminalRefreshFence,
1676
+ workspaceAutomaticSyncIsAdmitted,
1635
1677
  deferredWorkspaceTargetIsAllowed,
1636
1678
  deferredWorkspaceSyncTriggerIsAllowed,
1637
1679
  workspaceOperationsAreFenced,
1638
1680
  workspaceCommandTransportIsAllowed,
1639
1681
  deferredCredentialTransitionMustWait,
1640
1682
  workspaceConfigurationIncidentSnapshotIsCurrent,
1683
+ workspaceConfigurationResetToCanonicalIsAllowed,
1641
1684
  deferredWorkspaceRefreshWatchdogIsCurrent,
1642
1685
  incidentDeferredProjectCheckouts,
1643
1686
  deferredIncidentWorkspaceConfigurationResult,
@@ -3398,6 +3441,11 @@ async function startWorker(options) {
3398
3441
  let cliUpdateInProgress = false;
3399
3442
  let reloadAfterClose = false;
3400
3443
  let shutdownAfterClose = false;
3444
+ const automaticWorkspaceSyncIsCurrentlyAdmitted = () => workspaceAutomaticSyncIsAdmitted({
3445
+ workspaceConfigured,
3446
+ activeIncidentId: activeWorkspaceIncidentId,
3447
+ deferredConfiguration: deferredWorkspaceConfiguration
3448
+ });
3401
3449
  const bearerAuthHeader = `Authorization: Bearer ${token}`;
3402
3450
  const workerCredentialUsername = credentialUsernameForAuthHeader(bearerAuthHeader);
3403
3451
  const projectBranchKey = (projectId, branchName) => `${projectId}\0${branchName}`;
@@ -3679,6 +3727,17 @@ async function startWorker(options) {
3679
3727
  const activeTargets = activeWorkspaceMutationTargets();
3680
3728
  return projectConfigById.get(projectId)?.executionDisabled === true || hasProjectWorktree(branchPath) && (0, import_project_worktrees.projectWorktreeOperationInProgress)(branchPath) || canonicalWorkspaceMutationIsActive(activeTargets) || (0, import_workspace_automatic_sync_policy.projectBranchHasActiveWorkspaceTarget)(projectId, branchName, activeTargets);
3681
3729
  };
3730
+ const projectBranchMountExclusiveSyncBusy = (projectId, branchName, branchPath) => {
3731
+ const activeTargets = activeWorkspaceMutationTargets();
3732
+ return (0, import_workspace_automatic_sync_policy.projectBranchMountBusyDuringExclusiveSync)({
3733
+ projectId,
3734
+ branchName,
3735
+ executionDisabled: projectConfigById.get(projectId)?.executionDisabled === true,
3736
+ worktreeOperationInProgress: hasProjectWorktree(branchPath) && (0, import_project_worktrees.projectWorktreeOperationInProgress)(branchPath),
3737
+ retainedCanonicalProcessGroup: canonicalWorkspaceMutationIsActive([...credentialBearingProcessGroupTargets.values()]),
3738
+ activeTargets
3739
+ });
3740
+ };
3682
3741
  const projectBranchMountBusy = (projectId, branchName, branchPath) => !readyProjectIds.has(projectId) || projectBranchMountActivityBusy(projectId, branchName, branchPath);
3683
3742
  const buildWorkspaceMounts = (projects = projectConfigById.values()) => {
3684
3743
  const mounts = [];
@@ -3711,7 +3770,7 @@ async function startWorker(options) {
3711
3770
  preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
3712
3771
  busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
3713
3772
  mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
3714
- busyForRecovery: () => projectBranchMountActivityBusy(project.projectId, branch.branchName, branchPath)
3773
+ busyForRecovery: () => projectBranchMountExclusiveSyncBusy(project.projectId, branch.branchName, branchPath)
3715
3774
  });
3716
3775
  mounts.push({
3717
3776
  id: projectPlanMountId(project.projectId, branch.branchName),
@@ -3725,7 +3784,7 @@ async function startWorker(options) {
3725
3784
  preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
3726
3785
  busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
3727
3786
  mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
3728
- busyForRecovery: () => projectBranchMountActivityBusy(project.projectId, branch.branchName, branchPath)
3787
+ busyForRecovery: () => projectBranchMountExclusiveSyncBusy(project.projectId, branch.branchName, branchPath)
3729
3788
  });
3730
3789
  }
3731
3790
  void projectRoot;
@@ -3748,7 +3807,10 @@ async function startWorker(options) {
3748
3807
  mutationToken: () => String(visibleWorkspaceMutationEpoch),
3749
3808
  busyForRecovery: () => {
3750
3809
  const activeTargets = activeWorkspaceMutationTargets();
3751
- return canonicalWorkspaceMutationIsActive(activeTargets) || (0, import_workspace_automatic_sync_policy.hasActiveVisibleProjectsWorkspaceTarget)(activeTargets);
3810
+ return (0, import_workspace_automatic_sync_policy.workspacePlansMountBusyDuringExclusiveSync)(
3811
+ activeTargets,
3812
+ canonicalWorkspaceMutationIsActive([...credentialBearingProcessGroupTargets.values()])
3813
+ );
3752
3814
  }
3753
3815
  });
3754
3816
  for (const deletion of pendingMirrorDeletes.values()) {
@@ -3775,10 +3837,14 @@ async function startWorker(options) {
3775
3837
  }
3776
3838
  return mounts;
3777
3839
  };
3778
- const workspaceLocalHead = () => import_node_fs.default.existsSync(import_node_path.default.join(workspaceShadowRoot, ".git")) && tryGit(["rev-parse", "--verify", "HEAD"], { cwd: workspaceShadowRoot }) ? runGit(["rev-parse", "HEAD"], { cwd: workspaceShadowRoot }) : null;
3779
- const workspaceGitStatus = () => {
3840
+ const workspaceLocalHead = async () => {
3841
+ if (!import_node_fs.default.existsSync(import_node_path.default.join(workspaceShadowRoot, ".git"))) return null;
3842
+ if (!await tryGitAsync(["rev-parse", "--verify", "HEAD"], { cwd: workspaceShadowRoot })) return null;
3843
+ return runGitAsync(["rev-parse", "HEAD"], { cwd: workspaceShadowRoot });
3844
+ };
3845
+ const workspaceGitStatus = async () => {
3780
3846
  try {
3781
- return runGit(["status", "--porcelain"], { cwd: workspaceShadowRoot });
3847
+ return await runGitAsync(["status", "--porcelain"], { cwd: workspaceShadowRoot });
3782
3848
  } catch {
3783
3849
  return "";
3784
3850
  }
@@ -3791,7 +3857,7 @@ async function startWorker(options) {
3791
3857
  })
3792
3858
  )
3793
3859
  ].sort();
3794
- const ensureConfiguredProjects = () => {
3860
+ const ensureConfiguredProjects = async () => {
3795
3861
  for (const project of projectConfigById.values()) {
3796
3862
  validateProjectId(project.projectId);
3797
3863
  (0, import_repository_transition_policy.assertRepositoryTransitionState)(project);
@@ -3828,7 +3894,7 @@ async function startWorker(options) {
3828
3894
  }
3829
3895
  try {
3830
3896
  const connection = projectConnection(project);
3831
- const states = (0, import_project_worktrees.ensureProjectWorktrees)({
3897
+ const states = await (0, import_project_worktrees.ensureProjectWorktrees)({
3832
3898
  projectRoot,
3833
3899
  snapshotRoot: projectWorktreeSnapshotsRoot,
3834
3900
  primaryBranchName: primaryProjectBranch(project),
@@ -3877,13 +3943,13 @@ async function startWorker(options) {
3877
3943
  }
3878
3944
  }
3879
3945
  };
3880
- const collectAheadOfOriginBranches = () => {
3946
+ const collectAheadOfOriginBranches = async () => {
3881
3947
  const aheadBranches = [];
3882
3948
  for (const project of projectConfigById.values()) {
3883
3949
  if (project.executionDisabled || !readyProjectIds.has(project.projectId) || project.branches.length === 0) continue;
3884
3950
  const connection = projectConnection(project);
3885
3951
  const primaryPath = configuredProjectBranchPath(projectsRoot, project, primaryProjectBranch(project));
3886
- if (!tryGit(
3952
+ if (!await tryGitAsync(
3887
3953
  [
3888
3954
  ...(0, import_git_process_environment.gitTransportSecurityArgs)(connection.originUrl, connection.credentialHelper, connection.originCredentialUsername),
3889
3955
  "fetch",
@@ -3907,11 +3973,11 @@ async function startWorker(options) {
3907
3973
  primaryBranchName: primaryProjectBranch(project),
3908
3974
  defaultBranch: project.defaultBranch
3909
3975
  })}`;
3910
- if (tryGit(["show-ref", "--verify", "--quiet", originRef], { cwd: branchPath })) {
3911
- const counts = runGit(["rev-list", "--left-right", "--count", `${originRef}...HEAD`], { cwd: branchPath }).split(/\s+/).map(Number);
3976
+ if (await tryGitAsync(["show-ref", "--verify", "--quiet", originRef], { cwd: branchPath })) {
3977
+ const counts = (await runGitAsync(["rev-list", "--left-right", "--count", `${originRef}...HEAD`], { cwd: branchPath })).split(/\s+/).map(Number);
3912
3978
  ahead = Number.isSafeInteger(counts[1]) ? counts[1] : 0;
3913
- } else if (tryGit(["cat-file", "-e", `${branch.baseCommitHash}^{commit}`], { cwd: branchPath })) {
3914
- const count = Number(runGit(["rev-list", "--count", `${branch.baseCommitHash}..HEAD`], { cwd: branchPath }));
3979
+ } else if (await tryGitAsync(["cat-file", "-e", `${branch.baseCommitHash}^{commit}`], { cwd: branchPath })) {
3980
+ const count = Number(await runGitAsync(["rev-list", "--count", `${branch.baseCommitHash}..HEAD`], { cwd: branchPath }));
3915
3981
  ahead = Number.isSafeInteger(count) ? count : 0;
3916
3982
  }
3917
3983
  if (ahead > 0) aheadBranches.push({ projectId: project.projectId, branchName: branch.branchName, ahead });
@@ -3927,19 +3993,19 @@ async function startWorker(options) {
3927
3993
  (left, right) => left.projectId.localeCompare(right.projectId) || left.branchName.localeCompare(right.branchName)
3928
3994
  );
3929
3995
  };
3930
- const captureProjectHeadsForWorkspacePublication = () => {
3996
+ const captureProjectHeadsForWorkspacePublication = async () => {
3931
3997
  const heads = /* @__PURE__ */ new Map();
3932
3998
  for (const project of projectConfigById.values()) {
3933
3999
  if (!readyProjectIds.has(project.projectId)) continue;
3934
4000
  for (const branch of project.branches) {
3935
4001
  const branchPath = configuredProjectBranchPath(projectsRoot, project, branch.branchName);
3936
4002
  if (!hasProjectWorktree(branchPath)) continue;
3937
- heads.set(projectMountId(project.projectId, branch.branchName), runGit(["rev-parse", "HEAD"], { cwd: branchPath }));
4003
+ heads.set(projectMountId(project.projectId, branch.branchName), await runGitAsync(["rev-parse", "HEAD"], { cwd: branchPath }));
3938
4004
  }
3939
4005
  }
3940
4006
  return heads;
3941
4007
  };
3942
- const pushChangedProjectHeads = (activeMountIds, publishedHead, publicationHeads, suppressedMountIds = /* @__PURE__ */ new Set()) => {
4008
+ const pushChangedProjectHeads = async (activeMountIds, publishedHead, publicationHeads, suppressedMountIds = /* @__PURE__ */ new Set()) => {
3943
4009
  const active = new Set(activeMountIds);
3944
4010
  for (const project of projectConfigById.values()) {
3945
4011
  if (!readyProjectIds.has(project.projectId) || !(0, import_repository_transition_policy.repositoryMirrorWritesAllowed)(project)) continue;
@@ -3955,7 +4021,7 @@ async function startWorker(options) {
3955
4021
  }
3956
4022
  if (changed.size === 0) continue;
3957
4023
  const connection = projectConnection(project);
3958
- const results = (0, import_project_worktrees.pushProjectMirrorHeads)({
4024
+ const results = await (0, import_project_worktrees.pushProjectMirrorHeads)({
3959
4025
  projectRoot: configuredProjectRoot(projectsRoot, project),
3960
4026
  branchNames: project.branches.map(({ branchName }) => branchName),
3961
4027
  mirrorUrl: connection.mirrorUrl,
@@ -3986,7 +4052,7 @@ async function startWorker(options) {
3986
4052
  if (!active.has(deletion.id)) continue;
3987
4053
  if (!(0, import_repository_transition_policy.repositoryMirrorWritesAllowed)(projectConfigById.get(deletion.projectId))) continue;
3988
4054
  if (!deletion.projectDeleted) {
3989
- (0, import_project_worktrees.deleteProjectMirrorBranch)({
4055
+ await (0, import_project_worktrees.deleteProjectMirrorBranch)({
3990
4056
  gitDirectory: deletion.gitDirectory,
3991
4057
  branchName: deletion.branchName,
3992
4058
  mirrorUrl: deletion.mirrorUrl,
@@ -4007,7 +4073,7 @@ async function startWorker(options) {
4007
4073
  lastObservedProjectHeads.delete(key);
4008
4074
  }
4009
4075
  };
4010
- const observeProjectHeadsBeforeOuterWorkspace = (input) => {
4076
+ const observeProjectHeadsBeforeOuterWorkspace = async (input) => {
4011
4077
  const bundles = [];
4012
4078
  for (const project of projectConfigById.values()) {
4013
4079
  if (project.executionDisabled || !readyProjectIds.has(project.projectId) || project.branches.length === 0) continue;
@@ -4019,7 +4085,7 @@ async function startWorker(options) {
4019
4085
  projectId: project.projectId,
4020
4086
  projectRoot,
4021
4087
  primaryBranchName,
4022
- observations: (0, import_project_worktrees.observeProjectMirrorHeads)({
4088
+ observations: await (0, import_project_worktrees.observeProjectMirrorHeads)({
4023
4089
  projectRoot,
4024
4090
  primaryBranchName,
4025
4091
  branchNames: project.branches.map(({ branchName }) => branchName),
@@ -4039,7 +4105,7 @@ async function startWorker(options) {
4039
4105
  }
4040
4106
  return bundles;
4041
4107
  };
4042
- const applyObservedProjectHeadsAfterInboundWorkspace = (bundles, activeMountIds, requireObservedHeads) => {
4108
+ const applyObservedProjectHeadsAfterInboundWorkspace = async (bundles, activeMountIds, requireObservedHeads) => {
4043
4109
  const active = new Set(activeMountIds);
4044
4110
  for (const bundle of bundles) {
4045
4111
  const project = projectConfigById.get(bundle.projectId);
@@ -4047,7 +4113,7 @@ async function startWorker(options) {
4047
4113
  const observations = bundle.observations.filter(({ branchName }) => active.has(projectMountId(project.projectId, branchName)));
4048
4114
  if (observations.length === 0) continue;
4049
4115
  try {
4050
- const refreshed = (0, import_project_worktrees.applyObservedProjectMirrorHeads)({
4116
+ const refreshed = await (0, import_project_worktrees.applyObservedProjectMirrorHeads)({
4051
4117
  projectRoot: bundle.projectRoot,
4052
4118
  primaryBranchName: bundle.primaryBranchName,
4053
4119
  observations,
@@ -4057,7 +4123,7 @@ async function startWorker(options) {
4057
4123
  for (const observation of observations) {
4058
4124
  if (!observation.shouldMove || !observation.mirrorHead) continue;
4059
4125
  const checkoutPath = configuredProjectBranchPath(projectsRoot, project, observation.branchName);
4060
- const currentHead = runGit(["rev-parse", "HEAD"], { cwd: checkoutPath });
4126
+ const currentHead = await runGitAsync(["rev-parse", "HEAD"], { cwd: checkoutPath });
4061
4127
  if (currentHead !== observation.mirrorHead) {
4062
4128
  throw new Error(
4063
4129
  `Project ${project.projectId}/${observation.branchName} did not apply observed hidden mirror head ${observation.mirrorHead}`
@@ -4080,7 +4146,7 @@ async function startWorker(options) {
4080
4146
  }
4081
4147
  }
4082
4148
  };
4083
- const mapWorkspaceGitResult = (attemptId, trigger, result) => {
4149
+ const mapWorkspaceGitResult = async (attemptId, trigger, result) => {
4084
4150
  const activeProjectBranchPublications = (0, import_workspace_publication_evidence.activeProjectBranchPublicationEvidence)([...projectConfigById.values()], result.activeMountIds);
4085
4151
  return {
4086
4152
  type: "workspace_sync",
@@ -4093,7 +4159,7 @@ async function startWorker(options) {
4093
4159
  ...result.publishedHead ? { publishedHead: result.publishedHead } : {},
4094
4160
  rebaseCount: result.rebaseCount,
4095
4161
  diffSizeBytes: result.diffSizeBytes,
4096
- gitStatus: workspaceGitStatus(),
4162
+ gitStatus: await workspaceGitStatus(),
4097
4163
  affectedProjects: affectedProjects(result.affectedPaths),
4098
4164
  affectedPaths: result.affectedPaths,
4099
4165
  activeMountIds: result.activeMountIds,
@@ -4108,48 +4174,51 @@ async function startWorker(options) {
4108
4174
  ...result.error ? { error: result.error } : {}
4109
4175
  };
4110
4176
  };
4111
- const failedWorkspaceSyncResult = (attemptId, trigger, error) => ({
4112
- type: "workspace_sync",
4113
- attemptId,
4114
- workerLabel: label,
4115
- trigger,
4116
- outcome: "failed",
4117
- startingHead: workspaceLocalHead(),
4118
- ...workspaceLocalHead() ? { localHead: workspaceLocalHead() } : {},
4119
- rebaseCount: 0,
4120
- diffSizeBytes: 0,
4121
- gitStatus: workspaceGitStatus(),
4122
- affectedProjects: [],
4123
- affectedPaths: [],
4124
- discardedPaths: [],
4125
- localChangesDiscarded: false,
4126
- error: error instanceof Error ? error.message : String(error)
4127
- });
4177
+ const failedWorkspaceSyncResult = async (attemptId, trigger, error) => {
4178
+ const localHead = await workspaceLocalHead();
4179
+ return {
4180
+ type: "workspace_sync",
4181
+ attemptId,
4182
+ workerLabel: label,
4183
+ trigger,
4184
+ outcome: "failed",
4185
+ startingHead: localHead,
4186
+ ...localHead ? { localHead } : {},
4187
+ rebaseCount: 0,
4188
+ diffSizeBytes: 0,
4189
+ gitStatus: await workspaceGitStatus(),
4190
+ affectedProjects: [],
4191
+ affectedPaths: [],
4192
+ discardedPaths: [],
4193
+ localChangesDiscarded: false,
4194
+ error: error instanceof Error ? error.message : String(error)
4195
+ };
4196
+ };
4128
4197
  const performWorkspaceSync = async (input) => {
4129
4198
  if (!workspaceRemoteUrl || !workspaceCredentialHelper || !workspaceCredentialUsername || !workspaceGitIdentity) {
4130
4199
  throw new Error("Worker workspace configuration has not been received");
4131
4200
  }
4132
- const mounts = buildWorkspaceMounts();
4201
+ const configuredMounts = buildWorkspaceMounts();
4133
4202
  if (input.resetToCanonical) {
4134
- const resetHasBusyLiveMount = mounts.some(
4203
+ const resetHasBusyLiveMount = configuredMounts.some(
4135
4204
  (mount) => !(mount.deleteWhenSourceMissing && !import_node_fs.default.existsSync(mount.sourcePath)) && mount.busy?.()
4136
4205
  );
4137
4206
  if (resetHasBusyLiveMount) {
4138
4207
  throw new Error("Workspace reset cannot observe project mirror heads while a live mount is busy; remediation remains active");
4139
4208
  }
4140
- const observedProjectHeads2 = observeProjectHeadsBeforeOuterWorkspace({
4209
+ const observedProjectHeads2 = await observeProjectHeadsBeforeOuterWorkspace({
4141
4210
  allowNonFastForward: true,
4142
4211
  failClosed: true
4143
4212
  });
4144
- const reset = (0, import_workspace_git_sync.resetWorkspaceGit)({
4213
+ const reset = await (0, import_workspace_git_sync.resetWorkspaceGit)({
4145
4214
  workspacePath: workspaceShadowRoot,
4146
4215
  remoteUrl: workspaceRemoteUrl,
4147
4216
  credentialHelper: workspaceCredentialHelper,
4148
4217
  credentialUsername: workspaceCredentialUsername,
4149
4218
  gitIdentity: workspaceGitIdentity,
4150
- mounts
4219
+ mounts: configuredMounts
4151
4220
  });
4152
- applyObservedProjectHeadsAfterInboundWorkspace(observedProjectHeads2, reset.activeMountIds, true);
4221
+ await applyObservedProjectHeadsAfterInboundWorkspace(observedProjectHeads2, reset.activeMountIds, true);
4153
4222
  return {
4154
4223
  type: "workspace_sync",
4155
4224
  attemptId: input.attemptId,
@@ -4161,7 +4230,7 @@ async function startWorker(options) {
4161
4230
  ...reset.remoteHead ? { publishedHead: reset.remoteHead } : {},
4162
4231
  rebaseCount: 0,
4163
4232
  diffSizeBytes: 0,
4164
- gitStatus: workspaceGitStatus(),
4233
+ gitStatus: await workspaceGitStatus(),
4165
4234
  affectedProjects: affectedProjects(reset.discardedPaths),
4166
4235
  affectedPaths: reset.discardedPaths,
4167
4236
  activeMountIds: reset.activeMountIds,
@@ -4171,13 +4240,14 @@ async function startWorker(options) {
4171
4240
  };
4172
4241
  }
4173
4242
  const outerRemediation = input.trigger.type === "remediation" || input.trigger.type === "remediation_confirm";
4174
- const observedProjectHeads = outerRemediation ? [] : observeProjectHeadsBeforeOuterWorkspace({ allowNonFastForward: false, failClosed: false });
4243
+ const mounts = outerRemediation ? (0, import_workspace_git_sync.workspaceGitMountsForRemediation)(configuredMounts) : configuredMounts;
4244
+ const observedProjectHeads = outerRemediation ? [] : await observeProjectHeadsBeforeOuterWorkspace({ allowNonFastForward: false, failClosed: false });
4175
4245
  const inboundMoveMountIds = new Set(
4176
4246
  observedProjectHeads.flatMap(
4177
4247
  ({ projectId, observations }) => observations.filter(({ shouldMove }) => shouldMove).map(({ branchName }) => projectMountId(projectId, branchName))
4178
4248
  )
4179
4249
  );
4180
- const publicationHeads = outerRemediation ? /* @__PURE__ */ new Map() : captureProjectHeadsForWorkspacePublication();
4250
+ const publicationHeads = outerRemediation ? /* @__PURE__ */ new Map() : await captureProjectHeadsForWorkspacePublication();
4181
4251
  const result = await (0, import_workspace_git_sync.synchronizeWorkspaceGit)({
4182
4252
  attemptId: input.attemptId,
4183
4253
  workerLabel: label,
@@ -4192,13 +4262,13 @@ async function startWorker(options) {
4192
4262
  skipMountMirror: outerRemediation,
4193
4263
  requiredAncestorHeads: input.requiredAncestorHeads,
4194
4264
  assertStillAdmitted: input.assertStillAdmitted,
4195
- afterWorkspacePublished: outerRemediation ? void 0 : ({ activeMountIds, publishedHead }) => {
4196
- pushChangedProjectHeads(activeMountIds, publishedHead, publicationHeads, inboundMoveMountIds);
4265
+ afterWorkspacePublished: outerRemediation ? void 0 : async ({ activeMountIds, publishedHead }) => {
4266
+ await pushChangedProjectHeads(activeMountIds, publishedHead, publicationHeads, inboundMoveMountIds);
4197
4267
  }
4198
4268
  });
4199
4269
  input.assertStillAdmitted?.();
4200
4270
  if (["no_change", "updated", "pushed"].includes(result.outcome) && !outerRemediation) {
4201
- applyObservedProjectHeadsAfterInboundWorkspace(observedProjectHeads, result.activeMountIds, false);
4271
+ await applyObservedProjectHeadsAfterInboundWorkspace(observedProjectHeads, result.activeMountIds, false);
4202
4272
  }
4203
4273
  return mapWorkspaceGitResult(input.attemptId, input.trigger, result);
4204
4274
  };
@@ -4229,7 +4299,7 @@ async function startWorker(options) {
4229
4299
  };
4230
4300
  if (!deferredWorkspaceSyncTriggerIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, input.trigger)) {
4231
4301
  const result2 = {
4232
- ...failedWorkspaceSyncResult(
4302
+ ...await failedWorkspaceSyncResult(
4233
4303
  attemptId,
4234
4304
  input.trigger,
4235
4305
  new Error(
@@ -4256,7 +4326,7 @@ async function startWorker(options) {
4256
4326
  queueEnteredAt = Date.now();
4257
4327
  syncStartedAt = Date.now();
4258
4328
  if (!deferredWorkspaceSyncTriggerIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, input.trigger)) {
4259
- return failedWorkspaceSyncResult(
4329
+ return await failedWorkspaceSyncResult(
4260
4330
  attemptId,
4261
4331
  input.trigger,
4262
4332
  new Error(
@@ -4295,7 +4365,7 @@ async function startWorker(options) {
4295
4365
  },
4296
4366
  exitCode: 1
4297
4367
  });
4298
- return failedWorkspaceSyncResult(attemptId, input.trigger, error);
4368
+ return await failedWorkspaceSyncResult(attemptId, input.trigger, error);
4299
4369
  }
4300
4370
  });
4301
4371
  } finally {
@@ -4322,7 +4392,7 @@ async function startWorker(options) {
4322
4392
  pendingCreatedBranchPublicationNotBefore
4323
4393
  );
4324
4394
  const initialDeferral = pendingBranchDeferral();
4325
- if (!workspaceConfigured || activeWorkspaceIncidentId || deferredWorkspaceConfiguration || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN || automaticSyncInFlight || initialDeferral?.kind === "active_target") {
4395
+ if (!automaticWorkspaceSyncIsCurrentlyAdmitted() || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN || automaticSyncInFlight || initialDeferral?.kind === "active_target") {
4326
4396
  return;
4327
4397
  }
4328
4398
  if (workspaceAutomaticTimer) clearTimeout(workspaceAutomaticTimer);
@@ -4330,8 +4400,7 @@ async function startWorker(options) {
4330
4400
  () => {
4331
4401
  workspaceAutomaticTimer = void 0;
4332
4402
  const scheduledTrigger = pendingAutomaticTrigger;
4333
- if (!scheduledTrigger || !workspaceConfigured || activeWorkspaceIncidentId || deferredWorkspaceConfiguration || currentWorkerSocket !== ws)
4334
- return;
4403
+ if (!scheduledTrigger || !automaticWorkspaceSyncIsCurrentlyAdmitted() || currentWorkerSocket !== ws) return;
4335
4404
  const currentDeferral = pendingBranchDeferral();
4336
4405
  if (currentDeferral?.kind === "active_target") return;
4337
4406
  if (currentDeferral?.kind === "creation_grace") {
@@ -4353,7 +4422,7 @@ async function startWorker(options) {
4353
4422
  }).finally(() => {
4354
4423
  automaticSyncInFlight = false;
4355
4424
  heartbeatBusyGrace = (0, import_heartbeat.grantWorkerHeartbeatBusyGrace)(lastServerHeartbeatAt, heartbeatBusyGrace);
4356
- if (pendingAutomaticTrigger && !activeWorkspaceIncidentId && !deferredWorkspaceConfiguration) {
4425
+ if (pendingAutomaticTrigger && automaticWorkspaceSyncIsCurrentlyAdmitted()) {
4357
4426
  scheduleAutomaticWorkspaceSync(pendingAutomaticTrigger, WORKSPACE_GIT_QUIET_MS);
4358
4427
  }
4359
4428
  });
@@ -4395,7 +4464,7 @@ async function startWorker(options) {
4395
4464
  if (incidentDeferral.error) {
4396
4465
  workspaceConfigured = false;
4397
4466
  return {
4398
- result: failedWorkspaceSyncResult(crypto.randomUUID(), { type: "connect" }, incidentDeferral.error),
4467
+ result: await failedWorkspaceSyncResult(crypto.randomUUID(), { type: "connect" }, incidentDeferral.error),
4399
4468
  pending: incidentDeferredProjectCheckouts(message.projects),
4400
4469
  aheadOfOriginBranches: []
4401
4470
  };
@@ -4423,7 +4492,7 @@ async function startWorker(options) {
4423
4492
  canonicalRemediationActive: canonicalWorkspaceMutationIsActive(activeWorkspaceMutationTargets())
4424
4493
  })) {
4425
4494
  return {
4426
- result: failedWorkspaceSyncResult(
4495
+ result: await failedWorkspaceSyncResult(
4427
4496
  crypto.randomUUID(),
4428
4497
  { type: "connect" },
4429
4498
  new Error("Workspace credential rotation is deferred until the active canonical remediation command finishes")
@@ -4482,7 +4551,7 @@ async function startWorker(options) {
4482
4551
  } catch (error) {
4483
4552
  const wrapped = error instanceof import_registry_auth.RegistryAuthConfigurationError ? error : new import_registry_auth.RegistryAuthConfigurationError(error);
4484
4553
  return {
4485
- result: failedWorkspaceSyncResult(crypto.randomUUID(), { type: "connect" }, wrapped),
4554
+ result: await failedWorkspaceSyncResult(crypto.randomUUID(), { type: "connect" }, wrapped),
4486
4555
  pending: [],
4487
4556
  aheadOfOriginBranches: []
4488
4557
  };
@@ -4659,7 +4728,7 @@ async function startWorker(options) {
4659
4728
  }));
4660
4729
  const nextProjectConfigById = new Map(effectiveProjects.map((project) => [project.projectId, project]));
4661
4730
  if (incidentDeferral.incidentId) {
4662
- (0, import_workspace_git_sync.configureExistingWorkspaceGitForRemediation)({
4731
+ await (0, import_workspace_git_sync.configureExistingWorkspaceGitForRemediation)({
4663
4732
  workspacePath: workspaceShadowRoot,
4664
4733
  remoteUrl: message.workspaceRemoteUrl,
4665
4734
  credentialHelper: nextWorkspaceCredentialHelper,
@@ -4678,7 +4747,7 @@ async function startWorker(options) {
4678
4747
  result: deferredIncidentWorkspaceConfigurationResult({
4679
4748
  attemptId: crypto.randomUUID(),
4680
4749
  workerLabel: label,
4681
- head: workspaceLocalHead(),
4750
+ head: await workspaceLocalHead(),
4682
4751
  skippedMountIds: buildWorkspaceMounts().map(({ id }) => id)
4683
4752
  }),
4684
4753
  pending: pending2,
@@ -4686,7 +4755,7 @@ async function startWorker(options) {
4686
4755
  deferredWorkspaceSyncForIncidentId: incidentDeferral.incidentId
4687
4756
  };
4688
4757
  }
4689
- (0, import_workspace_git_sync.ensureWorkspaceGitClone)({
4758
+ await (0, import_workspace_git_sync.ensureWorkspaceGitClone)({
4690
4759
  workspacePath: workspaceShadowRoot,
4691
4760
  remoteUrl: message.workspaceRemoteUrl,
4692
4761
  credentialHelper: nextWorkspaceCredentialHelper,
@@ -4706,7 +4775,7 @@ async function startWorker(options) {
4706
4775
  sourcePath: recoverySourceOverrides.get(mount.id) ?? mount.sourcePath,
4707
4776
  busy: mount.busyForRecovery ?? mount.busy
4708
4777
  }));
4709
- (0, import_workspace_git_sync.recoverWorkspaceGitHydration)(workspaceShadowRoot, recoveryMounts, { preserveStaleBases: true });
4778
+ await (0, import_workspace_git_sync.recoverWorkspaceGitHydration)(workspaceShadowRoot, recoveryMounts, { preserveStaleBases: true });
4710
4779
  for (const project of message.projects) {
4711
4780
  for (const { branchName } of project.preserveOnlyBranches) {
4712
4781
  if (stillPendingCreatedBranches.has((0, import_workspace_automatic_sync_policy.pendingCreatedBranchKey)(project.projectId, branchName))) {
@@ -4743,7 +4812,7 @@ async function startWorker(options) {
4743
4812
  }
4744
4813
  stageDurableWorkspaceDeletions();
4745
4814
  const mountsBeforeGit = buildWorkspaceMounts();
4746
- (0, import_workspace_git_sync.hydrateWorkspaceGitMounts)(
4815
+ await (0, import_workspace_git_sync.hydrateWorkspaceGitMounts)(
4747
4816
  workspaceShadowRoot,
4748
4817
  mountsBeforeGit.filter((mount) => !mount.deleteWhenSourceMissing && !import_node_fs.default.existsSync(mount.sourcePath) && mount.busy?.() !== true),
4749
4818
  // These mounts are intentionally hydrated before their project Git
@@ -4752,7 +4821,7 @@ async function startWorker(options) {
4752
4821
  // checkout initialization from erasing synchronized dirt.
4753
4822
  { ignoreBusy: true }
4754
4823
  );
4755
- ensureConfiguredProjects();
4824
+ await ensureConfiguredProjects();
4756
4825
  const configurationSyncAdmissionGeneration = workerAdmissionGeneration;
4757
4826
  const assertConfigurationSyncStillAdmitted = () => {
4758
4827
  if (receiptGeneration !== workspaceConfigurationReceiptGeneration || configurationSyncAdmissionGeneration !== workerAdmissionGeneration || !workspaceConfigurationIncidentSnapshotIsCurrent(incidentDeferral.incidentId, activeWorkspaceIncidentId) || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
@@ -4762,6 +4831,7 @@ async function startWorker(options) {
4762
4831
  const result = await performWorkspaceSync({
4763
4832
  attemptId: crypto.randomUUID(),
4764
4833
  trigger: { type: "connect" },
4834
+ resetToCanonical: workspaceConfigurationResetToCanonicalIsAllowed(message.resetToCanonical, incidentDeferral.incidentId),
4765
4835
  assertStillAdmitted: assertConfigurationSyncStillAdmitted
4766
4836
  });
4767
4837
  assertConfigurationSyncStillAdmitted();
@@ -4796,12 +4866,12 @@ async function startWorker(options) {
4796
4866
  const pending = [...pendingCheckouts.values()].sort(
4797
4867
  (left, right) => left.projectId.localeCompare(right.projectId) || left.branchName.localeCompare(right.branchName)
4798
4868
  );
4799
- return { result, pending, aheadOfOriginBranches: collectAheadOfOriginBranches() };
4869
+ return { result, pending, aheadOfOriginBranches: await collectAheadOfOriginBranches() };
4800
4870
  });
4801
4871
  } catch (error) {
4802
4872
  if (receiptGeneration !== workspaceConfigurationReceiptGeneration) {
4803
4873
  return {
4804
- result: failedWorkspaceSyncResult(crypto.randomUUID(), { type: "connect" }, error),
4874
+ result: await failedWorkspaceSyncResult(crypto.randomUUID(), { type: "connect" }, error),
4805
4875
  pending: incidentDeferredProjectCheckouts(message.projects),
4806
4876
  aheadOfOriginBranches: []
4807
4877
  };
@@ -4833,7 +4903,7 @@ async function startWorker(options) {
4833
4903
  pendingCheckouts.set(projectBranchKey(pending.projectId, pending.branchName), pending);
4834
4904
  }
4835
4905
  return {
4836
- result: failedWorkspaceSyncResult(crypto.randomUUID(), { type: "connect" }, error),
4906
+ result: await failedWorkspaceSyncResult(crypto.randomUUID(), { type: "connect" }, error),
4837
4907
  pending: [...pendingCheckouts.values()].sort(
4838
4908
  (left, right) => left.projectId.localeCompare(right.projectId) || left.branchName.localeCompare(right.branchName)
4839
4909
  ),
@@ -4860,7 +4930,7 @@ async function startWorker(options) {
4860
4930
  workspaceAutomaticTimer = void 0;
4861
4931
  }
4862
4932
  void (async () => {
4863
- if (workspaceConfigured && !activeWorkspaceIncidentId && !deferredWorkspaceConfiguration) {
4933
+ if (automaticWorkspaceSyncIsCurrentlyAdmitted()) {
4864
4934
  await runWorkspaceSync({
4865
4935
  trigger: { type: "manual", detail: "graceful worker shutdown" }
4866
4936
  });
@@ -4911,7 +4981,8 @@ async function startWorker(options) {
4911
4981
  execStdinV1: true,
4912
4982
  ptyEnvFilesV1: true,
4913
4983
  workspaceRemediationAncestorGuardV1: true,
4914
- workspaceIncidentConfigDeferralV1: true
4984
+ workspaceIncidentConfigDeferralV1: true,
4985
+ workspaceConfigResetToCanonicalV1: true
4915
4986
  },
4916
4987
  projectRoot: projectsRoot,
4917
4988
  artifactRoot,
@@ -4975,7 +5046,7 @@ async function startWorker(options) {
4975
5046
  }, WORKSPACE_GIT_PERIODIC_MS);
4976
5047
  workspacePeriodicTimer.unref();
4977
5048
  }
4978
- if (pendingAutomaticTrigger && !activeWorkspaceIncidentId && !deferredWorkspaceConfiguration) {
5049
+ if (pendingAutomaticTrigger && automaticWorkspaceSyncIsCurrentlyAdmitted()) {
4979
5050
  scheduleAutomaticWorkspaceSync(pendingAutomaticTrigger, 0);
4980
5051
  }
4981
5052
  return;
@@ -5215,35 +5286,33 @@ async function startWorker(options) {
5215
5286
  if (nextIncidentId !== previousIncidentId) advanceWorkerAdmissionGeneration();
5216
5287
  activeWorkspaceIncidentId = nextIncidentId;
5217
5288
  if (previousIncidentId && !activeWorkspaceIncidentId && (message.status === "resolved" || message.status === "confirmed" || message.status === "reset")) {
5218
- const disposition = workspaceIncidentTerminalClearDisposition({
5289
+ const refresh = workspaceIncidentTerminalRefreshFence({
5219
5290
  deferredConfiguration: deferredWorkspaceConfiguration,
5220
5291
  clearedIncidentId: previousIncidentId
5221
5292
  });
5222
- if (disposition !== "ordinary_resume") {
5223
- pendingAutomaticTrigger ??= { type: "periodic", detail: "resume after workspace incident" };
5224
- workspaceConfigured = false;
5225
- advanceWorkerAdmissionGeneration();
5226
- if (disposition === "reconnect_for_refresh") {
5227
- ws.close(1012, "Refreshing deferred workspace configuration");
5228
- } else {
5229
- if (deferredWorkspaceConfigurationRefreshTimer) clearTimeout(deferredWorkspaceConfigurationRefreshTimer);
5230
- const deferredIncidentId = deferredWorkspaceConfiguration?.incidentId;
5231
- deferredWorkspaceConfigurationRefreshTimer = setTimeout(() => {
5232
- deferredWorkspaceConfigurationRefreshTimer = void 0;
5233
- if (!deferredIncidentId || !deferredWorkspaceRefreshWatchdogIsCurrent({
5234
- capturedIncidentId: deferredIncidentId,
5235
- deferredConfiguration: deferredWorkspaceConfiguration,
5236
- activeIncidentId: activeWorkspaceIncidentId
5237
- }) || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
5238
- return;
5239
- }
5240
- ws.close(1012, "Timed out waiting for deferred workspace configuration refresh");
5241
- }, WORKSPACE_INCIDENT_CONFIG_REFRESH_TIMEOUT_MS);
5242
- deferredWorkspaceConfigurationRefreshTimer.unref();
5243
- }
5244
- return;
5293
+ deferredWorkspaceConfiguration = refresh.deferredConfiguration;
5294
+ pendingAutomaticTrigger ??= { type: "periodic", detail: "resume after workspace incident" };
5295
+ workspaceConfigured = false;
5296
+ advanceWorkerAdmissionGeneration();
5297
+ if (refresh.disposition === "reconnect_for_refresh") {
5298
+ ws.close(1012, "Refreshing deferred workspace configuration");
5299
+ } else {
5300
+ if (deferredWorkspaceConfigurationRefreshTimer) clearTimeout(deferredWorkspaceConfigurationRefreshTimer);
5301
+ const deferredIncidentId = deferredWorkspaceConfiguration.incidentId;
5302
+ deferredWorkspaceConfigurationRefreshTimer = setTimeout(() => {
5303
+ deferredWorkspaceConfigurationRefreshTimer = void 0;
5304
+ if (!deferredWorkspaceRefreshWatchdogIsCurrent({
5305
+ capturedIncidentId: deferredIncidentId,
5306
+ deferredConfiguration: deferredWorkspaceConfiguration,
5307
+ activeIncidentId: activeWorkspaceIncidentId
5308
+ }) || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
5309
+ return;
5310
+ }
5311
+ ws.close(1012, "Timed out waiting for deferred workspace configuration refresh");
5312
+ }, WORKSPACE_INCIDENT_CONFIG_REFRESH_TIMEOUT_MS);
5313
+ deferredWorkspaceConfigurationRefreshTimer.unref();
5245
5314
  }
5246
- scheduleAutomaticWorkspaceSync(pendingAutomaticTrigger ?? { type: "periodic", detail: "resume after workspace incident" }, 0);
5315
+ return;
5247
5316
  }
5248
5317
  return;
5249
5318
  }
@@ -5616,7 +5685,12 @@ async function startWorker(options) {
5616
5685
  if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
5617
5686
  });
5618
5687
  }
5619
- await (0, import_workspace_command_sync_policy.runWorkspaceCommand)(message.target, workspaceSyncSingleFlight, runCommand);
5688
+ await (0, import_workspace_command_sync_policy.runReservedWorkspaceCommand)(message.target, workspaceSyncSingleFlight, runCommand, () => {
5689
+ if (targetReserved) {
5690
+ workspaceSyncPriorityProcessTargets.delete(message.runId);
5691
+ targetReserved = false;
5692
+ }
5693
+ });
5620
5694
  } finally {
5621
5695
  if (targetReserved) workspaceSyncPriorityProcessTargets.delete(message.runId);
5622
5696
  }
@@ -5690,7 +5764,7 @@ async function startWorker(options) {
5690
5764
  } finally {
5691
5765
  if (reservesVisibleWorkspace) {
5692
5766
  workspaceSyncPriorityOperationTargets.delete(message.requestId);
5693
- if (pendingAutomaticTrigger && !activeWorkspaceIncidentId && !deferredWorkspaceConfiguration) {
5767
+ if (pendingAutomaticTrigger && automaticWorkspaceSyncIsCurrentlyAdmitted()) {
5694
5768
  scheduleAutomaticWorkspaceSync(pendingAutomaticTrigger, dirtyTrigger ? WORKSPACE_GIT_QUIET_MS : 0);
5695
5769
  }
5696
5770
  }