@ricsam/r5d-worker 0.0.46 → 0.0.47

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
@@ -21,7 +21,8 @@ import {
21
21
  WORKSPACE_PERIODIC_SCAN_INTERVAL_MS,
22
22
  WorkspaceSyncSingleFlight,
23
23
  calculateWorkspaceDiffFingerprint,
24
- synchronizeWorkspace
24
+ synchronizeWorkspace,
25
+ workspaceProjectsForSync
25
26
  } from "./workspace-sync.mjs";
26
27
  class WorkerServerUnavailableError extends Error {
27
28
  name = "WorkerServerUnavailableError";
@@ -578,6 +579,14 @@ function validatePlanId(planId) {
578
579
  throw new Error(`Invalid plan id from server: ${planId}`);
579
580
  }
580
581
  }
582
+ const NON_RECURSIVE_GIT_CONFIG_ARGS = [
583
+ "-c",
584
+ "submodule.recurse=false",
585
+ "-c",
586
+ "fetch.recurseSubmodules=false",
587
+ "-c",
588
+ "push.recurseSubmodules=false"
589
+ ];
581
590
  function gitExtraHeaderConfigKey(extraHeaderUrl) {
582
591
  return `http.${normalizeBaseUrl(extraHeaderUrl)}/.extraHeader`;
583
592
  }
@@ -585,10 +594,23 @@ function gitAuthArgs(auth) {
585
594
  if (!auth) {
586
595
  return [];
587
596
  }
588
- return ["-c", `${gitExtraHeaderConfigKey(auth.extraHeaderUrl)}=${auth.header}`];
597
+ const scopedKey = gitExtraHeaderConfigKey(auth.extraHeaderUrl);
598
+ return ["-c", "http.extraHeader=", "-c", `${scopedKey}=`, "-c", `${scopedKey}=${auth.header}`];
599
+ }
600
+ const visibleCheckoutGitTestHarness = {
601
+ gitAuthArgs,
602
+ commandArgs: (args, auth) => ["git", ...NON_RECURSIVE_GIT_CONFIG_ARGS, ...gitAuthArgs(auth), ...args],
603
+ cloneArgs: (remoteUrl, branchPath) => ["clone", "--no-recurse-submodules", "--origin", "origin", remoteUrl, branchPath],
604
+ fetchArgs: (...args) => ["fetch", "--no-recurse-submodules", ...args]
605
+ };
606
+ function visibleCheckoutCloneArgs(remoteUrl, branchPath) {
607
+ return visibleCheckoutGitTestHarness.cloneArgs(remoteUrl, branchPath);
608
+ }
609
+ function visibleCheckoutFetchArgs(...args) {
610
+ return visibleCheckoutGitTestHarness.fetchArgs(...args);
589
611
  }
590
612
  function runGit(args, options = {}) {
591
- const command = ["git", ...gitAuthArgs(options.auth), ...args];
613
+ const command = visibleCheckoutGitTestHarness.commandArgs(args, options.auth);
592
614
  const result = Bun.spawnSync(command, {
593
615
  cwd: options.cwd,
594
616
  stdout: "pipe",
@@ -732,8 +754,28 @@ function resolveWorkerFilePath(branchPath, inputPath, builtInPaths) {
732
754
  function fallbackInternalRemoteUrl(baseUrl, projectId) {
733
755
  return new URL(`/git/${projectId}.git`, baseUrl).toString();
734
756
  }
735
- function githubRemoteUrlFor(baseUrl, projectId, manifest) {
736
- return manifest?.repoHttpUrl ?? fallbackInternalRemoteUrl(baseUrl, projectId);
757
+ function visibleCheckoutRemoteFor(input) {
758
+ const canonicalCheckout = input.manifest?.canonicalCheckouts?.find((checkout) => checkout.branchName === input.branchName);
759
+ const isCanonicalManifestBranch = Boolean(canonicalCheckout);
760
+ const usesCanonicalRemote = isCanonicalManifestBranch || !input.manifest?.repoHttpUrl;
761
+ if (usesCanonicalRemote) {
762
+ return {
763
+ remoteUrl: fallbackInternalRemoteUrl(input.baseUrl, input.projectId),
764
+ authHeader: `Authorization: Bearer ${input.token}`,
765
+ persistAuth: false,
766
+ reconcileExistingOrigin: isCanonicalManifestBranch,
767
+ requireRemoteBranch: isCanonicalManifestBranch,
768
+ requiredBaseCommit: canonicalCheckout?.scaffoldCommitHash ?? null
769
+ };
770
+ }
771
+ return {
772
+ remoteUrl: input.manifest?.repoHttpUrl ?? fallbackInternalRemoteUrl(input.baseUrl, input.projectId),
773
+ authHeader: input.manifest?.repoAuthHeader ?? null,
774
+ persistAuth: true,
775
+ reconcileExistingOrigin: Boolean(input.manifest?.repoHttpUrl),
776
+ requireRemoteBranch: false,
777
+ requiredBaseCommit: null
778
+ };
737
779
  }
738
780
  function gitExtraHeaderUrlForRemote(remoteUrl) {
739
781
  try {
@@ -762,6 +804,10 @@ function ensureOriginRemote(branchPath, remoteUrl) {
762
804
  runGit(["remote", "add", "origin", remoteUrl], { cwd: branchPath });
763
805
  }
764
806
  }
807
+ function originRemoteUrl(branchPath) {
808
+ if (!tryGit(["remote", "get-url", "origin"], { cwd: branchPath })) return null;
809
+ return runGit(["remote", "get-url", "origin"], { cwd: branchPath });
810
+ }
765
811
  function shellQuote(value) {
766
812
  return `'${value.replace(/'/g, "'\\''")}'`;
767
813
  }
@@ -830,6 +876,18 @@ function configureVisibleGitHubAuth(branchPath, remoteUrl, authHeader) {
830
876
  }
831
877
  runGit(["config", `${gitExtraHeaderConfigKey(gitExtraHeaderUrlForRemote(remoteUrl))}`, authHeader], { cwd: branchPath });
832
878
  }
879
+ function clearVisibleGitAuthentication(branchPath) {
880
+ let extraHeaderKeys = [];
881
+ try {
882
+ extraHeaderKeys = runGit(["config", "--local", "--name-only", "--get-regexp", "^http\\..*extraheader$"], { cwd: branchPath }).split(/\r?\n/).filter(Boolean);
883
+ } catch {
884
+ }
885
+ for (const key of extraHeaderKeys) {
886
+ tryGit(["config", "--local", "--unset-all", key], { cwd: branchPath });
887
+ }
888
+ runGit(["config", "--local", "--replace-all", "credential.helper", ""], { cwd: branchPath });
889
+ tryGit(["config", "--local", "--unset-all", "credential.useHttpPath"], { cwd: branchPath });
890
+ }
833
891
  function dockerConfigPath() {
834
892
  return path.join(os.homedir(), ".docker", "config.json");
835
893
  }
@@ -918,18 +976,58 @@ function checkoutVisibleBranch(input) {
918
976
  });
919
977
  const clean = !hasWorktreeChanges(input.branchPath);
920
978
  if (remoteBranchExists && clean) {
921
- runGit(["checkout", "-B", input.branchName, `origin/${input.branchName}`], { cwd: input.branchPath });
979
+ runGit(["checkout", "--no-recurse-submodules", "-B", input.branchName, `origin/${input.branchName}`], {
980
+ cwd: input.branchPath
981
+ });
922
982
  return;
923
983
  }
924
984
  if (branchExists) {
925
- runGit(["checkout", input.branchName], { cwd: input.branchPath });
985
+ runGit(["checkout", "--no-recurse-submodules", input.branchName], { cwd: input.branchPath });
926
986
  return;
927
987
  }
928
988
  if (defaultRemoteExists) {
929
- runGit(["checkout", "-B", input.branchName, `origin/${input.defaultBranch}`], { cwd: input.branchPath });
989
+ runGit(["checkout", "--no-recurse-submodules", "-B", input.branchName, `origin/${input.defaultBranch}`], {
990
+ cwd: input.branchPath
991
+ });
930
992
  return;
931
993
  }
932
- runGit(["checkout", "-B", input.branchName], { cwd: input.branchPath });
994
+ runGit(["checkout", "--no-recurse-submodules", "-B", input.branchName], { cwd: input.branchPath });
995
+ }
996
+ function migrateExistingCheckoutToRequiredRemote(input) {
997
+ if (originRemoteUrl(input.branchPath) === input.remoteUrl) return;
998
+ if (hasWorktreeChanges(input.branchPath)) {
999
+ throw new Error(`Cannot migrate dirty checkout for ${input.branchName} to its canonical remote`);
1000
+ }
1001
+ runGit(
1002
+ visibleCheckoutFetchArgs("--no-tags", input.remoteUrl, `+refs/heads/${input.branchName}:refs/remotes/origin/${input.branchName}`),
1003
+ {
1004
+ cwd: input.branchPath,
1005
+ auth: input.remoteAuth
1006
+ }
1007
+ );
1008
+ const canonicalRemoteRef = `refs/remotes/origin/${input.branchName}`;
1009
+ if (!tryGit(["show-ref", "--verify", "--quiet", canonicalRemoteRef], { cwd: input.branchPath })) {
1010
+ throw new Error(`Canonical branch ${input.branchName} is unavailable during checkout migration`);
1011
+ }
1012
+ if (input.requiredBaseCommit && (!tryGit(["cat-file", "-e", `${input.requiredBaseCommit}^{commit}`], { cwd: input.branchPath }) || !tryGit(["merge-base", "--is-ancestor", input.requiredBaseCommit, canonicalRemoteRef], { cwd: input.branchPath }))) {
1013
+ throw new Error(`Canonical branch ${input.branchName} is not derived from required scaffold ${input.requiredBaseCommit}`);
1014
+ }
1015
+ if (tryGit(["rev-parse", "--verify", "HEAD"], { cwd: input.branchPath })) {
1016
+ const previousHead = runGit(["rev-parse", "HEAD"], { cwd: input.branchPath });
1017
+ runGit(["update-ref", `refs/r5d/pre-canonical-checkout/${previousHead}`, previousHead], { cwd: input.branchPath });
1018
+ }
1019
+ ensureOriginRemote(input.branchPath, input.remoteUrl);
1020
+ runGit(["checkout", "--no-recurse-submodules", "-B", input.branchName, `origin/${input.branchName}`], {
1021
+ cwd: input.branchPath
1022
+ });
1023
+ }
1024
+ function assertCheckoutDerivedFromRequiredBase(input) {
1025
+ const remoteRevision = `refs/remotes/origin/${input.branchName}`;
1026
+ for (const revision of [remoteRevision, "HEAD"]) {
1027
+ if (!tryGit(["cat-file", "-e", `${input.requiredBaseCommit}^{commit}`], { cwd: input.branchPath }) || !tryGit(["merge-base", "--is-ancestor", input.requiredBaseCommit, revision], { cwd: input.branchPath })) {
1028
+ throw new Error(`Canonical checkout ${input.branchName} is not derived from required scaffold ${input.requiredBaseCommit}`);
1029
+ }
1030
+ }
933
1031
  }
934
1032
  function ensureVisibleGitCheckout(input) {
935
1033
  validateBranchName(input.branchName);
@@ -939,40 +1037,114 @@ function ensureVisibleGitCheckout(input) {
939
1037
  if (createdCheckout) {
940
1038
  fs.rmSync(branchPath, { recursive: true, force: true });
941
1039
  fs.mkdirSync(path.dirname(branchPath), { recursive: true });
942
- if (!tryGit(["clone", "--origin", "origin", input.githubRemoteUrl, branchPath], { auth: input.githubAuth })) {
1040
+ const cloned = tryGit(visibleCheckoutCloneArgs(input.remoteUrl, branchPath), {
1041
+ auth: input.remoteAuth
1042
+ });
1043
+ if (!cloned && input.requireRemoteBranch) {
1044
+ fs.rmSync(branchPath, { recursive: true, force: true });
1045
+ throw new Error(`Failed to clone canonical checkout for branch ${input.branchName}`);
1046
+ }
1047
+ if (!cloned) {
943
1048
  fs.mkdirSync(branchPath, { recursive: true });
944
1049
  runGit(["init"], { cwd: branchPath });
945
1050
  }
946
- ensureOriginRemote(branchPath, input.githubRemoteUrl);
947
- configureVisibleGitHubAuth(branchPath, input.githubRemoteUrl, input.githubAuthHeader);
1051
+ ensureOriginRemote(branchPath, input.remoteUrl);
1052
+ if (input.clearPersistentAuth) clearVisibleGitAuthentication(branchPath);
1053
+ else configureVisibleGitHubAuth(branchPath, input.remoteUrl, input.persistentAuthHeader);
948
1054
  configureVisibleGitIdentity(branchPath, visibleGitIdentity, runGit);
949
- tryGit(["fetch", "origin", "--prune"], { cwd: branchPath, auth: input.githubAuth });
1055
+ tryGit(visibleCheckoutFetchArgs("origin", "--prune"), { cwd: branchPath, auth: input.remoteAuth });
1056
+ if (input.requireRemoteBranch && !tryGit(["show-ref", "--verify", "--quiet", `refs/remotes/origin/${input.branchName}`], { cwd: branchPath })) {
1057
+ fs.rmSync(branchPath, { recursive: true, force: true });
1058
+ throw new Error(`Canonical branch ${input.branchName} is unavailable after clone`);
1059
+ }
950
1060
  checkoutVisibleBranch({ branchPath, branchName: input.branchName, defaultBranch: input.defaultBranch });
1061
+ if (input.requiredBaseCommit) {
1062
+ try {
1063
+ assertCheckoutDerivedFromRequiredBase({
1064
+ branchPath,
1065
+ branchName: input.branchName,
1066
+ requiredBaseCommit: input.requiredBaseCommit
1067
+ });
1068
+ } catch (error) {
1069
+ fs.rmSync(branchPath, { recursive: true, force: true });
1070
+ throw error;
1071
+ }
1072
+ }
951
1073
  return branchPath;
952
1074
  }
1075
+ if (input.requireRemoteBranch) {
1076
+ if (input.allowRequiredRemoteMigration === false && originRemoteUrl(branchPath) !== input.remoteUrl) {
1077
+ throw new Error(`Canonical checkout ${input.branchName} no longer uses its required remote`);
1078
+ }
1079
+ migrateExistingCheckoutToRequiredRemote({
1080
+ branchPath,
1081
+ branchName: input.branchName,
1082
+ remoteUrl: input.remoteUrl,
1083
+ remoteAuth: input.remoteAuth,
1084
+ requiredBaseCommit: input.requiredBaseCommit
1085
+ });
1086
+ runGit(
1087
+ visibleCheckoutFetchArgs("--no-tags", input.remoteUrl, `+refs/heads/${input.branchName}:refs/remotes/origin/${input.branchName}`),
1088
+ {
1089
+ cwd: branchPath,
1090
+ auth: input.remoteAuth
1091
+ }
1092
+ );
1093
+ }
953
1094
  if (input.reconcileExistingOrigin) {
954
- ensureOriginRemote(branchPath, input.githubRemoteUrl);
1095
+ ensureOriginRemote(branchPath, input.remoteUrl);
1096
+ }
1097
+ if (input.requireRemoteBranch && !tryGit(["show-ref", "--verify", "--quiet", `refs/remotes/origin/${input.branchName}`], { cwd: branchPath })) {
1098
+ throw new Error(`Existing checkout for ${input.branchName} does not contain its canonical remote branch`);
1099
+ }
1100
+ if (input.requiredBaseCommit) {
1101
+ assertCheckoutDerivedFromRequiredBase({
1102
+ branchPath,
1103
+ branchName: input.branchName,
1104
+ requiredBaseCommit: input.requiredBaseCommit
1105
+ });
955
1106
  }
956
- configureVisibleGitHubAuth(branchPath, input.githubRemoteUrl, input.githubAuthHeader);
1107
+ if (input.clearPersistentAuth) clearVisibleGitAuthentication(branchPath);
1108
+ else configureVisibleGitHubAuth(branchPath, input.remoteUrl, input.persistentAuthHeader);
957
1109
  configureVisibleGitIdentity(branchPath, visibleGitIdentity, runGit);
958
1110
  return branchPath;
959
1111
  }
960
1112
  function ensureBranchWorkspace(input) {
961
1113
  const defaultBranch = input.manifest?.defaultBranch || "main";
962
- const githubRemoteUrl = githubRemoteUrlFor(input.baseUrl, input.projectId, input.manifest);
963
- const githubAuth = gitAuthForRemote(githubRemoteUrl, input.manifest?.repoAuthHeader);
1114
+ const remote = visibleCheckoutRemoteFor(input);
964
1115
  return {
965
1116
  branchPath: ensureVisibleGitCheckout({
966
1117
  projectRoot: input.projectRoot,
967
1118
  branchName: input.branchName,
968
- githubRemoteUrl,
969
- githubAuth,
970
- githubAuthHeader: input.manifest?.repoAuthHeader ?? null,
1119
+ remoteUrl: remote.remoteUrl,
1120
+ remoteAuth: gitAuthForRemote(remote.remoteUrl, remote.authHeader),
1121
+ persistentAuthHeader: remote.persistAuth ? remote.authHeader : null,
971
1122
  defaultBranch,
972
- reconcileExistingOrigin: Boolean(input.manifest?.repoHttpUrl)
1123
+ reconcileExistingOrigin: remote.reconcileExistingOrigin,
1124
+ requireRemoteBranch: remote.requireRemoteBranch,
1125
+ requiredBaseCommit: remote.requiredBaseCommit,
1126
+ clearPersistentAuth: !remote.persistAuth
973
1127
  })
974
1128
  };
975
1129
  }
1130
+ function workspaceSyncCheckoutTargets(input) {
1131
+ const selected = /* @__PURE__ */ new Map();
1132
+ const add = (target) => {
1133
+ selected.set(`${target.projectId}\0${target.branchName}`, target);
1134
+ };
1135
+ if (input.canonicalCheckoutOnly) {
1136
+ add(input.canonicalCheckoutOnly);
1137
+ } else if (input.includeAllManifestCheckouts) {
1138
+ for (const project of input.projects) {
1139
+ for (const branchName of project.branches) add({ projectId: project.projectId, branchName });
1140
+ }
1141
+ } else {
1142
+ for (const target of input.pendingTargets) add(target);
1143
+ }
1144
+ return [...selected.values()].sort(
1145
+ (left, right) => left.projectId.localeCompare(right.projectId) || left.branchName.localeCompare(right.branchName)
1146
+ );
1147
+ }
976
1148
  function resolveCommandCwd(branchPath, cwd) {
977
1149
  if (!cwd) {
978
1150
  return branchPath;
@@ -2196,6 +2368,7 @@ async function startWorker(options) {
2196
2368
  let reloadAfterClose = false;
2197
2369
  const workspaceSyncInput = (trigger, overrides = {}) => {
2198
2370
  if (!workspaceRemoteUrl) throw new Error("Worker workspace manifest has not been received");
2371
+ const projects = workspaceProjectsForSync([...manifestByProjectId.values()], trigger);
2199
2372
  return {
2200
2373
  workerLabel: label,
2201
2374
  remoteUrl: workspaceRemoteUrl,
@@ -2203,7 +2376,7 @@ async function startWorker(options) {
2203
2376
  projectsRoot,
2204
2377
  plansRoot: planRoot,
2205
2378
  shadowRoot: workspaceShadowRoot,
2206
- projects: [...manifestByProjectId.values()],
2379
+ projects,
2207
2380
  trigger,
2208
2381
  ...overrides
2209
2382
  };
@@ -2215,7 +2388,7 @@ async function startWorker(options) {
2215
2388
  const allManifestCheckouts = () => [...manifestByProjectId.values()].flatMap(
2216
2389
  (manifest) => manifest.branches.map((branchName) => ({ projectId: manifest.projectId, branchName }))
2217
2390
  );
2218
- const ensureVisibleWorkspaceCheckouts = (targets) => {
2391
+ const ensureVisibleWorkspaceCheckouts = (targets, options2 = {}) => {
2219
2392
  const created = [];
2220
2393
  for (const target of targets) {
2221
2394
  const manifest = manifestByProjectId.get(target.projectId);
@@ -2227,15 +2400,25 @@ async function startWorker(options) {
2227
2400
  const projectRoot = projectRootFor(projectsRoot, manifest.projectId, manifestByProjectId);
2228
2401
  const branchPath = path.join(projectRoot, target.branchName);
2229
2402
  const existed = hasNormalVisibleGitDir(branchPath);
2230
- const githubRemoteUrl = githubRemoteUrlFor(baseUrl, manifest.projectId, manifest);
2403
+ const remote = visibleCheckoutRemoteFor({
2404
+ baseUrl,
2405
+ token,
2406
+ projectId: manifest.projectId,
2407
+ branchName: target.branchName,
2408
+ manifest
2409
+ });
2231
2410
  ensureVisibleGitCheckout({
2232
2411
  projectRoot,
2233
2412
  branchName: target.branchName,
2234
- githubRemoteUrl,
2235
- githubAuth: gitAuthForRemote(githubRemoteUrl, manifest.repoAuthHeader),
2236
- githubAuthHeader: manifest.repoAuthHeader,
2413
+ remoteUrl: remote.remoteUrl,
2414
+ remoteAuth: gitAuthForRemote(remote.remoteUrl, remote.authHeader),
2415
+ persistentAuthHeader: remote.persistAuth ? remote.authHeader : null,
2237
2416
  defaultBranch: manifest.defaultBranch || "main",
2238
- reconcileExistingOrigin: Boolean(manifest.repoHttpUrl)
2417
+ reconcileExistingOrigin: remote.reconcileExistingOrigin,
2418
+ requireRemoteBranch: remote.requireRemoteBranch,
2419
+ requiredBaseCommit: remote.requiredBaseCommit,
2420
+ allowRequiredRemoteMigration: !(options2.strictCanonicalRevalidation && remote.requireRemoteBranch),
2421
+ clearPersistentAuth: !remote.persistAuth
2239
2422
  });
2240
2423
  if (!existed) created.push(target);
2241
2424
  }
@@ -2247,15 +2430,30 @@ async function startWorker(options) {
2247
2430
  let pendingTargets = [];
2248
2431
  const result = await workspaceSyncSingleFlight.runPrepared(() => {
2249
2432
  const shouldEnsureCheckouts = !overrides.skipVisibleMirror && !overrides.resetToCanonical;
2433
+ const syncProjects = workspaceProjectsForSync([...manifestByProjectId.values()], trigger);
2434
+ const allowedCheckoutKeys = new Set(
2435
+ syncProjects.flatMap((project) => project.branches.map((branchName) => manifestCheckoutKey(project.projectId, branchName)))
2436
+ );
2437
+ const scopedPendingTargets = [...pendingManifestCheckouts.values()].filter(
2438
+ (target) => allowedCheckoutKeys.has(manifestCheckoutKey(target.projectId, target.branchName))
2439
+ );
2250
2440
  let checkoutTargets = [];
2251
2441
  if (shouldEnsureCheckouts) {
2252
- checkoutTargets = trigger.type === "connect" ? allManifestCheckouts() : [...pendingManifestCheckouts.values()];
2442
+ checkoutTargets = workspaceSyncCheckoutTargets({
2443
+ projects: syncProjects,
2444
+ pendingTargets: scopedPendingTargets,
2445
+ includeAllManifestCheckouts: trigger.type === "connect",
2446
+ ...trigger.canonicalCheckoutOnly && trigger.projectId && trigger.branchName ? { canonicalCheckoutOnly: { projectId: trigger.projectId, branchName: trigger.branchName } } : {}
2447
+ });
2253
2448
  }
2254
- pendingTargets = shouldEnsureCheckouts ? [...pendingManifestCheckouts.values()] : [];
2255
- const createdCheckouts = ensureVisibleWorkspaceCheckouts(checkoutTargets);
2449
+ pendingTargets = shouldEnsureCheckouts ? scopedPendingTargets : [];
2450
+ const createdCheckouts = ensureVisibleWorkspaceCheckouts(checkoutTargets, {
2451
+ strictCanonicalRevalidation: true
2452
+ });
2256
2453
  const newVisibleCheckoutByKey = /* @__PURE__ */ new Map();
2257
2454
  for (const target of [...overrides.newVisibleCheckouts ?? [], ...createdCheckouts, ...pendingTargets]) {
2258
- newVisibleCheckoutByKey.set(manifestCheckoutKey(target.projectId, target.branchName), target);
2455
+ const key = manifestCheckoutKey(target.projectId, target.branchName);
2456
+ if (allowedCheckoutKeys.has(key)) newVisibleCheckoutByKey.set(key, target);
2259
2457
  }
2260
2458
  const newVisibleCheckouts = [...newVisibleCheckoutByKey.values()];
2261
2459
  return workspaceSyncInput(trigger, {
@@ -2331,7 +2529,8 @@ async function startWorker(options) {
2331
2529
  version: getWorkerVersion(),
2332
2530
  r5dctlVersion: readInstalledCliVersion("r5dctl"),
2333
2531
  capabilities: {
2334
- updateClis: true
2532
+ updateClis: true,
2533
+ canonicalResolverCheckout: true
2335
2534
  },
2336
2535
  projectRoot: projectsRoot,
2337
2536
  artifactRoot,
@@ -2391,7 +2590,15 @@ async function startWorker(options) {
2391
2590
  if (!workspaceRemoteUrl || activeWorkspaceIncidentId || cliUpdateInProgress || periodicWorkspaceScanInFlight) return;
2392
2591
  periodicWorkspaceScanInFlight = true;
2393
2592
  void (async () => {
2394
- if (pendingManifestCheckouts.size > 0) {
2593
+ const genericCheckoutKeys = new Set(
2594
+ workspaceProjectsForSync([...manifestByProjectId.values()], { type: "periodic" }).flatMap(
2595
+ (project) => project.branches.map((branchName) => manifestCheckoutKey(project.projectId, branchName))
2596
+ )
2597
+ );
2598
+ const hasPendingGenericCheckout = [...pendingManifestCheckouts.values()].some(
2599
+ (target) => genericCheckoutKeys.has(manifestCheckoutKey(target.projectId, target.branchName))
2600
+ );
2601
+ if (hasPendingGenericCheckout) {
2395
2602
  previousPeriodicFingerprint = null;
2396
2603
  await runRequestedWorkspaceSync(crypto.randomUUID(), {
2397
2604
  type: "periodic",
@@ -2808,5 +3015,8 @@ export {
2808
3015
  resolveHostShell,
2809
3016
  resolveWorkerFilePath,
2810
3017
  syncSessionArtifacts,
3018
+ visibleCheckoutGitTestHarness,
3019
+ visibleCheckoutRemoteFor,
3020
+ workspaceSyncCheckoutTargets,
2811
3021
  writeWorkerTextFile
2812
3022
  };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.46",
3
+ "version": "0.0.47",
4
4
  "type": "module"
5
5
  }