@ricsam/r5d-worker 0.0.78 → 0.0.79

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.
Files changed (58) hide show
  1. package/README.md +6 -0
  2. package/dist/cjs/main.cjs +1550 -335
  3. package/dist/cjs/managed-paths.cjs +101 -1
  4. package/dist/cjs/package.json +1 -1
  5. package/dist/cjs/project-workspace-state.cjs +184 -5
  6. package/dist/cjs/project-worktrees.cjs +641 -56
  7. package/dist/cjs/registry-auth.cjs +310 -0
  8. package/dist/cjs/repository-transition-policy.cjs +49 -0
  9. package/dist/cjs/supervisor.cjs +62 -11
  10. package/dist/cjs/working-tree-mirror.cjs +191 -34
  11. package/dist/cjs/workspace-automatic-sync-policy.cjs +69 -0
  12. package/dist/cjs/workspace-branch-incarnation-policy.cjs +37 -0
  13. package/dist/cjs/workspace-command-sync-policy.cjs +18 -0
  14. package/dist/cjs/workspace-git-sync.cjs +1028 -54
  15. package/dist/cjs/workspace-mount-boundary.cjs +71 -0
  16. package/dist/cjs/workspace-path-move.cjs +195 -0
  17. package/dist/cjs/workspace-preserve-only-policy.cjs +36 -0
  18. package/dist/cjs/workspace-project-config-policy.cjs +46 -0
  19. package/dist/cjs/workspace-publication-evidence.cjs +46 -0
  20. package/dist/mjs/main.mjs +1584 -341
  21. package/dist/mjs/managed-paths.mjs +100 -1
  22. package/dist/mjs/package.json +1 -1
  23. package/dist/mjs/project-workspace-state.mjs +181 -5
  24. package/dist/mjs/project-worktrees.mjs +632 -55
  25. package/dist/mjs/registry-auth.mjs +269 -0
  26. package/dist/mjs/repository-transition-policy.mjs +22 -0
  27. package/dist/mjs/supervisor.mjs +61 -11
  28. package/dist/mjs/working-tree-mirror.mjs +190 -34
  29. package/dist/mjs/workspace-automatic-sync-policy.mjs +40 -0
  30. package/dist/mjs/workspace-branch-incarnation-policy.mjs +13 -0
  31. package/dist/mjs/workspace-command-sync-policy.mjs +17 -0
  32. package/dist/mjs/workspace-git-sync.mjs +1023 -54
  33. package/dist/mjs/workspace-mount-boundary.mjs +37 -0
  34. package/dist/mjs/workspace-path-move.mjs +160 -0
  35. package/dist/mjs/workspace-preserve-only-policy.mjs +11 -0
  36. package/dist/mjs/workspace-project-config-policy.mjs +21 -0
  37. package/dist/mjs/workspace-publication-evidence.mjs +21 -0
  38. package/dist/types/credential-authority-lock-fixture.d.ts +1 -0
  39. package/dist/types/main.d.ts +175 -1
  40. package/dist/types/managed-paths.d.ts +11 -0
  41. package/dist/types/project-workspace-state.d.ts +45 -1
  42. package/dist/types/project-worktrees.d.ts +119 -2
  43. package/dist/types/registry-auth.d.ts +47 -0
  44. package/dist/types/repository-transition-policy.d.ts +26 -0
  45. package/dist/types/supervisor-daemonized-fixture.d.ts +1 -0
  46. package/dist/types/supervisor-signal-fixture.d.ts +1 -0
  47. package/dist/types/supervisor.d.ts +6 -4
  48. package/dist/types/working-tree-mirror.d.ts +7 -0
  49. package/dist/types/workspace-automatic-sync-policy.d.ts +37 -0
  50. package/dist/types/workspace-branch-incarnation-policy.d.ts +14 -0
  51. package/dist/types/workspace-command-sync-policy.d.ts +9 -0
  52. package/dist/types/workspace-git-sync.d.ts +33 -3
  53. package/dist/types/workspace-mount-boundary.d.ts +10 -0
  54. package/dist/types/workspace-path-move.d.ts +21 -0
  55. package/dist/types/workspace-preserve-only-policy.d.ts +15 -0
  56. package/dist/types/workspace-project-config-policy.d.ts +34 -0
  57. package/dist/types/workspace-publication-evidence.d.ts +17 -0
  58. package/package.json +1 -1
@@ -30,11 +30,16 @@ var workspace_git_sync_exports = {};
30
30
  __export(workspace_git_sync_exports, {
31
31
  MAX_WORKSPACE_GIT_DIFF_BYTES: () => MAX_WORKSPACE_GIT_DIFF_BYTES,
32
32
  WORKSPACE_GIT_BRANCH: () => WORKSPACE_GIT_BRANCH,
33
+ WORKSPACE_GIT_CHECKOUT_DURABILITY: () => WORKSPACE_GIT_CHECKOUT_DURABILITY,
33
34
  WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION: () => WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION,
35
+ WORKSPACE_GIT_HYDRATED_RECEIPT: () => WORKSPACE_GIT_HYDRATED_RECEIPT,
36
+ WORKSPACE_GIT_HYDRATION_TRANSACTION: () => WORKSPACE_GIT_HYDRATION_TRANSACTION,
34
37
  ensureWorkspaceGitClone: () => ensureWorkspaceGitClone,
35
38
  hydrateWorkspaceGitMounts: () => hydrateWorkspaceGitMounts,
39
+ recoverWorkspaceGitHydration: () => recoverWorkspaceGitHydration,
36
40
  resetWorkspaceGit: () => resetWorkspaceGit,
37
41
  synchronizeWorkspaceGit: () => synchronizeWorkspaceGit,
42
+ workspaceGitHydrationIsCurrent: () => workspaceGitHydrationIsCurrent,
38
43
  workspaceGitSyncTestHarness: () => workspaceGitSyncTestHarness
39
44
  });
40
45
  module.exports = __toCommonJS(workspace_git_sync_exports);
@@ -42,10 +47,14 @@ var import_node_fs = __toESM(require("node:fs"), 1);
42
47
  var import_node_path = __toESM(require("node:path"), 1);
43
48
  var import_git_process_environment = require("./git-process-environment.cjs");
44
49
  var import_working_tree_mirror = require("./working-tree-mirror.cjs");
50
+ var import_workspace_mount_boundary = require("./workspace-mount-boundary.cjs");
45
51
  const WORKSPACE_GIT_BRANCH = "main";
46
52
  const MAX_WORKSPACE_GIT_DIFF_BYTES = 5 * 1024 * 1024;
47
53
  const WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION = "r5d-confirm-large-diff-v1";
48
54
  const WORKSPACE_GIT_INTEGRATED_REF = "refs/r5d/workspace-local/integrated";
55
+ const WORKSPACE_GIT_HYDRATED_RECEIPT = "r5d/workspace-hydrated-head";
56
+ const WORKSPACE_GIT_HYDRATION_TRANSACTION = "r5d/workspace-hydration-transaction";
57
+ const WORKSPACE_GIT_CHECKOUT_DURABILITY = "r5d/workspace-checkout-durability";
49
58
  const NON_RECURSIVE_GIT_CONFIG = [
50
59
  "-c",
51
60
  "submodule.recurse=false",
@@ -88,6 +97,631 @@ function revParse(workspacePath, revision) {
88
97
  function updateIntegratedWorkspaceHead(workspacePath, head) {
89
98
  git(workspacePath, ["update-ref", WORKSPACE_GIT_INTEGRATED_REF, head], "record integrated workspace head");
90
99
  }
100
+ function fsyncDirectory(directoryPath) {
101
+ const descriptor = import_node_fs.default.openSync(directoryPath, import_node_fs.default.constants.O_RDONLY);
102
+ try {
103
+ import_node_fs.default.fsyncSync(descriptor);
104
+ } finally {
105
+ import_node_fs.default.closeSync(descriptor);
106
+ }
107
+ }
108
+ function lstatIfExists(targetPath) {
109
+ try {
110
+ return import_node_fs.default.lstatSync(targetPath);
111
+ } catch (error) {
112
+ if (error.code === "ENOENT") return null;
113
+ throw error;
114
+ }
115
+ }
116
+ function hydratedWorkspaceReceiptPath(workspacePath) {
117
+ return import_node_path.default.join(workspacePath, ".git", ...WORKSPACE_GIT_HYDRATED_RECEIPT.split("/"));
118
+ }
119
+ function workspaceCheckoutDurabilityPath(workspacePath) {
120
+ return import_node_path.default.join(workspacePath, ".git", ...WORKSPACE_GIT_CHECKOUT_DURABILITY.split("/"));
121
+ }
122
+ function normalizedWorkspaceMountPath(workspaceRelativePath) {
123
+ return workspaceRelativePath.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
124
+ }
125
+ function workspaceHydrationMountBasis(mount) {
126
+ return {
127
+ id: mount.id,
128
+ incarnationKey: mount.hydrationIncarnationKey,
129
+ sourcePath: import_node_path.default.resolve(mount.sourcePath),
130
+ durabilityRootPath: import_node_path.default.resolve(mount.durabilityRootPath),
131
+ workspaceRelativePath: normalizedWorkspaceMountPath(mount.workspaceRelativePath),
132
+ sourceMode: mount.sourceMode,
133
+ hydrateDeletionMode: mount.hydrateDeletionMode,
134
+ preserveLocalOnInitialOuterAbsence: mount.preserveLocalOnInitialOuterAbsence === true,
135
+ preserveLocalOnHydrationBasisChange: mount.preserveLocalOnHydrationBasisChange === true,
136
+ deleteWhenSourceMissing: mount.deleteWhenSourceMissing === true
137
+ };
138
+ }
139
+ function compareWorkspaceHydrationMountBasis(left, right) {
140
+ return left.id.localeCompare(right.id);
141
+ }
142
+ function workspaceHydrationReceipt(head, mounts) {
143
+ const bases = mounts.map(workspaceHydrationMountBasis).sort(compareWorkspaceHydrationMountBasis);
144
+ const ids = /* @__PURE__ */ new Set();
145
+ for (const basis of bases) {
146
+ if (ids.has(basis.id)) throw new Error(`Workspace hydration receipt mount id must be unique: ${basis.id}`);
147
+ ids.add(basis.id);
148
+ }
149
+ return { version: 2, head, mounts: bases };
150
+ }
151
+ function workspaceHydrationReceiptsEqual(left, right) {
152
+ return JSON.stringify(left) === JSON.stringify(right);
153
+ }
154
+ function workspaceHydrationReceiptCoversMount(receipt, head, mount) {
155
+ if (!receipt || receipt.head !== head) return false;
156
+ const expected = workspaceHydrationMountBasis(mount);
157
+ const actual = receipt.mounts.find(({ id }) => id === expected.id);
158
+ return Boolean(actual && JSON.stringify(actual) === JSON.stringify(expected));
159
+ }
160
+ function workspaceHydrationReceiptMatchesMounts(receipt, head, mounts) {
161
+ return workspaceHydrationReceiptsEqual(receipt, workspaceHydrationReceipt(head, mounts));
162
+ }
163
+ function configuredWorkspaceHydrationBasisMounts(workspacePath, mounts, deferMountIds) {
164
+ const resolvedWorkspacePath = import_node_path.default.resolve(workspacePath);
165
+ return mounts.filter((mount) => {
166
+ if (deferMountIds?.has(mount.id)) return false;
167
+ if (mount.deleteWhenSourceMissing) return !import_node_fs.default.existsSync(mount.sourcePath);
168
+ const outerPath = import_node_path.default.join(resolvedWorkspacePath, ...normalizedWorkspaceMountPath(mount.workspaceRelativePath).split("/"));
169
+ return import_node_fs.default.existsSync(mount.sourcePath) || import_node_fs.default.existsSync(outerPath) || mount.sourceMode === "all";
170
+ });
171
+ }
172
+ function parseWorkspaceHydrationReceiptContent(workspacePath, receiptPath, content) {
173
+ const legacyHead = content.endsWith("\n") ? content.slice(0, -1) : "";
174
+ if (/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(legacyHead) && content === `${legacyHead}
175
+ `) {
176
+ if (!tryGit(workspacePath, ["cat-file", "-e", `${legacyHead}^{commit}`])) {
177
+ throw new Error(`Workspace hydration receipt does not resolve to a local commit: ${receiptPath}`);
178
+ }
179
+ return { version: 2, head: legacyHead, mounts: [] };
180
+ }
181
+ let parsed;
182
+ try {
183
+ parsed = JSON.parse(content);
184
+ } catch {
185
+ throw new Error(`Workspace hydration receipt contains an invalid object id or mount basis: ${receiptPath}`);
186
+ }
187
+ if (!parsed || typeof parsed !== "object") {
188
+ throw new Error(`Workspace hydration receipt contains an invalid object id or mount basis: ${receiptPath}`);
189
+ }
190
+ const candidate = parsed;
191
+ if (candidate.version !== 2 || typeof candidate.head !== "string" || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(candidate.head)) {
192
+ throw new Error(`Workspace hydration receipt contains an invalid object id or mount basis: ${receiptPath}`);
193
+ }
194
+ if (!Array.isArray(candidate.mounts)) {
195
+ throw new Error(`Workspace hydration receipt contains an invalid mount basis: ${receiptPath}`);
196
+ }
197
+ const seenIds = /* @__PURE__ */ new Set();
198
+ const mounts = candidate.mounts.map((rawMount) => {
199
+ if (!rawMount || typeof rawMount !== "object") {
200
+ throw new Error(`Workspace hydration receipt contains an invalid mount basis: ${receiptPath}`);
201
+ }
202
+ const mount = rawMount;
203
+ const normalizedRelativePath = typeof mount.workspaceRelativePath === "string" ? normalizedWorkspaceMountPath(mount.workspaceRelativePath) : "";
204
+ const durabilityRelative = typeof mount.sourcePath === "string" && typeof mount.durabilityRootPath === "string" ? import_node_path.default.relative(mount.durabilityRootPath, mount.sourcePath) : "..";
205
+ if (typeof mount.id !== "string" || !mount.id || seenIds.has(mount.id) || typeof mount.incarnationKey !== "string" || !mount.incarnationKey || mount.incarnationKey.includes("\0") || typeof mount.sourcePath !== "string" || import_node_path.default.resolve(mount.sourcePath) !== mount.sourcePath || typeof mount.durabilityRootPath !== "string" || import_node_path.default.resolve(mount.durabilityRootPath) !== mount.durabilityRootPath || !durabilityRelative || durabilityRelative === ".." || durabilityRelative.startsWith(`..${import_node_path.default.sep}`) || import_node_path.default.isAbsolute(durabilityRelative) || typeof mount.workspaceRelativePath !== "string" || mount.workspaceRelativePath !== normalizedRelativePath || !normalizedRelativePath || normalizedRelativePath === "." || normalizedRelativePath.split("/").some((segment) => segment === ".." || segment === ".git") || mount.sourceMode !== "all" && mount.sourceMode !== "git" || mount.hydrateDeletionMode !== "all" && mount.hydrateDeletionMode !== "git" || typeof mount.preserveLocalOnInitialOuterAbsence !== "boolean" || typeof mount.preserveLocalOnHydrationBasisChange !== "boolean" || typeof mount.deleteWhenSourceMissing !== "boolean") {
206
+ throw new Error(`Workspace hydration receipt contains an invalid mount basis: ${receiptPath}`);
207
+ }
208
+ seenIds.add(mount.id);
209
+ return mount;
210
+ });
211
+ const receipt = {
212
+ version: 2,
213
+ head: candidate.head,
214
+ mounts: mounts.sort(compareWorkspaceHydrationMountBasis)
215
+ };
216
+ if (content !== `${JSON.stringify(receipt)}
217
+ `) {
218
+ throw new Error(`Workspace hydration receipt contains a noncanonical mount basis: ${receiptPath}`);
219
+ }
220
+ if (!tryGit(workspacePath, ["cat-file", "-e", `${receipt.head}^{commit}`])) {
221
+ throw new Error(`Workspace hydration receipt does not resolve to a local commit: ${receiptPath}`);
222
+ }
223
+ return receipt;
224
+ }
225
+ function readHydratedWorkspaceReceipt(workspacePath) {
226
+ const receiptPath = hydratedWorkspaceReceiptPath(workspacePath);
227
+ const status = lstatIfExists(receiptPath);
228
+ if (!status) return null;
229
+ if (!status.isFile() || status.isSymbolicLink()) {
230
+ throw new Error(`Workspace hydration receipt is not a regular file: ${receiptPath}`);
231
+ }
232
+ const content = import_node_fs.default.readFileSync(receiptPath, "utf8");
233
+ return parseWorkspaceHydrationReceiptContent(workspacePath, receiptPath, content);
234
+ }
235
+ function workspaceGitHydrationIsCurrent(workspacePath, mounts) {
236
+ const resolvedWorkspacePath = import_node_path.default.resolve(workspacePath);
237
+ if (!import_node_fs.default.existsSync(import_node_path.default.join(resolvedWorkspacePath, ".git"))) return true;
238
+ if (mounts) validateMounts(resolvedWorkspacePath, mounts);
239
+ const head = revParse(resolvedWorkspacePath, "HEAD");
240
+ if (!workspaceCheckoutDurabilityIsCurrent(resolvedWorkspacePath)) return false;
241
+ if (head === null) return true;
242
+ const receipt = readHydratedWorkspaceReceipt(resolvedWorkspacePath);
243
+ return mounts ? workspaceHydrationReceiptMatchesMounts(receipt, head, configuredWorkspaceHydrationBasisMounts(resolvedWorkspacePath, mounts)) : receipt?.head === head;
244
+ }
245
+ function updateHydratedWorkspaceReceipt(workspacePath, receipt) {
246
+ if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(receipt.head)) {
247
+ throw new Error(`Cannot record invalid fully hydrated workspace object id: ${receipt.head}`);
248
+ }
249
+ if (!tryGit(workspacePath, ["cat-file", "-e", `${receipt.head}^{commit}`])) {
250
+ throw new Error(`Cannot record workspace hydration receipt for a missing commit: ${receipt.head}`);
251
+ }
252
+ readHydratedWorkspaceReceipt(workspacePath);
253
+ const gitDirectory = import_node_path.default.join(workspacePath, ".git");
254
+ const gitDirectoryStatus = import_node_fs.default.lstatSync(gitDirectory);
255
+ if (!gitDirectoryStatus.isDirectory() || gitDirectoryStatus.isSymbolicLink()) {
256
+ throw new Error(`Workspace Git directory is not a regular directory: ${gitDirectory}`);
257
+ }
258
+ const receiptPath = hydratedWorkspaceReceiptPath(workspacePath);
259
+ const receiptDirectory = import_node_path.default.dirname(receiptPath);
260
+ const existingReceiptDirectory = lstatIfExists(receiptDirectory);
261
+ if (!existingReceiptDirectory) {
262
+ import_node_fs.default.mkdirSync(receiptDirectory, { recursive: false, mode: 448 });
263
+ fsyncDirectory(gitDirectory);
264
+ }
265
+ const receiptDirectoryStatus = import_node_fs.default.lstatSync(receiptDirectory);
266
+ if (!receiptDirectoryStatus.isDirectory() || receiptDirectoryStatus.isSymbolicLink()) {
267
+ throw new Error(`Workspace hydration receipt directory is not a regular directory: ${receiptDirectory}`);
268
+ }
269
+ const temporaryPath = import_node_path.default.join(receiptDirectory, `.workspace-hydrated-head.${process.pid}.${crypto.randomUUID()}.tmp`);
270
+ let descriptor;
271
+ try {
272
+ descriptor = import_node_fs.default.openSync(temporaryPath, import_node_fs.default.constants.O_WRONLY | import_node_fs.default.constants.O_CREAT | import_node_fs.default.constants.O_EXCL, 384);
273
+ import_node_fs.default.writeFileSync(descriptor, `${JSON.stringify(receipt)}
274
+ `, "utf8");
275
+ import_node_fs.default.fsyncSync(descriptor);
276
+ import_node_fs.default.closeSync(descriptor);
277
+ descriptor = void 0;
278
+ import_node_fs.default.renameSync(temporaryPath, receiptPath);
279
+ fsyncDirectory(receiptDirectory);
280
+ } catch (error) {
281
+ if (descriptor !== void 0) import_node_fs.default.closeSync(descriptor);
282
+ import_node_fs.default.rmSync(temporaryPath, { force: true });
283
+ throw error;
284
+ }
285
+ }
286
+ function hydrationTransactionPath(workspacePath) {
287
+ return import_node_path.default.join(workspacePath, ".git", ...WORKSPACE_GIT_HYDRATION_TRANSACTION.split("/"));
288
+ }
289
+ function workspacePrivateStateDirectory(workspacePath) {
290
+ const gitDirectory = import_node_path.default.join(workspacePath, ".git");
291
+ const gitDirectoryStatus = import_node_fs.default.lstatSync(gitDirectory);
292
+ if (!gitDirectoryStatus.isDirectory() || gitDirectoryStatus.isSymbolicLink()) {
293
+ throw new Error(`Workspace Git directory is not a regular directory: ${gitDirectory}`);
294
+ }
295
+ const stateDirectory = import_node_path.default.dirname(hydrationTransactionPath(workspacePath));
296
+ const existing = lstatIfExists(stateDirectory);
297
+ if (!existing) {
298
+ import_node_fs.default.mkdirSync(stateDirectory, { recursive: false, mode: 448 });
299
+ fsyncDirectory(gitDirectory);
300
+ } else if (!existing.isDirectory() || existing.isSymbolicLink()) {
301
+ throw new Error(`Workspace Git private state is not a regular directory: ${stateDirectory}`);
302
+ }
303
+ return stateDirectory;
304
+ }
305
+ function writePrivateFileDurably(filePath, content) {
306
+ const descriptor = import_node_fs.default.openSync(
307
+ filePath,
308
+ import_node_fs.default.constants.O_WRONLY | import_node_fs.default.constants.O_CREAT | import_node_fs.default.constants.O_EXCL | import_node_fs.default.constants.O_NOFOLLOW,
309
+ 384
310
+ );
311
+ try {
312
+ import_node_fs.default.writeFileSync(descriptor, content, "utf8");
313
+ import_node_fs.default.fsyncSync(descriptor);
314
+ } finally {
315
+ import_node_fs.default.closeSync(descriptor);
316
+ }
317
+ }
318
+ function fsyncTree(rootPath) {
319
+ const syncEntry = (entryPath) => {
320
+ const status = import_node_fs.default.lstatSync(entryPath);
321
+ if (status.isSymbolicLink()) return;
322
+ if (status.isDirectory()) {
323
+ for (const name of import_node_fs.default.readdirSync(entryPath)) syncEntry(import_node_path.default.join(entryPath, name));
324
+ fsyncDirectory(entryPath);
325
+ return;
326
+ }
327
+ if (!status.isFile()) return;
328
+ const descriptor = import_node_fs.default.openSync(entryPath, import_node_fs.default.constants.O_RDONLY | import_node_fs.default.constants.O_NOFOLLOW);
329
+ try {
330
+ import_node_fs.default.fsyncSync(descriptor);
331
+ } finally {
332
+ import_node_fs.default.closeSync(descriptor);
333
+ }
334
+ };
335
+ syncEntry(rootPath);
336
+ }
337
+ function parseWorkspaceCheckoutDurabilityRecord(workspacePath, recordPath, content) {
338
+ let parsed;
339
+ try {
340
+ parsed = JSON.parse(content);
341
+ } catch {
342
+ throw new Error(`Workspace checkout durability record is invalid: ${recordPath}`);
343
+ }
344
+ if (!parsed || typeof parsed !== "object") {
345
+ throw new Error(`Workspace checkout durability record is invalid: ${recordPath}`);
346
+ }
347
+ const candidate = parsed;
348
+ if (candidate.version !== 1 || candidate.state !== "durable" && candidate.state !== "transition" || candidate.head !== null && (typeof candidate.head !== "string" || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(candidate.head))) {
349
+ throw new Error(`Workspace checkout durability record is invalid: ${recordPath}`);
350
+ }
351
+ const record = {
352
+ version: 1,
353
+ state: candidate.state,
354
+ head: candidate.head
355
+ };
356
+ if (content !== `${JSON.stringify(record)}
357
+ `) {
358
+ throw new Error(`Workspace checkout durability record is noncanonical: ${recordPath}`);
359
+ }
360
+ if (record.head && !tryGit(workspacePath, ["cat-file", "-e", `${record.head}^{commit}`])) {
361
+ throw new Error(`Workspace checkout durability record names a missing commit: ${recordPath}`);
362
+ }
363
+ return record;
364
+ }
365
+ function readWorkspaceCheckoutDurabilityRecord(workspacePath) {
366
+ const recordPath = workspaceCheckoutDurabilityPath(workspacePath);
367
+ const status = lstatIfExists(recordPath);
368
+ if (!status) return null;
369
+ if (!status.isFile() || status.isSymbolicLink()) {
370
+ throw new Error(`Workspace checkout durability record is not a regular file: ${recordPath}`);
371
+ }
372
+ return parseWorkspaceCheckoutDurabilityRecord(workspacePath, recordPath, import_node_fs.default.readFileSync(recordPath, "utf8"));
373
+ }
374
+ function updateWorkspaceCheckoutDurabilityRecord(workspacePath, record) {
375
+ if (record.head && !tryGit(workspacePath, ["cat-file", "-e", `${record.head}^{commit}`])) {
376
+ throw new Error(`Cannot record workspace checkout durability for a missing commit: ${record.head}`);
377
+ }
378
+ readWorkspaceCheckoutDurabilityRecord(workspacePath);
379
+ const stateDirectory = workspacePrivateStateDirectory(workspacePath);
380
+ const recordPath = workspaceCheckoutDurabilityPath(workspacePath);
381
+ const temporaryPath = import_node_path.default.join(stateDirectory, `.workspace-checkout-durability.${process.pid}.${crypto.randomUUID()}.tmp`);
382
+ try {
383
+ writePrivateFileDurably(temporaryPath, `${JSON.stringify(record)}
384
+ `);
385
+ import_node_fs.default.renameSync(temporaryPath, recordPath);
386
+ fsyncDirectory(stateDirectory);
387
+ } catch (error) {
388
+ import_node_fs.default.rmSync(temporaryPath, { force: true });
389
+ throw error;
390
+ }
391
+ }
392
+ function fsyncWorkspaceCheckoutTree(workspacePath) {
393
+ const status = import_node_fs.default.lstatSync(workspacePath);
394
+ if (!status.isDirectory() || status.isSymbolicLink()) {
395
+ throw new Error(`Workspace clone root is not a regular directory: ${workspacePath}`);
396
+ }
397
+ for (const name of import_node_fs.default.readdirSync(workspacePath)) {
398
+ if (name === ".git") continue;
399
+ fsyncTree(import_node_path.default.join(workspacePath, name));
400
+ }
401
+ fsyncDirectory(workspacePath);
402
+ }
403
+ function workspaceRebaseInProgress(workspacePath) {
404
+ const gitDirectory = import_node_path.default.join(workspacePath, ".git");
405
+ return import_node_fs.default.existsSync(import_node_path.default.join(gitDirectory, "rebase-merge")) || import_node_fs.default.existsSync(import_node_path.default.join(gitDirectory, "rebase-apply"));
406
+ }
407
+ function clearWorkspaceCheckoutTree(workspacePath) {
408
+ for (const name of import_node_fs.default.readdirSync(workspacePath)) {
409
+ if (name === ".git") continue;
410
+ import_node_fs.default.rmSync(import_node_path.default.join(workspacePath, name), { recursive: true, force: true });
411
+ }
412
+ fsyncDirectory(workspacePath);
413
+ }
414
+ function recoverWorkspaceCheckoutDurability(workspacePath) {
415
+ workspacePath = import_node_path.default.resolve(workspacePath);
416
+ const record = readWorkspaceCheckoutDurabilityRecord(workspacePath);
417
+ const observedHead = revParse(workspacePath, "HEAD");
418
+ if (record?.state === "durable" && record.head === observedHead) return;
419
+ let targetHead = record?.state === "transition" ? record.head : observedHead;
420
+ if (record?.state === "transition" && observedHead && observedHead !== record.head) {
421
+ const hydrationReceipt = readHydratedWorkspaceReceipt(workspacePath);
422
+ if (hydrationReceipt?.head === observedHead) {
423
+ targetHead = observedHead;
424
+ }
425
+ }
426
+ if (workspaceRebaseInProgress(workspacePath)) {
427
+ const abort = gitResult(workspacePath, ["rebase", "--abort"]);
428
+ if (abort.exitCode !== 0) {
429
+ throw new Error(`Abort interrupted workspace rebase: ${abort.stderr || abort.stdout || `git exited ${abort.exitCode}`}`);
430
+ }
431
+ if (record?.state !== "transition") targetHead = revParse(workspacePath, "HEAD");
432
+ }
433
+ clearWorkspaceCheckoutTree(workspacePath);
434
+ if (targetHead) {
435
+ git(workspacePath, ["reset", "--hard", targetHead], "reconstruct durable workspace checkout");
436
+ if (revParse(workspacePath, "HEAD") !== targetHead) {
437
+ throw new Error(`Workspace checkout reconstruction did not restore ${targetHead}`);
438
+ }
439
+ } else {
440
+ git(workspacePath, ["symbolic-ref", "HEAD", `refs/heads/${WORKSPACE_GIT_BRANCH}`], "restore unborn workspace HEAD");
441
+ git(workspacePath, ["update-ref", "-d", `refs/heads/${WORKSPACE_GIT_BRANCH}`], "remove interrupted workspace branch ref");
442
+ git(workspacePath, ["read-tree", "--empty"], "reconstruct unborn workspace checkout");
443
+ if (revParse(workspacePath, "HEAD") !== null) {
444
+ throw new Error("Workspace checkout reconstruction did not restore its unborn HEAD");
445
+ }
446
+ }
447
+ fsyncWorkspaceCheckoutTree(workspacePath);
448
+ updateWorkspaceCheckoutDurabilityRecord(workspacePath, { version: 1, state: "durable", head: targetHead });
449
+ }
450
+ function workspaceCheckoutDurabilityIsCurrent(workspacePath) {
451
+ const record = readWorkspaceCheckoutDurabilityRecord(workspacePath);
452
+ return Boolean(record?.state === "durable" && record.head === revParse(workspacePath, "HEAD"));
453
+ }
454
+ function beginWorkspaceCheckoutTransition(workspacePath) {
455
+ recoverWorkspaceCheckoutDurability(workspacePath);
456
+ const head = revParse(workspacePath, "HEAD");
457
+ updateWorkspaceCheckoutDurabilityRecord(workspacePath, { version: 1, state: "transition", head });
458
+ }
459
+ function completeWorkspaceCheckoutTransition(workspacePath) {
460
+ fsyncWorkspaceCheckoutTree(workspacePath);
461
+ updateWorkspaceCheckoutDurabilityRecord(workspacePath, {
462
+ version: 1,
463
+ state: "durable",
464
+ head: revParse(workspacePath, "HEAD")
465
+ });
466
+ }
467
+ function throwAfterWorkspaceCheckoutRecovery(workspacePath, error) {
468
+ try {
469
+ recoverWorkspaceCheckoutDurability(workspacePath);
470
+ } catch (recoveryError) {
471
+ throw new AggregateError(
472
+ [error, recoveryError],
473
+ `Workspace checkout mutation failed and its durable head could not be restored: ${error instanceof Error ? error.message : String(error)}`
474
+ );
475
+ }
476
+ throw error;
477
+ }
478
+ function runWorkspaceCheckoutTransition(workspacePath, mutate) {
479
+ beginWorkspaceCheckoutTransition(workspacePath);
480
+ try {
481
+ mutate();
482
+ completeWorkspaceCheckoutTransition(workspacePath);
483
+ } catch (error) {
484
+ throwAfterWorkspaceCheckoutRecovery(workspacePath, error);
485
+ }
486
+ }
487
+ function parseHydrationTransactionManifest(transactionPath) {
488
+ const manifestPath = import_node_path.default.join(transactionPath, "manifest.json");
489
+ const workspacePath = import_node_path.default.resolve(transactionPath, "..", "..", "..");
490
+ const status = import_node_fs.default.lstatSync(manifestPath);
491
+ if (!status.isFile() || status.isSymbolicLink()) {
492
+ throw new Error(`Workspace hydration transaction manifest is not a regular file: ${manifestPath}`);
493
+ }
494
+ let parsed;
495
+ try {
496
+ parsed = JSON.parse(import_node_fs.default.readFileSync(manifestPath, "utf8"));
497
+ } catch (error) {
498
+ throw new Error(`Workspace hydration transaction manifest is invalid: ${manifestPath}`, { cause: error });
499
+ }
500
+ if (!parsed || typeof parsed !== "object") {
501
+ throw new Error(`Workspace hydration transaction manifest is invalid: ${manifestPath}`);
502
+ }
503
+ const candidate = parsed;
504
+ if (candidate.version !== 2 || !Array.isArray(candidate.mounts)) {
505
+ throw new Error(`Workspace hydration transaction manifest is invalid: ${manifestPath}`);
506
+ }
507
+ const parseReceipt = (rawReceipt) => {
508
+ if (rawReceipt === null) return null;
509
+ if (!rawReceipt || typeof rawReceipt !== "object") {
510
+ throw new Error(`Workspace hydration transaction manifest is invalid: ${manifestPath}`);
511
+ }
512
+ return parseWorkspaceHydrationReceiptContent(workspacePath, manifestPath, `${JSON.stringify(rawReceipt)}
513
+ `);
514
+ };
515
+ const targetReceipt = parseReceipt(candidate.targetReceipt);
516
+ const receiptBefore = parseReceipt(candidate.receiptBefore);
517
+ const seenIds = /* @__PURE__ */ new Set();
518
+ const seenSnapshots = /* @__PURE__ */ new Set();
519
+ const mounts = candidate.mounts.map((rawMount) => {
520
+ if (!rawMount || typeof rawMount !== "object") {
521
+ throw new Error(`Workspace hydration transaction manifest is invalid: ${manifestPath}`);
522
+ }
523
+ const mount = rawMount;
524
+ if (typeof mount.id !== "string" || !mount.id || typeof mount.incarnationKey !== "string" || !mount.incarnationKey || mount.incarnationKey.includes("\0") || typeof mount.sourcePath !== "string" || import_node_path.default.resolve(mount.sourcePath) !== mount.sourcePath || typeof mount.durabilityRootPath !== "string" || import_node_path.default.resolve(mount.durabilityRootPath) !== mount.durabilityRootPath || typeof mount.workspaceRelativePath !== "string" || typeof mount.existed !== "boolean" || typeof mount.snapshotName !== "string" || !/^[0-9]+$/.test(mount.snapshotName) || seenIds.has(mount.id) || seenSnapshots.has(mount.snapshotName)) {
525
+ throw new Error(`Workspace hydration transaction manifest is invalid: ${manifestPath}`);
526
+ }
527
+ const relative = import_node_path.default.relative(mount.durabilityRootPath, mount.sourcePath);
528
+ if (!relative || relative === ".." || relative.startsWith(`..${import_node_path.default.sep}`) || import_node_path.default.isAbsolute(relative)) {
529
+ throw new Error(`Workspace hydration transaction source escapes its durability root: ${mount.sourcePath}`);
530
+ }
531
+ seenIds.add(mount.id);
532
+ seenSnapshots.add(mount.snapshotName);
533
+ return mount;
534
+ });
535
+ return {
536
+ version: 2,
537
+ targetReceipt,
538
+ receiptBefore,
539
+ mounts
540
+ };
541
+ }
542
+ function removeHydrationTransaction(workspacePath) {
543
+ const transactionPath = hydrationTransactionPath(workspacePath);
544
+ import_node_fs.default.rmSync(transactionPath, { recursive: true, force: true });
545
+ fsyncDirectory(import_node_path.default.dirname(transactionPath));
546
+ }
547
+ function hydrationTransactionCommitted(transactionPath) {
548
+ const markerPath = import_node_path.default.join(transactionPath, "committed");
549
+ const markerStatus = lstatIfExists(markerPath);
550
+ if (!markerStatus) return false;
551
+ if (!markerStatus.isFile() || markerStatus.isSymbolicLink() || import_node_fs.default.readFileSync(markerPath, "utf8") !== "committed\n") {
552
+ throw new Error(`Workspace hydration transaction commit marker is invalid: ${markerPath}`);
553
+ }
554
+ return true;
555
+ }
556
+ function restoreHydrationTransaction(workspacePath, manifest) {
557
+ const transactionPath = hydrationTransactionPath(workspacePath);
558
+ for (const mount of manifest.mounts) {
559
+ (0, import_workspace_mount_boundary.assertManagedDirectoryPath)({
560
+ trustedRoot: mount.durabilityRootPath,
561
+ candidate: mount.sourcePath,
562
+ label: `Workspace hydration recovery mount ${mount.id}`
563
+ });
564
+ if (!mount.existed) {
565
+ import_node_fs.default.rmSync(mount.sourcePath, { recursive: true, force: true });
566
+ continue;
567
+ }
568
+ const snapshotPath = import_node_path.default.join(transactionPath, "mounts", mount.snapshotName);
569
+ const snapshotStatus = import_node_fs.default.lstatSync(snapshotPath);
570
+ if (!snapshotStatus.isDirectory() || snapshotStatus.isSymbolicLink()) {
571
+ throw new Error(`Workspace hydration snapshot is not a regular directory: ${snapshotPath}`);
572
+ }
573
+ (0, import_working_tree_mirror.mirrorWorkingTree)({ sourceRoot: snapshotPath, targetRoot: mount.sourcePath, sourceMode: "all", deletionMode: "all" });
574
+ }
575
+ fsyncHydratedWorkspaceMounts(
576
+ manifest.mounts.map((mount) => ({
577
+ ...mount,
578
+ hydrationIncarnationKey: mount.incarnationKey,
579
+ sourceMode: "all",
580
+ hydrateDeletionMode: "all"
581
+ }))
582
+ );
583
+ }
584
+ function recoverPendingHydrationTransaction(workspacePath) {
585
+ const stateDirectory = import_node_path.default.dirname(hydrationTransactionPath(workspacePath));
586
+ const stateStatus = lstatIfExists(stateDirectory);
587
+ if (!stateStatus) return;
588
+ if (!stateStatus.isDirectory() || stateStatus.isSymbolicLink()) {
589
+ throw new Error(`Workspace Git private state is not a regular directory: ${stateDirectory}`);
590
+ }
591
+ for (const name of import_node_fs.default.readdirSync(stateDirectory)) {
592
+ if (!name.startsWith(".workspace-hydration-transaction.") || !name.endsWith(".tmp")) continue;
593
+ import_node_fs.default.rmSync(import_node_path.default.join(stateDirectory, name), { recursive: true, force: true });
594
+ fsyncDirectory(stateDirectory);
595
+ }
596
+ const transactionPath = hydrationTransactionPath(workspacePath);
597
+ const transactionStatus = lstatIfExists(transactionPath);
598
+ if (!transactionStatus) return;
599
+ if (!transactionStatus.isDirectory() || transactionStatus.isSymbolicLink()) {
600
+ throw new Error(`Workspace hydration transaction is not a regular directory: ${transactionPath}`);
601
+ }
602
+ const manifest = parseHydrationTransactionManifest(transactionPath);
603
+ if (hydrationTransactionCommitted(transactionPath)) {
604
+ const receipt2 = readHydratedWorkspaceReceipt(workspacePath);
605
+ if (manifest.targetReceipt && !workspaceHydrationReceiptsEqual(receipt2, manifest.targetReceipt)) {
606
+ updateHydratedWorkspaceReceipt(workspacePath, manifest.targetReceipt);
607
+ }
608
+ removeHydrationTransaction(workspacePath);
609
+ return;
610
+ }
611
+ const receipt = readHydratedWorkspaceReceipt(workspacePath);
612
+ if (manifest.targetReceipt && !workspaceHydrationReceiptsEqual(manifest.targetReceipt, manifest.receiptBefore) && workspaceHydrationReceiptsEqual(receipt, manifest.targetReceipt)) {
613
+ removeHydrationTransaction(workspacePath);
614
+ return;
615
+ }
616
+ restoreHydrationTransaction(workspacePath, manifest);
617
+ removeHydrationTransaction(workspacePath);
618
+ }
619
+ function beginHydrationTransaction(workspacePath, mounts, targetReceipt) {
620
+ recoverPendingHydrationTransaction(workspacePath);
621
+ const stateDirectory = workspacePrivateStateDirectory(workspacePath);
622
+ const transactionPath = hydrationTransactionPath(workspacePath);
623
+ const stagingPath = import_node_path.default.join(stateDirectory, `.workspace-hydration-transaction.${process.pid}.${crypto.randomUUID()}.tmp`);
624
+ import_node_fs.default.mkdirSync(stagingPath, { mode: 448 });
625
+ try {
626
+ const snapshotRoot = import_node_path.default.join(stagingPath, "mounts");
627
+ import_node_fs.default.mkdirSync(snapshotRoot, { mode: 448 });
628
+ const manifest = {
629
+ version: 2,
630
+ targetReceipt,
631
+ receiptBefore: readHydratedWorkspaceReceipt(workspacePath),
632
+ mounts: mounts.map((mount, index) => {
633
+ const sourcePath = import_node_path.default.resolve(mount.sourcePath);
634
+ const durabilityRootPath = import_node_path.default.resolve(mount.durabilityRootPath);
635
+ (0, import_workspace_mount_boundary.assertManagedDirectoryPath)({
636
+ trustedRoot: durabilityRootPath,
637
+ candidate: sourcePath,
638
+ label: `Workspace hydration snapshot mount ${mount.id}`
639
+ });
640
+ const sourceStatus = lstatIfExists(sourcePath);
641
+ if (sourceStatus && (!sourceStatus.isDirectory() || sourceStatus.isSymbolicLink())) {
642
+ throw new Error(`Workspace hydration snapshot source is not a regular directory: ${sourcePath}`);
643
+ }
644
+ const snapshotName = String(index);
645
+ const snapshotPath = import_node_path.default.join(snapshotRoot, snapshotName);
646
+ import_node_fs.default.mkdirSync(snapshotPath, { mode: 448 });
647
+ if (sourceStatus) {
648
+ (0, import_working_tree_mirror.mirrorWorkingTree)({ sourceRoot: sourcePath, targetRoot: snapshotPath, sourceMode: "all", deletionMode: "all" });
649
+ }
650
+ return {
651
+ id: mount.id,
652
+ incarnationKey: mount.hydrationIncarnationKey,
653
+ sourcePath,
654
+ durabilityRootPath,
655
+ workspaceRelativePath: mount.workspaceRelativePath,
656
+ existed: Boolean(sourceStatus),
657
+ snapshotName
658
+ };
659
+ })
660
+ };
661
+ writePrivateFileDurably(import_node_path.default.join(stagingPath, "manifest.json"), `${JSON.stringify(manifest)}
662
+ `);
663
+ fsyncTree(stagingPath);
664
+ import_node_fs.default.renameSync(stagingPath, transactionPath);
665
+ fsyncDirectory(stateDirectory);
666
+ return manifest;
667
+ } catch (error) {
668
+ import_node_fs.default.rmSync(stagingPath, { recursive: true, force: true });
669
+ fsyncDirectory(stateDirectory);
670
+ throw error;
671
+ }
672
+ }
673
+ function markHydrationTransactionDurable(workspacePath) {
674
+ const transactionPath = hydrationTransactionPath(workspacePath);
675
+ writePrivateFileDurably(import_node_path.default.join(transactionPath, "committed"), "committed\n");
676
+ fsyncDirectory(transactionPath);
677
+ }
678
+ function fsyncHydratedWorkspaceMount(mount) {
679
+ (0, import_workspace_mount_boundary.assertManagedDirectoryPath)({
680
+ trustedRoot: mount.durabilityRootPath,
681
+ candidate: mount.sourcePath,
682
+ label: `Workspace mount ${mount.id} source`
683
+ });
684
+ const syncEntry = (entryPath, root) => {
685
+ const status = import_node_fs.default.lstatSync(entryPath);
686
+ if (status.isSymbolicLink()) return;
687
+ if (status.isDirectory()) {
688
+ for (const name of import_node_fs.default.readdirSync(entryPath)) {
689
+ if (root && name === ".git") continue;
690
+ syncEntry(import_node_path.default.join(entryPath, name), false);
691
+ }
692
+ fsyncDirectory(entryPath);
693
+ return;
694
+ }
695
+ if (!status.isFile()) return;
696
+ const descriptor = import_node_fs.default.openSync(entryPath, import_node_fs.default.constants.O_RDONLY);
697
+ try {
698
+ import_node_fs.default.fsyncSync(descriptor);
699
+ } finally {
700
+ import_node_fs.default.closeSync(descriptor);
701
+ }
702
+ };
703
+ if (import_node_fs.default.existsSync(mount.sourcePath)) syncEntry(mount.sourcePath, true);
704
+ const durabilityRoot = import_node_path.default.resolve(mount.durabilityRootPath);
705
+ let boundaryParent = import_node_path.default.dirname(import_node_path.default.resolve(mount.sourcePath));
706
+ while (true) {
707
+ if (import_node_fs.default.existsSync(boundaryParent)) {
708
+ const boundaryStatus = import_node_fs.default.lstatSync(boundaryParent);
709
+ if (!boundaryStatus.isDirectory() || boundaryStatus.isSymbolicLink()) {
710
+ throw new Error(`Workspace mount durability boundary is not a regular directory: ${boundaryParent}`);
711
+ }
712
+ fsyncDirectory(boundaryParent);
713
+ }
714
+ if (boundaryParent === durabilityRoot) break;
715
+ const nextParent = import_node_path.default.dirname(boundaryParent);
716
+ if (nextParent === boundaryParent) {
717
+ throw new Error(`Workspace mount ${mount.sourcePath} does not descend from durability root ${durabilityRoot}`);
718
+ }
719
+ boundaryParent = nextParent;
720
+ }
721
+ }
722
+ function fsyncHydratedWorkspaceMounts(mounts) {
723
+ for (const mount of mounts) fsyncHydratedWorkspaceMount(mount);
724
+ }
91
725
  function workspaceIsShallow(workspacePath) {
92
726
  return gitResult(workspacePath, ["rev-parse", "--is-shallow-repository"]).stdout === "true";
93
727
  }
@@ -100,7 +734,22 @@ function validateMounts(workspacePath, mounts) {
100
734
  const paths = [];
101
735
  for (const mount of mounts) {
102
736
  if (!mount.id || ids.has(mount.id)) throw new Error(`Workspace mount id must be unique: ${mount.id}`);
737
+ if (!mount.hydrationIncarnationKey || mount.hydrationIncarnationKey.includes("\0")) {
738
+ throw new Error(`Workspace mount hydration incarnation is invalid: ${mount.id}`);
739
+ }
103
740
  ids.add(mount.id);
741
+ const sourcePath = import_node_path.default.resolve(mount.sourcePath);
742
+ const durabilityRoot = import_node_path.default.resolve(mount.durabilityRootPath);
743
+ const durabilityRelative = import_node_path.default.relative(durabilityRoot, sourcePath);
744
+ const durabilityRootStatus = import_node_fs.default.lstatSync(durabilityRoot);
745
+ if (!durabilityRootStatus.isDirectory() || durabilityRootStatus.isSymbolicLink() || !durabilityRelative || durabilityRelative === ".." || durabilityRelative.startsWith(`..${import_node_path.default.sep}`) || import_node_path.default.isAbsolute(durabilityRelative)) {
746
+ throw new Error(`Workspace mount source escapes its durability root: ${mount.sourcePath}`);
747
+ }
748
+ (0, import_workspace_mount_boundary.assertManagedDirectoryPath)({
749
+ trustedRoot: durabilityRoot,
750
+ candidate: sourcePath,
751
+ label: `Workspace mount ${mount.id} source`
752
+ });
104
753
  const normalized = mount.workspaceRelativePath.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
105
754
  if (!normalized || normalized === "." || normalized.split("/").some((segment) => segment === ".." || segment === ".git")) {
106
755
  throw new Error(`Invalid workspace mount path: ${mount.workspaceRelativePath}`);
@@ -147,6 +796,8 @@ function configureWorkspaceRepository(input) {
147
796
  if (!name || !email) throw new Error("Workspace Git identity must include name and email");
148
797
  git(input.workspacePath, ["config", "--local", "user.name", name], "configure workspace Git user name");
149
798
  git(input.workspacePath, ["config", "--local", "user.email", email], "configure workspace Git user email");
799
+ git(input.workspacePath, ["config", "--local", "core.fsync", "committed"], "configure durable workspace commits");
800
+ git(input.workspacePath, ["config", "--local", "core.fsyncMethod", "fsync"], "configure workspace fsync method");
150
801
  }
151
802
  function fetchWorkspaceHead(workspacePath, remoteUrl, credentialHelper, credentialUsername) {
152
803
  const result = gitResult(workspacePath, [
@@ -170,7 +821,24 @@ ${result.stdout}`;
170
821
  }
171
822
  function ensureWorkspaceGitClone(input) {
172
823
  const workspacePath = import_node_path.default.resolve(input.workspacePath);
824
+ const workspaceParent = import_node_path.default.dirname(workspacePath);
825
+ const parentStatus = import_node_fs.default.lstatSync(workspaceParent);
826
+ if (!parentStatus.isDirectory() || parentStatus.isSymbolicLink()) {
827
+ throw new Error(`Workspace clone parent is not a regular directory: ${workspaceParent}`);
828
+ }
829
+ const workspaceStatus = lstatIfExists(workspacePath);
830
+ if (workspaceStatus) {
831
+ if (!workspaceStatus.isDirectory() || workspaceStatus.isSymbolicLink()) {
832
+ throw new Error(`Workspace clone root is not a regular directory: ${workspacePath}`);
833
+ }
834
+ }
173
835
  const gitPath = import_node_path.default.join(workspacePath, ".git");
836
+ const gitStatus = lstatIfExists(gitPath);
837
+ if (gitStatus) {
838
+ if (!gitStatus.isDirectory() || gitStatus.isSymbolicLink()) {
839
+ throw new Error(`Workspace clone Git metadata is not a regular directory: ${gitPath}`);
840
+ }
841
+ }
174
842
  if (!import_node_fs.default.existsSync(gitPath)) {
175
843
  import_node_fs.default.rmSync(workspacePath, { recursive: true, force: true });
176
844
  import_node_fs.default.mkdirSync(import_node_path.default.dirname(workspacePath), { recursive: true });
@@ -192,6 +860,7 @@ function ensureWorkspaceGitClone(input) {
192
860
  }
193
861
  }
194
862
  configureWorkspaceRepository({ ...input, workspacePath });
863
+ recoverWorkspaceCheckoutDurability(workspacePath);
195
864
  const previousRemoteHead = revParse(workspacePath, `refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`);
196
865
  const localHeadBeforeFetch = revParse(workspacePath, "HEAD");
197
866
  if (!revParse(workspacePath, WORKSPACE_GIT_INTEGRATED_REF) && previousRemoteHead && localHeadBeforeFetch && tryGit(workspacePath, ["merge-base", "--is-ancestor", previousRemoteHead, localHeadBeforeFetch])) {
@@ -200,17 +869,21 @@ function ensureWorkspaceGitClone(input) {
200
869
  const remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
201
870
  let localHead = revParse(workspacePath, "HEAD");
202
871
  if (!localHead && remoteHead) {
203
- git(
204
- workspacePath,
205
- ["checkout", "--no-recurse-submodules", "-B", WORKSPACE_GIT_BRANCH, `refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`],
206
- "check out workspace main"
207
- );
872
+ runWorkspaceCheckoutTransition(workspacePath, () => {
873
+ git(
874
+ workspacePath,
875
+ ["checkout", "--no-recurse-submodules", "-B", WORKSPACE_GIT_BRANCH, `refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`],
876
+ "check out workspace main"
877
+ );
878
+ });
208
879
  localHead = remoteHead;
209
880
  }
210
881
  if (localHead && remoteHead && localHead === remoteHead) {
211
882
  updateIntegratedWorkspaceHead(workspacePath, remoteHead);
212
883
  } else if (localHead && remoteHead && workspaceIsShallow(workspacePath) && !tryGit(workspacePath, ["merge-base", localHead, remoteHead]) && revParse(workspacePath, WORKSPACE_GIT_INTEGRATED_REF) === localHead && workspaceIsClean(workspacePath)) {
213
- git(workspacePath, ["reset", "--hard", remoteHead], "advance workspace across retained history boundary");
884
+ runWorkspaceCheckoutTransition(workspacePath, () => {
885
+ git(workspacePath, ["reset", "--hard", remoteHead], "advance workspace across retained history boundary");
886
+ });
214
887
  updateIntegratedWorkspaceHead(workspacePath, remoteHead);
215
888
  localHead = remoteHead;
216
889
  }
@@ -230,6 +903,16 @@ function activeMounts(mounts) {
230
903
  }
231
904
  function mirrorMountsToWorkspace(workspacePath, mounts) {
232
905
  for (const mount of mounts) {
906
+ (0, import_workspace_mount_boundary.assertManagedDirectoryPath)({
907
+ trustedRoot: mount.durabilityRootPath,
908
+ candidate: mount.sourcePath,
909
+ label: `Workspace mount ${mount.id} source`
910
+ });
911
+ (0, import_workspace_mount_boundary.assertManagedDirectoryPath)({
912
+ trustedRoot: workspacePath,
913
+ candidate: import_node_path.default.join(workspacePath, ...mount.workspaceRelativePath.replace(/\\/g, "/").split("/")),
914
+ label: `Workspace mount ${mount.id} outer target`
915
+ });
233
916
  (0, import_working_tree_mirror.mirrorWorkingTree)({
234
917
  sourceRoot: mount.sourcePath,
235
918
  targetRoot: import_node_path.default.join(workspacePath, ...mount.workspaceRelativePath.replace(/\\/g, "/").split("/")),
@@ -241,6 +924,11 @@ function mirrorMountsToWorkspace(workspacePath, mounts) {
241
924
  function removeWorkspaceMounts(workspacePath, mounts) {
242
925
  for (const mount of mounts) {
243
926
  const targetPath = import_node_path.default.join(workspacePath, ...mount.workspaceRelativePath.replace(/\\/g, "/").split("/"));
927
+ (0, import_workspace_mount_boundary.assertManagedDirectoryPath)({
928
+ trustedRoot: workspacePath,
929
+ candidate: targetPath,
930
+ label: `Workspace mount ${mount.id} outer target`
931
+ });
244
932
  const relative = import_node_path.default.relative(import_node_path.default.resolve(workspacePath), import_node_path.default.resolve(targetPath));
245
933
  if (relative === ".." || relative.startsWith(`..${import_node_path.default.sep}`) || import_node_path.default.isAbsolute(relative)) {
246
934
  throw new Error(`Workspace mount escapes the workspace clone: ${mount.workspaceRelativePath}`);
@@ -248,9 +936,45 @@ function removeWorkspaceMounts(workspacePath, mounts) {
248
936
  import_node_fs.default.rmSync(targetPath, { recursive: true, force: true });
249
937
  }
250
938
  }
251
- function hydrateWorkspaceGitMounts(workspacePath, mounts) {
939
+ function restoreDeferredWorkspaceMountsFromHead(workspacePath, mounts) {
940
+ const head = revParse(workspacePath, "HEAD");
941
+ for (const mount of mounts) {
942
+ const normalized = normalizedWorkspaceMountPath(mount.workspaceRelativePath);
943
+ const targetPath = import_node_path.default.join(workspacePath, ...normalized.split("/"));
944
+ (0, import_workspace_mount_boundary.assertManagedDirectoryPath)({
945
+ trustedRoot: workspacePath,
946
+ candidate: targetPath,
947
+ label: `Deferred workspace mount ${mount.id} outer target`
948
+ });
949
+ import_node_fs.default.rmSync(targetPath, { recursive: true, force: true });
950
+ if (head && tryGit(workspacePath, ["cat-file", "-e", `${head}:${normalized}`])) {
951
+ git(
952
+ workspacePath,
953
+ ["checkout", "--no-recurse-submodules", head, "--", `:(literal)${normalized}`],
954
+ `restore deferred workspace mount ${mount.id} from its integration basis`
955
+ );
956
+ }
957
+ }
958
+ }
959
+ function hydrateWorkspaceGitMountsRaw(workspacePath, mounts, options = {}) {
960
+ const hydratedMountIds = [];
961
+ const skippedMountIds = [];
252
962
  for (const mount of mounts) {
963
+ if (!options.ignoreBusy && mount.busy?.()) {
964
+ skippedMountIds.push(mount.id);
965
+ continue;
966
+ }
253
967
  const sourceRoot = import_node_path.default.join(workspacePath, ...mount.workspaceRelativePath.replace(/\\/g, "/").split("/"));
968
+ (0, import_workspace_mount_boundary.assertManagedDirectoryPath)({
969
+ trustedRoot: workspacePath,
970
+ candidate: sourceRoot,
971
+ label: `Workspace mount ${mount.id} outer source`
972
+ });
973
+ (0, import_workspace_mount_boundary.assertManagedDirectoryPath)({
974
+ trustedRoot: mount.durabilityRootPath,
975
+ candidate: mount.sourcePath,
976
+ label: `Workspace mount ${mount.id} hydration target`
977
+ });
254
978
  if (!import_node_fs.default.existsSync(sourceRoot)) {
255
979
  if (mount.hydrateDeletionMode === "all") {
256
980
  import_node_fs.default.rmSync(mount.sourcePath, { recursive: true, force: true });
@@ -267,37 +991,167 @@ function hydrateWorkspaceGitMounts(workspacePath, mounts) {
267
991
  import_node_fs.default.rmSync(emptyRoot, { recursive: true, force: true });
268
992
  }
269
993
  }
994
+ hydratedMountIds.push(mount.id);
270
995
  continue;
271
996
  }
997
+ const targetExists = import_node_fs.default.existsSync(mount.sourcePath);
272
998
  (0, import_working_tree_mirror.mirrorWorkingTree)({
273
999
  sourceRoot,
274
1000
  targetRoot: mount.sourcePath,
275
1001
  sourceMode: "all",
276
- deletionMode: mount.hydrateDeletionMode
1002
+ // A missing checkout has no Git index to define git-only deletions. Its
1003
+ // newly created directory is empty, so all-mode is equivalent and lets
1004
+ // bootstrap hydration preserve the fetched tree before Git initializes.
1005
+ deletionMode: targetExists ? mount.hydrateDeletionMode : "all"
277
1006
  });
1007
+ hydratedMountIds.push(mount.id);
1008
+ }
1009
+ return { hydratedMountIds: hydratedMountIds.sort(), skippedMountIds: skippedMountIds.sort() };
1010
+ }
1011
+ function hydrateWorkspaceGitMountsTransactionally(input) {
1012
+ const workspacePath = import_node_path.default.resolve(input.workspacePath);
1013
+ const mountIds = input.mounts.map(({ id }) => id).sort();
1014
+ const requiredIds = (input.requiredMounts ?? input.mounts).map(({ id }) => id).sort();
1015
+ if (mountIds.length !== requiredIds.length || mountIds.some((id, index) => id !== requiredIds[index])) {
1016
+ return { hydratedMountIds: [], skippedMountIds: requiredIds };
1017
+ }
1018
+ const busyMountIds = input.ignoreBusy ? [] : (input.durabilityMounts ?? input.mounts).filter((mount) => mount.busy?.()).map(({ id }) => id).sort();
1019
+ if (busyMountIds.length > 0) {
1020
+ return { hydratedMountIds: [], skippedMountIds: busyMountIds };
1021
+ }
1022
+ if (input.recordCurrentHead && !workspaceCheckoutDurabilityIsCurrent(workspacePath)) {
1023
+ throw new Error("Workspace checkout is not durably materialized at its current HEAD");
1024
+ }
1025
+ const targetHead = input.recordCurrentHead ? revParse(workspacePath, "HEAD") : null;
1026
+ const targetReceipt = targetHead ? workspaceHydrationReceipt(targetHead, input.receiptMounts ?? input.durabilityMounts ?? input.mounts) : null;
1027
+ const manifest = beginHydrationTransaction(workspacePath, input.mounts, targetReceipt);
1028
+ try {
1029
+ const hydration = hydrateWorkspaceGitMountsRaw(workspacePath, input.mounts, { ignoreBusy: true });
1030
+ if (hydration.skippedMountIds.length > 0 || hydration.hydratedMountIds.length !== requiredIds.length || hydration.hydratedMountIds.some((id, index) => id !== requiredIds[index])) {
1031
+ throw new Error("Workspace hydration did not include every required mount");
1032
+ }
1033
+ fsyncHydratedWorkspaceMounts(input.durabilityMounts ?? input.mounts);
1034
+ markHydrationTransactionDurable(workspacePath);
1035
+ if (targetReceipt) updateHydratedWorkspaceReceipt(workspacePath, targetReceipt);
1036
+ removeHydrationTransaction(workspacePath);
1037
+ return hydration;
1038
+ } catch (error) {
1039
+ const transactionPath = hydrationTransactionPath(workspacePath);
1040
+ if (lstatIfExists(transactionPath) && hydrationTransactionCommitted(transactionPath)) {
1041
+ throw error;
1042
+ }
1043
+ try {
1044
+ restoreHydrationTransaction(workspacePath, manifest);
1045
+ removeHydrationTransaction(workspacePath);
1046
+ } catch (restoreError) {
1047
+ throw new AggregateError(
1048
+ [error, restoreError],
1049
+ `Workspace hydration failed and its durable snapshot could not be restored: ${error instanceof Error ? error.message : String(error)}`
1050
+ );
1051
+ }
1052
+ throw error;
1053
+ }
1054
+ }
1055
+ function hydrateWorkspaceGitMounts(workspacePath, mounts, options = {}) {
1056
+ const gitDirectory = import_node_path.default.join(import_node_path.default.resolve(workspacePath), ".git");
1057
+ if (!import_node_fs.default.existsSync(gitDirectory)) {
1058
+ return hydrateWorkspaceGitMountsRaw(workspacePath, mounts, options);
1059
+ }
1060
+ recoverWorkspaceCheckoutDurability(import_node_path.default.resolve(workspacePath));
1061
+ return hydrateWorkspaceGitMountsTransactionally({
1062
+ workspacePath,
1063
+ mounts,
1064
+ ignoreBusy: options.ignoreBusy
1065
+ });
1066
+ }
1067
+ function recoverWorkspaceGitHydration(workspacePath, mounts, options = {}) {
1068
+ workspacePath = import_node_path.default.resolve(workspacePath);
1069
+ validateMounts(workspacePath, mounts);
1070
+ recoverWorkspaceCheckoutDurability(workspacePath);
1071
+ recoverPendingHydrationTransaction(workspacePath);
1072
+ const currentHead = revParse(workspacePath, "HEAD");
1073
+ if (!currentHead) return;
1074
+ const currentReceipt = readHydratedWorkspaceReceipt(workspacePath);
1075
+ const outerHeadContainsMount = (mount) => tryGit(workspacePath, ["cat-file", "-e", `HEAD:${normalizedWorkspaceMountPath(mount.workspaceRelativePath)}`]);
1076
+ const configuredBasisMounts = configuredWorkspaceHydrationBasisMounts(workspacePath, mounts, options.deferMountIds);
1077
+ const uncoveredBasisMounts = configuredBasisMounts.filter(
1078
+ (mount) => !workspaceHydrationReceiptCoversMount(currentReceipt, currentHead, mount)
1079
+ );
1080
+ if (workspaceHydrationReceiptMatchesMounts(currentReceipt, currentHead, configuredBasisMounts)) return;
1081
+ const busyMountIds = options.ignoreBusy ? [] : uncoveredBasisMounts.filter((mount) => mount.busy?.()).map(({ id }) => id);
1082
+ if (busyMountIds.length > 0) {
1083
+ throw new Error(
1084
+ `Workspace HEAD ${currentHead} has not been fully hydrated; busy mounts must finish before projection: ${busyMountIds.join(", ")}`
1085
+ );
1086
+ }
1087
+ const hydrationTargets = uncoveredBasisMounts.filter(
1088
+ (mount) => !mount.deleteWhenSourceMissing && !(mount.preserveLocalOnHydrationBasisChange === true && currentReceipt?.head === currentHead) && (outerHeadContainsMount(mount) || mount.preserveLocalOnInitialOuterAbsence !== true)
1089
+ );
1090
+ const hydration = hydrateWorkspaceGitMountsTransactionally({
1091
+ workspacePath,
1092
+ mounts: hydrationTargets,
1093
+ durabilityMounts: uncoveredBasisMounts,
1094
+ receiptMounts: configuredBasisMounts,
1095
+ requiredMounts: hydrationTargets,
1096
+ ignoreBusy: options.ignoreBusy,
1097
+ recordCurrentHead: true
1098
+ });
1099
+ if (hydration.skippedMountIds.length > 0) {
1100
+ throw new Error(
1101
+ `Workspace HEAD ${currentHead} has not been fully hydrated; busy mounts must finish before projection: ${hydration.skippedMountIds.join(", ")}`
1102
+ );
278
1103
  }
279
1104
  }
280
1105
  function resetWorkspaceGit(input) {
281
1106
  const workspacePath = import_node_path.default.resolve(input.workspacePath);
282
1107
  validateMounts(workspacePath, input.mounts);
283
1108
  const initial = ensureWorkspaceGitClone({ ...input, workspacePath });
1109
+ recoverPendingHydrationTransaction(workspacePath);
284
1110
  const status = gitResult(workspacePath, ["status", "--porcelain=v1", "-z"]);
285
1111
  const discardedPaths = status.exitCode === 0 ? status.stdout.split("\0").filter(Boolean).map((entry) => entry.slice(3)).sort() : [];
286
1112
  const remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
287
1113
  if (remoteHead) {
288
- git(workspacePath, ["checkout", "--no-recurse-submodules", "-B", WORKSPACE_GIT_BRANCH, remoteHead], "reset workspace main");
289
- git(workspacePath, ["clean", "-fd", "--", "."], "remove untracked workspace changes");
1114
+ runWorkspaceCheckoutTransition(workspacePath, () => {
1115
+ git(workspacePath, ["checkout", "--no-recurse-submodules", "-B", WORKSPACE_GIT_BRANCH, remoteHead], "reset workspace main");
1116
+ git(workspacePath, ["clean", "-fd", "--", "."], "remove untracked workspace changes");
1117
+ });
290
1118
  } else if (revParse(workspacePath, "HEAD")) {
291
- git(workspacePath, ["rm", "-rf", "--ignore-unmatch", "--", "."], "clear unborn workspace tree");
292
- git(workspacePath, ["commit", "--allow-empty", "-m", JSON.stringify({ type: "workspace_reset" })], "record empty workspace reset");
1119
+ runWorkspaceCheckoutTransition(workspacePath, () => {
1120
+ git(workspacePath, ["rm", "-rf", "--ignore-unmatch", "--", "."], "clear unborn workspace tree");
1121
+ git(workspacePath, ["commit", "--allow-empty", "-m", JSON.stringify({ type: "workspace_reset" })], "record empty workspace reset");
1122
+ });
293
1123
  }
294
1124
  const selected = activeMounts(input.mounts);
295
- hydrateWorkspaceGitMounts(workspacePath, selected.active);
1125
+ const requiredLiveMountsBeforeHydration = input.mounts.filter(({ sourcePath }) => import_node_fs.default.existsSync(sourcePath));
1126
+ const bootstrapMounts = selected.skipped.filter((mount) => {
1127
+ if (mount.deleteWhenSourceMissing || import_node_fs.default.existsSync(mount.sourcePath) || mount.busy?.()) return false;
1128
+ const outerPath = import_node_path.default.join(workspacePath, ...mount.workspaceRelativePath.replace(/\\/g, "/").split("/"));
1129
+ return import_node_fs.default.existsSync(outerPath);
1130
+ });
1131
+ const hydrationMounts = [...selected.active, ...bootstrapMounts];
1132
+ const requiredLiveMounts = [
1133
+ ...new Map([...requiredLiveMountsBeforeHydration, ...bootstrapMounts].map((mount) => [mount.id, mount])).values()
1134
+ ];
1135
+ const hydration = hydrateWorkspaceGitMountsTransactionally({
1136
+ workspacePath,
1137
+ mounts: hydrationMounts,
1138
+ durabilityMounts: [...requiredLiveMounts, ...selected.tombstones],
1139
+ receiptMounts: input.mounts,
1140
+ requiredMounts: requiredLiveMounts,
1141
+ recordCurrentHead: true
1142
+ });
1143
+ const requiredLiveIds = requiredLiveMounts.map(({ id }) => id).sort();
1144
+ const resetHead = revParse(workspacePath, "HEAD");
1145
+ if (hydration.skippedMountIds.length > 0 || hydration.hydratedMountIds.length !== requiredLiveIds.length || hydration.hydratedMountIds.some((id, index) => id !== requiredLiveIds[index]) || resetHead && !workspaceHydrationReceiptMatchesMounts(readHydratedWorkspaceReceipt(workspacePath), resetHead, input.mounts)) {
1146
+ throw new Error("Workspace reset could not durably hydrate every live mount; remediation remains active");
1147
+ }
296
1148
  return {
297
1149
  startingHead: initial.localHead,
298
1150
  localHead: revParse(workspacePath, "HEAD"),
299
1151
  remoteHead,
300
- discardedPaths
1152
+ discardedPaths,
1153
+ activeMountIds: hydration.hydratedMountIds,
1154
+ skippedMountIds: [.../* @__PURE__ */ new Set([...selected.skipped.map(({ id }) => id), ...hydration.skippedMountIds])].sort()
301
1155
  };
302
1156
  }
303
1157
  function stagedPaths(workspacePath) {
@@ -362,17 +1216,22 @@ function snapshotConflict(input) {
362
1216
  }
363
1217
  function rebaseWorkspace(input) {
364
1218
  const remoteRevision = `refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`;
1219
+ beginWorkspaceCheckoutTransition(input.workspacePath);
365
1220
  const rebase = gitResult(
366
1221
  input.workspacePath,
367
1222
  input.truncatedHistoryBase ? ["rebase", "--onto", remoteRevision, input.truncatedHistoryBase] : ["rebase", remoteRevision]
368
1223
  );
369
- if (rebase.exitCode === 0) return { ok: true };
1224
+ if (rebase.exitCode === 0) {
1225
+ completeWorkspaceCheckoutTransition(input.workspacePath);
1226
+ return { ok: true };
1227
+ }
370
1228
  const conflicts = conflictPaths(input.workspacePath);
371
1229
  const refs = snapshotConflict(input);
372
1230
  const abort = gitResult(input.workspacePath, ["rebase", "--abort"]);
373
1231
  if (abort.exitCode !== 0) {
374
1232
  throw new Error(`Abort workspace rebase: ${abort.stderr || abort.stdout || `git exited ${abort.exitCode}`}`);
375
1233
  }
1234
+ completeWorkspaceCheckoutTransition(input.workspacePath);
376
1235
  return {
377
1236
  ok: false,
378
1237
  conflictPaths: conflicts,
@@ -382,26 +1241,31 @@ function rebaseWorkspace(input) {
382
1241
  }
383
1242
  function synchronizeWithFetchedHead(input) {
384
1243
  const localHead = revParse(input.workspacePath, "HEAD");
385
- if (!input.remoteHead) return { kind: "ready", updated: false, rebased: false };
1244
+ const remoteHead = input.remoteHead;
1245
+ if (!remoteHead) return { kind: "ready", updated: false, rebased: false };
386
1246
  if (!localHead) {
387
- git(
388
- input.workspacePath,
389
- ["checkout", "--no-recurse-submodules", "-B", WORKSPACE_GIT_BRANCH, `refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`],
390
- "check out fetched workspace main"
391
- );
1247
+ runWorkspaceCheckoutTransition(input.workspacePath, () => {
1248
+ git(
1249
+ input.workspacePath,
1250
+ ["checkout", "--no-recurse-submodules", "-B", WORKSPACE_GIT_BRANCH, `refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`],
1251
+ "check out fetched workspace main"
1252
+ );
1253
+ });
392
1254
  return { kind: "ready", updated: true, rebased: false };
393
1255
  }
394
- if (localHead === input.remoteHead) {
395
- updateIntegratedWorkspaceHead(input.workspacePath, input.remoteHead);
1256
+ if (localHead === remoteHead) {
1257
+ updateIntegratedWorkspaceHead(input.workspacePath, remoteHead);
396
1258
  return { kind: "ready", updated: false, rebased: false };
397
1259
  }
398
- if (tryGit(input.workspacePath, ["merge-base", "--is-ancestor", localHead, input.remoteHead])) {
399
- git(input.workspacePath, ["reset", "--hard", input.remoteHead], "fast-forward workspace main");
400
- updateIntegratedWorkspaceHead(input.workspacePath, input.remoteHead);
1260
+ if (tryGit(input.workspacePath, ["merge-base", "--is-ancestor", localHead, remoteHead])) {
1261
+ runWorkspaceCheckoutTransition(input.workspacePath, () => {
1262
+ git(input.workspacePath, ["reset", "--hard", remoteHead], "fast-forward workspace main");
1263
+ });
1264
+ updateIntegratedWorkspaceHead(input.workspacePath, remoteHead);
401
1265
  return { kind: "ready", updated: true, rebased: false };
402
1266
  }
403
- if (tryGit(input.workspacePath, ["merge-base", "--is-ancestor", input.remoteHead, localHead])) {
404
- updateIntegratedWorkspaceHead(input.workspacePath, input.remoteHead);
1267
+ if (tryGit(input.workspacePath, ["merge-base", "--is-ancestor", remoteHead, localHead])) {
1268
+ updateIntegratedWorkspaceHead(input.workspacePath, remoteHead);
405
1269
  return { kind: "ready", updated: false, rebased: false };
406
1270
  }
407
1271
  const integratedHead = revParse(input.workspacePath, WORKSPACE_GIT_INTEGRATED_REF);
@@ -414,11 +1278,11 @@ function synchronizeWithFetchedHead(input) {
414
1278
  const rebased = rebaseWorkspace({
415
1279
  workspacePath: input.workspacePath,
416
1280
  localHead,
417
- remoteHead: input.remoteHead,
1281
+ remoteHead,
418
1282
  attemptId: input.attemptId,
419
1283
  truncatedHistoryBase
420
1284
  });
421
- if (rebased.ok) updateIntegratedWorkspaceHead(input.workspacePath, input.remoteHead);
1285
+ if (rebased.ok) updateIntegratedWorkspaceHead(input.workspacePath, remoteHead);
422
1286
  return rebased.ok ? { kind: "ready", updated: false, rebased: true } : { kind: "conflict", conflictPaths: rebased.conflictPaths, refs: rebased.refs, error: rebased.error };
423
1287
  }
424
1288
  async function synchronizeWorkspaceGit(input) {
@@ -427,25 +1291,131 @@ async function synchronizeWorkspaceGit(input) {
427
1291
  const maxDiffBytes = input.maxDiffBytes ?? MAX_WORKSPACE_GIT_DIFF_BYTES;
428
1292
  const maxPushAttempts = Math.max(1, input.maxPushAttempts ?? 4);
429
1293
  validateMounts(workspacePath, input.mounts);
1294
+ const busyLiveMountIds = input.skipMountMirror ? [] : input.mounts.filter((mount) => !(mount.deleteWhenSourceMissing && !import_node_fs.default.existsSync(mount.sourcePath)) && mount.busy?.()).map(({ id }) => id).sort();
1295
+ if (busyLiveMountIds.length > 0) {
1296
+ const cloneExists = import_node_fs.default.existsSync(import_node_path.default.join(workspacePath, ".git"));
1297
+ const localHead = cloneExists ? revParse(workspacePath, "HEAD") : null;
1298
+ const remoteHead2 = cloneExists ? revParse(workspacePath, `refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`) : null;
1299
+ return {
1300
+ outcome: "no_change",
1301
+ startingHead: localHead,
1302
+ localHead,
1303
+ remoteHead: remoteHead2,
1304
+ publishedHead: remoteHead2,
1305
+ rebaseCount: 0,
1306
+ diffSizeBytes: 0,
1307
+ affectedPaths: [],
1308
+ activeMountIds: [],
1309
+ skippedMountIds: input.mounts.map(({ id }) => id).sort()
1310
+ };
1311
+ }
430
1312
  const initial = ensureWorkspaceGitClone({ ...input, workspacePath });
431
1313
  const startingHead = initial.localHead;
1314
+ const receiptBeforeInitialHydration = readHydratedWorkspaceReceipt(workspacePath);
1315
+ const deferredMountIds = /* @__PURE__ */ new Set();
1316
+ if (initial.localHead && initial.remoteHead && initial.remoteHead !== initial.localHead && receiptBeforeInitialHydration?.head === initial.localHead) {
1317
+ for (const mount of input.mounts) {
1318
+ if (!mount.deleteWhenSourceMissing && mount.preserveLocalOnInitialOuterAbsence !== true && mount.preserveLocalOnHydrationBasisChange !== true && !workspaceHydrationReceiptCoversMount(receiptBeforeInitialHydration, initial.localHead, mount)) {
1319
+ deferredMountIds.add(mount.id);
1320
+ }
1321
+ }
1322
+ }
1323
+ recoverWorkspaceGitHydration(workspacePath, input.mounts, {
1324
+ ...deferredMountIds.size > 0 ? { deferMountIds: deferredMountIds } : {}
1325
+ });
1326
+ const receiptRequiredLiveMounts = input.mounts.filter(({ sourcePath }) => import_node_fs.default.existsSync(sourcePath));
432
1327
  const selected = activeMounts(input.mounts);
1328
+ const projectedActiveMounts = selected.active.filter(({ id }) => !deferredMountIds.has(id));
1329
+ const projectedTombstones = selected.tombstones.filter(({ id }) => !deferredMountIds.has(id));
1330
+ const projectionBasisHead = revParse(workspacePath, "HEAD");
1331
+ const projectionBasisReceipt = readHydratedWorkspaceReceipt(workspacePath);
1332
+ const projectionReceiptMounts = configuredWorkspaceHydrationBasisMounts(workspacePath, input.mounts).filter(
1333
+ (mount) => !deferredMountIds.has(mount.id) && Boolean(projectionBasisHead && workspaceHydrationReceiptCoversMount(projectionBasisReceipt, projectionBasisHead, mount))
1334
+ );
1335
+ const completedMountSelection = (requireCurrentHeadReceipt = false) => {
1336
+ const currentHeadBeforeHydration = revParse(workspacePath, "HEAD");
1337
+ const completionReceiptMounts = configuredWorkspaceHydrationBasisMounts(workspacePath, input.mounts);
1338
+ if (currentHeadBeforeHydration && workspaceHydrationReceiptMatchesMounts(
1339
+ readHydratedWorkspaceReceipt(workspacePath),
1340
+ currentHeadBeforeHydration,
1341
+ completionReceiptMounts
1342
+ )) {
1343
+ const lateBusyIds = selected.active.filter((mount) => mount.busy?.()).map(({ id }) => id);
1344
+ const lateBusy = new Set(lateBusyIds);
1345
+ return {
1346
+ activeMountIds: [...selected.active.filter(({ id }) => !lateBusy.has(id)), ...selected.tombstones].map(({ id }) => id).sort(),
1347
+ skippedMountIds: [.../* @__PURE__ */ new Set([...selected.skipped.map(({ id }) => id), ...lateBusyIds])].sort()
1348
+ };
1349
+ }
1350
+ const currentReceiptBeforeHydration = readHydratedWorkspaceReceipt(workspacePath);
1351
+ const mountIsAlreadyCovered = (mount) => Boolean(
1352
+ currentHeadBeforeHydration && workspaceHydrationReceiptCoversMount(currentReceiptBeforeHydration, currentHeadBeforeHydration, mount)
1353
+ );
1354
+ const uncoveredCompletionMounts = selected.active.filter((mount) => !mountIsAlreadyCovered(mount));
1355
+ const uncoveredRequiredLiveMounts = receiptRequiredLiveMounts.filter((mount) => !mountIsAlreadyCovered(mount));
1356
+ const uncoveredCompletionBasisMounts = completionReceiptMounts.filter((mount) => !mountIsAlreadyCovered(mount));
1357
+ const hydration = hydrateWorkspaceGitMountsTransactionally({
1358
+ workspacePath,
1359
+ mounts: uncoveredCompletionMounts,
1360
+ durabilityMounts: uncoveredCompletionBasisMounts,
1361
+ receiptMounts: completionReceiptMounts,
1362
+ requiredMounts: uncoveredRequiredLiveMounts,
1363
+ recordCurrentHead: true
1364
+ });
1365
+ if (requireCurrentHeadReceipt) {
1366
+ const currentHead = revParse(workspacePath, "HEAD");
1367
+ if (currentHead && !workspaceHydrationReceiptMatchesMounts(readHydratedWorkspaceReceipt(workspacePath), currentHead, completionReceiptMounts)) {
1368
+ throw new Error(`Workspace inbound integration ${currentHead} could not be fully hydrated before publication continued`);
1369
+ }
1370
+ }
1371
+ return {
1372
+ activeMountIds: [...selected.active, ...selected.tombstones].map(({ id }) => id).sort(),
1373
+ skippedMountIds: [.../* @__PURE__ */ new Set([...selected.skipped.map(({ id }) => id), ...hydration.skippedMountIds])].sort()
1374
+ };
1375
+ };
1376
+ let newlyStagedPaths = [];
1377
+ const stageAndCommitWorkspace = () => {
1378
+ git(workspacePath, ["add", "-A", "--", "."], "stage workspace working trees");
1379
+ newlyStagedPaths = stagedPaths(workspacePath);
1380
+ if (newlyStagedPaths.length > 0) {
1381
+ git(
1382
+ workspacePath,
1383
+ [
1384
+ "commit",
1385
+ "-m",
1386
+ JSON.stringify({ type: "workspace_sync", worker: input.workerLabel, attemptId, detail: input.commitDetail ?? null })
1387
+ ],
1388
+ "commit workspace working trees"
1389
+ );
1390
+ }
1391
+ };
433
1392
  if (!input.skipMountMirror) {
434
- mirrorMountsToWorkspace(workspacePath, selected.active);
435
- removeWorkspaceMounts(workspacePath, selected.tombstones);
1393
+ beginWorkspaceCheckoutTransition(workspacePath);
1394
+ try {
1395
+ mirrorMountsToWorkspace(workspacePath, projectedActiveMounts);
1396
+ removeWorkspaceMounts(workspacePath, projectedTombstones);
1397
+ restoreDeferredWorkspaceMountsFromHead(
1398
+ workspacePath,
1399
+ input.mounts.filter(({ id }) => deferredMountIds.has(id))
1400
+ );
1401
+ stageAndCommitWorkspace();
1402
+ fsyncWorkspaceCheckoutTree(workspacePath);
1403
+ } catch (error) {
1404
+ throwAfterWorkspaceCheckoutRecovery(workspacePath, error);
1405
+ }
1406
+ } else {
1407
+ stageAndCommitWorkspace();
1408
+ if (newlyStagedPaths.length > 0) completeWorkspaceCheckoutTransition(workspacePath);
436
1409
  }
437
- git(workspacePath, ["add", "-A", "--", "."], "stage workspace working trees");
438
- const newlyStagedPaths = stagedPaths(workspacePath);
439
- if (newlyStagedPaths.length > 0) {
440
- git(
441
- workspacePath,
442
- [
443
- "commit",
444
- "-m",
445
- JSON.stringify({ type: "workspace_sync", worker: input.workerLabel, attemptId, detail: input.commitDetail ?? null })
446
- ],
447
- "commit workspace working trees"
448
- );
1410
+ if (!input.skipMountMirror) {
1411
+ const projectedHead = revParse(workspacePath, "HEAD");
1412
+ if (!projectedHead) throw new Error("Workspace projection did not retain a local HEAD");
1413
+ const targetProjectionReceipt = workspaceHydrationReceipt(projectedHead, projectionReceiptMounts);
1414
+ if (!workspaceHydrationReceiptsEqual(readHydratedWorkspaceReceipt(workspacePath), targetProjectionReceipt)) {
1415
+ fsyncHydratedWorkspaceMounts([...projectedActiveMounts, ...projectedTombstones]);
1416
+ updateHydratedWorkspaceReceipt(workspacePath, targetProjectionReceipt);
1417
+ }
1418
+ completeWorkspaceCheckoutTransition(workspacePath);
449
1419
  }
450
1420
  let remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
451
1421
  let rebaseCount = 0;
@@ -463,8 +1433,8 @@ async function synchronizeWorkspaceGit(input) {
463
1433
  rebaseCount: rebaseCount + 1,
464
1434
  diffSizeBytes: 0,
465
1435
  affectedPaths: [.../* @__PURE__ */ new Set([...newlyStagedPaths, ...reconciled.conflictPaths])].sort(),
466
- activeMountIds: [...selected.active, ...selected.tombstones].map(({ id }) => id).sort(),
467
- skippedMountIds: selected.skipped.map(({ id }) => id).sort(),
1436
+ activeMountIds: [...projectedActiveMounts, ...projectedTombstones].map(({ id }) => id).sort(),
1437
+ skippedMountIds: [.../* @__PURE__ */ new Set([...selected.skipped.map(({ id }) => id), ...deferredMountIds])].sort(),
468
1438
  conflictPaths: reconciled.conflictPaths,
469
1439
  conflictSnapshotRefs: reconciled.refs,
470
1440
  error: reconciled.error
@@ -472,6 +1442,7 @@ async function synchronizeWorkspaceGit(input) {
472
1442
  }
473
1443
  updated ||= reconciled.updated;
474
1444
  if (reconciled.rebased) rebaseCount += 1;
1445
+ if (reconciled.updated || reconciled.rebased) completedMountSelection(true);
475
1446
  const localHead = revParse(workspacePath, "HEAD");
476
1447
  if (!localHead) {
477
1448
  return {
@@ -505,11 +1476,11 @@ async function synchronizeWorkspaceGit(input) {
505
1476
  };
506
1477
  }
507
1478
  if (localHead === remoteHead) {
1479
+ const completedMounts = completedMountSelection(Boolean(input.skipMountMirror));
508
1480
  await input.afterWorkspacePublished?.({
509
1481
  publishedHead: localHead,
510
- activeMountIds: [...selected.active, ...selected.tombstones].map(({ id }) => id).sort()
1482
+ activeMountIds: completedMounts.activeMountIds
511
1483
  });
512
- if (!input.skipMountHydration) hydrateWorkspaceGitMounts(workspacePath, selected.active);
513
1484
  return {
514
1485
  outcome: updated ? "updated" : "no_change",
515
1486
  startingHead,
@@ -519,8 +1490,7 @@ async function synchronizeWorkspaceGit(input) {
519
1490
  rebaseCount,
520
1491
  diffSizeBytes: size,
521
1492
  affectedPaths: paths,
522
- activeMountIds: [...selected.active, ...selected.tombstones].map(({ id }) => id).sort(),
523
- skippedMountIds: selected.skipped.map(({ id }) => id).sort()
1493
+ ...completedMounts
524
1494
  };
525
1495
  }
526
1496
  const pushArgs = [
@@ -533,11 +1503,11 @@ async function synchronizeWorkspaceGit(input) {
533
1503
  const push = gitResult(workspacePath, pushArgs);
534
1504
  if (push.exitCode === 0) {
535
1505
  updateIntegratedWorkspaceHead(workspacePath, localHead);
1506
+ const completedMounts = completedMountSelection(Boolean(input.skipMountMirror));
536
1507
  await input.afterWorkspacePublished?.({
537
1508
  publishedHead: localHead,
538
- activeMountIds: [...selected.active, ...selected.tombstones].map(({ id }) => id).sort()
1509
+ activeMountIds: completedMounts.activeMountIds
539
1510
  });
540
- if (!input.skipMountHydration) hydrateWorkspaceGitMounts(workspacePath, selected.active);
541
1511
  return {
542
1512
  outcome: "pushed",
543
1513
  startingHead,
@@ -547,8 +1517,7 @@ async function synchronizeWorkspaceGit(input) {
547
1517
  rebaseCount,
548
1518
  diffSizeBytes: size,
549
1519
  affectedPaths: paths,
550
- activeMountIds: [...selected.active, ...selected.tombstones].map(({ id }) => id).sort(),
551
- skippedMountIds: selected.skipped.map(({ id }) => id).sort()
1520
+ ...completedMounts
552
1521
  };
553
1522
  }
554
1523
  if (!/(non-fast-forward|fetch first|rejected|failed to push some refs)/i.test(`${push.stderr}
@@ -570,10 +1539,15 @@ const workspaceGitSyncTestHarness = {
570
1539
  0 && (module.exports = {
571
1540
  MAX_WORKSPACE_GIT_DIFF_BYTES,
572
1541
  WORKSPACE_GIT_BRANCH,
1542
+ WORKSPACE_GIT_CHECKOUT_DURABILITY,
573
1543
  WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION,
1544
+ WORKSPACE_GIT_HYDRATED_RECEIPT,
1545
+ WORKSPACE_GIT_HYDRATION_TRANSACTION,
574
1546
  ensureWorkspaceGitClone,
575
1547
  hydrateWorkspaceGitMounts,
1548
+ recoverWorkspaceGitHydration,
576
1549
  resetWorkspaceGit,
577
1550
  synchronizeWorkspaceGit,
1551
+ workspaceGitHydrationIsCurrent,
578
1552
  workspaceGitSyncTestHarness
579
1553
  });