@ricsam/r5d-worker 0.0.57 → 0.0.59

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.
@@ -32,11 +32,13 @@ __export(workspace_sync_exports, {
32
32
  WORKSPACE_BRANCH: () => WORKSPACE_BRANCH,
33
33
  WORKSPACE_INTENT_REF_PREFIX: () => WORKSPACE_INTENT_REF_PREFIX,
34
34
  WORKSPACE_NATIVE_PLANS_RELATIVE_PATH: () => WORKSPACE_NATIVE_PLANS_RELATIVE_PATH,
35
- WORKSPACE_PERIODIC_SCAN_INTERVAL_MS: () => WORKSPACE_PERIODIC_SCAN_INTERVAL_MS,
35
+ WORKSPACE_PUBLICATION_REF_PREFIX: () => WORKSPACE_PUBLICATION_REF_PREFIX,
36
36
  WorkspaceSyncSingleFlight: () => WorkspaceSyncSingleFlight,
37
37
  assertCanonicalCheckoutIndexMatchesWorktree: () => assertCanonicalCheckoutIndexMatchesWorktree,
38
38
  assertWorkspaceQuarantineRef: () => assertWorkspaceQuarantineRef,
39
39
  calculateWorkspaceDiffFingerprint: () => calculateWorkspaceDiffFingerprint,
40
+ calculateWorkspaceLocalDiffFingerprint: () => calculateWorkspaceLocalDiffFingerprint,
41
+ convergeWorkspaceHead: () => convergeWorkspaceHead,
40
42
  encodeWorkspaceBranch: () => encodeWorkspaceBranch,
41
43
  mirrorShadowWorkspaceToVisible: () => mirrorShadowWorkspaceToVisible,
42
44
  mirrorVisibleWorkspaceToShadow: () => mirrorVisibleWorkspaceToShadow,
@@ -54,8 +56,8 @@ var import_managed_paths = require("./managed-paths.cjs");
54
56
  var import_workspace_mutation_gate = require("./workspace-mutation-gate.cjs");
55
57
  const WORKSPACE_BRANCH = "main";
56
58
  const WORKSPACE_INTENT_REF_PREFIX = "refs/r5d/workspace-intents/";
59
+ const WORKSPACE_PUBLICATION_REF_PREFIX = "refs/r5d/workspace-publications/";
57
60
  const MAX_WORKSPACE_SYNC_DIFF_BYTES = 5 * 1024 * 1024;
58
- const WORKSPACE_PERIODIC_SCAN_INTERVAL_MS = 5e3;
59
61
  const MAX_REPORTED_WORKSPACE_PATHS = 2e3;
60
62
  const MAX_REPORTED_WORKSPACE_STATUS_BYTES = 256 * 1024;
61
63
  const MAX_MIRROR_COMPARISON_CACHE_ENTRIES = 2e5;
@@ -960,7 +962,7 @@ function hydrateProjectFromShadowOptimistically(input, relativeRoot, visibleRoot
960
962
  expectedCheckoutSnapshot = checkoutGitSnapshot(visibleRoot);
961
963
  return expectedCheckoutSnapshot !== null;
962
964
  };
963
- let currentGitlinks = new Map(checkoutGitlinks(visibleRoot).map((entry) => [entry.filePath, entry]));
965
+ const currentGitlinks = new Map(checkoutGitlinks(visibleRoot).map((entry) => [entry.filePath, entry]));
964
966
  const changedPaths = [.../* @__PURE__ */ new Set([...preimage.keys(), ...desired.keys()])].filter(
965
967
  (filePath) => !hydrationEntriesEqual(preimage.get(filePath), desired.get(filePath))
966
968
  );
@@ -1582,6 +1584,33 @@ function resetUncommittedShadowSnapshot(input) {
1582
1584
  }
1583
1585
  runGit(input, input.shadowRoot, ["clean", "-fd"], "clean interrupted workspace snapshot");
1584
1586
  }
1587
+ function activeWorkspaceRebase(input) {
1588
+ for (const name of ["rebase-merge", "rebase-apply"]) {
1589
+ const resolved = runGitResult(input, input.shadowRoot, ["rev-parse", "--git-path", name]);
1590
+ if (resolved.exitCode !== 0 || !resolved.stdout) continue;
1591
+ const gitPath = import_node_path.default.isAbsolute(resolved.stdout) ? resolved.stdout : import_node_path.default.resolve(input.shadowRoot, resolved.stdout);
1592
+ if (import_node_fs.default.existsSync(gitPath)) return name;
1593
+ }
1594
+ return null;
1595
+ }
1596
+ function remediationRebaseInProgressResult(input, result) {
1597
+ if (input.trigger.type !== "remediation" && input.trigger.type !== "remediation_confirm") return null;
1598
+ const rebaseKind = activeWorkspaceRebase(input);
1599
+ if (!rebaseKind) return null;
1600
+ const conflictPaths = runGitResult(input, input.shadowRoot, ["diff", "--name-only", "--diff-filter=U", "-z"]).stdout.split("\0").filter(Boolean).sort();
1601
+ const status = reportedStatus(gitStatus(input));
1602
+ return {
1603
+ ...result,
1604
+ outcome: "conflict_blocked",
1605
+ expectedHead: revParse(input, `origin/${WORKSPACE_BRANCH}`),
1606
+ candidateHead: revParse(input, "ORIG_HEAD") ?? revParse(input, "HEAD") ?? void 0,
1607
+ rebaseCount: 1,
1608
+ gitStatus: status,
1609
+ affectedProjects: affectedProjects(conflictPaths),
1610
+ affectedPaths: reportedPaths(conflictPaths),
1611
+ error: `Workspace conflict remediation has an in-progress ${rebaseKind}; the index and worktree were preserved until the resolver completes or aborts it.`
1612
+ };
1613
+ }
1585
1614
  function fastForwardCleanShadowForNewCheckouts(input) {
1586
1615
  if (!input.newVisibleCheckouts?.length) return null;
1587
1616
  runGit(input, input.shadowRoot, ["add", "-A"], "stage existing workspace changes before checkout hydration");
@@ -1727,9 +1756,12 @@ function commitMessage(input) {
1727
1756
  });
1728
1757
  }
1729
1758
  function assertWorkspaceQuarantineRef(quarantineRef) {
1730
- const suffix = quarantineRef.slice(WORKSPACE_INTENT_REF_PREFIX.length);
1731
- if (!quarantineRef.startsWith(WORKSPACE_INTENT_REF_PREFIX) || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(suffix)) {
1732
- throw new Error(`Workspace candidate ref must be below ${WORKSPACE_INTENT_REF_PREFIX}`);
1759
+ const prefix = quarantineRef.startsWith(WORKSPACE_PUBLICATION_REF_PREFIX) ? WORKSPACE_PUBLICATION_REF_PREFIX : WORKSPACE_INTENT_REF_PREFIX;
1760
+ const suffix = quarantineRef.slice(prefix.length);
1761
+ if (!quarantineRef.startsWith(prefix) || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(suffix)) {
1762
+ throw new Error(
1763
+ `Workspace candidate ref must be below ${WORKSPACE_PUBLICATION_REF_PREFIX} or ${WORKSPACE_INTENT_REF_PREFIX}`
1764
+ );
1733
1765
  }
1734
1766
  const result = Bun.spawnSync(["git", "check-ref-format", quarantineRef], {
1735
1767
  stdout: "pipe",
@@ -1801,17 +1833,105 @@ function convergeRedundantInboundCandidate(input, result) {
1801
1833
  affectedPaths: []
1802
1834
  };
1803
1835
  }
1836
+ function preserveWorkspaceConflictCandidate(input, observed, options) {
1837
+ try {
1838
+ if (!options.baseRevision) throw new Error("the preserved workspace does not have an old-base commit");
1839
+ runGit(input, input.shadowRoot, ["reset", "--hard", options.baseRevision], "restore old-base workspace conflict candidate");
1840
+ runGit(input, input.shadowRoot, ["clean", "-fd"], "clean old-base workspace conflict candidate");
1841
+ if (!options.preserveShadowCandidate) {
1842
+ const projection = mirrorVisibleWorkspaceToShadow(input);
1843
+ restoreUnscopedCanonicalCheckoutSubtrees(input);
1844
+ const stagePathspecs = projection.opaqueRoots.flatMap((root) => [`:(exclude,literal)${root}`]);
1845
+ runGit(input, input.shadowRoot, ["add", "-A", "--", ".", ...stagePathspecs], "stage preserved workspace conflict candidate");
1846
+ forceStageCanonicalCheckoutFiles(input, projection.entries, projection.opaqueRoots);
1847
+ if (stagedPaths(input).length > 0) {
1848
+ runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit preserved workspace conflict candidate");
1849
+ }
1850
+ }
1851
+ const candidateHead = revParse(input, "HEAD");
1852
+ if (!candidateHead) throw new Error("the preserved workspace conflict candidate does not have a commit");
1853
+ assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, candidateHead);
1854
+ const upload = uploadWorkspaceCandidate(input, candidateHead);
1855
+ if (upload.exitCode !== 0) {
1856
+ throw new Error(upload.stderr || upload.stdout || "workspace conflict candidate upload failed");
1857
+ }
1858
+ const conflictPaths = [...new Set(options.conflictPaths)].sort();
1859
+ return {
1860
+ ...observed,
1861
+ outcome: "conflict_blocked",
1862
+ expectedHead: options.expectedHead,
1863
+ candidateHead,
1864
+ quarantineRef: input.quarantineRef,
1865
+ ...options.publishedHead ? { publishedHead: options.publishedHead } : {},
1866
+ rebaseCount: options.rebaseCount ?? 0,
1867
+ affectedProjects: affectedProjects(conflictPaths),
1868
+ affectedPaths: reportedPaths(conflictPaths),
1869
+ error: options.error,
1870
+ gitStatus: gitStatus(input)
1871
+ };
1872
+ } catch (error) {
1873
+ return {
1874
+ ...observed,
1875
+ outcome: "failed",
1876
+ rebaseCount: options.rebaseCount ?? 0,
1877
+ affectedProjects: affectedProjects(options.conflictPaths),
1878
+ affectedPaths: reportedPaths(options.conflictPaths),
1879
+ error: `Workspace synchronization found a conflict but could not preserve a durable candidate: ${error instanceof Error ? error.message : String(error)}`,
1880
+ gitStatus: gitStatus(input)
1881
+ };
1882
+ }
1883
+ }
1884
+ function preserveLargeDiffCandidate(input, observed, error) {
1885
+ try {
1886
+ if (stagedPaths(input).length > 0) {
1887
+ runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit blocked workspace candidate");
1888
+ }
1889
+ const candidateHead = revParse(input, "HEAD");
1890
+ if (!candidateHead) throw new Error("the blocked workspace candidate does not have a commit");
1891
+ assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, candidateHead);
1892
+ const upload = uploadWorkspaceCandidate(input, candidateHead);
1893
+ if (upload.exitCode !== 0) {
1894
+ throw new Error(upload.stderr || upload.stdout || "blocked workspace candidate upload failed");
1895
+ }
1896
+ return {
1897
+ ...observed,
1898
+ outcome: "large_diff_blocked",
1899
+ expectedHead: revParse(input, `origin/${WORKSPACE_BRANCH}`),
1900
+ candidateHead,
1901
+ quarantineRef: input.quarantineRef,
1902
+ error,
1903
+ gitStatus: gitStatus(input)
1904
+ };
1905
+ } catch (candidateError) {
1906
+ return {
1907
+ ...observed,
1908
+ outcome: "failed",
1909
+ error: `Workspace safety guard blocked publication but could not preserve a durable candidate: ${candidateError instanceof Error ? candidateError.message : String(candidateError)}`,
1910
+ gitStatus: gitStatus(input)
1911
+ };
1912
+ }
1913
+ }
1804
1914
  async function synchronizeWorkspace(rawInput) {
1805
1915
  let input = { ...rawInput, attemptId: rawInput.attemptId ?? crypto.randomUUID() };
1806
1916
  try {
1807
1917
  input = { ...input, projects: workspaceProjectsForSync(input.projects, input.trigger) };
1918
+ const isCanonicalRemediationSync = input.skipVisibleMirror === true && (input.trigger.type === "remediation" || input.trigger.type === "remediation_confirm");
1808
1919
  let mirroredGitlinkProjection = { entries: [], opaqueRoots: [], visiblePreimages: /* @__PURE__ */ new Map() };
1809
1920
  let fastForwardedFromHead = null;
1810
1921
  const shadowWasCreated = ensureShadowWorkspace(input);
1811
1922
  let hydrationPreimages = input.skipVisibleMirror ? hydrationPreimagesFromShadowRevision(input, shadowWasCreated ? null : revParse(input, "HEAD")) : /* @__PURE__ */ new Map();
1812
1923
  const remoteHeadAtStart = revParse(input, `origin/${WORKSPACE_BRANCH}`);
1813
1924
  const result = baseResult(input, remoteHeadAtStart);
1814
- const inboundConflictsAtStart = input.skipVisibleMirror && remoteHeadAtStart ? visibleHydrationConflictPaths(input, hydrationPreimages, `origin/${WORKSPACE_BRANCH}`) : [];
1925
+ if (input.expectedCanonicalHead !== void 0 && remoteHeadAtStart !== input.expectedCanonicalHead) {
1926
+ return {
1927
+ ...result,
1928
+ outcome: "failed",
1929
+ expectedHead: input.expectedCanonicalHead,
1930
+ publishedHead: remoteHeadAtStart ?? void 0,
1931
+ error: `canonical_head_advanced: expected ${input.expectedCanonicalHead ?? "unborn"}, fetched ${remoteHeadAtStart ?? "unborn"}`
1932
+ };
1933
+ }
1934
+ const inboundConflictsAtStart = input.skipVisibleMirror && !isCanonicalRemediationSync && remoteHeadAtStart ? visibleHydrationConflictPaths(input, hydrationPreimages, `origin/${WORKSPACE_BRANCH}`) : [];
1815
1935
  if (input.resetToCanonical) {
1816
1936
  if (input.trigger.canonicalCheckoutOnly) {
1817
1937
  throw new Error("A canonical-checkout-only synchronization cannot reset the authoritative resolver checkout");
@@ -1825,6 +1945,8 @@ async function synchronizeWorkspace(rawInput) {
1825
1945
  gitStatus: gitStatus(input)
1826
1946
  };
1827
1947
  }
1948
+ const inProgressRemediation = remediationRebaseInProgressResult(input, result);
1949
+ if (inProgressRemediation) return inProgressRemediation;
1828
1950
  if (!input.skipVisibleMirror) resetUncommittedShadowSnapshot(input);
1829
1951
  restoreUnscopedCanonicalCheckoutSubtrees(input);
1830
1952
  if (!input.skipVisibleMirror) {
@@ -1857,18 +1979,18 @@ async function synchronizeWorkspace(rawInput) {
1857
1979
  const destructiveReductions = destructiveCheckoutReductions(input, baseRevision);
1858
1980
  if (destructiveReductions.length > 0 && !input.confirmedLargeDiff) {
1859
1981
  const summary = destructiveReductions.map(({ root, trackedBefore, trackedAfter }) => `${root} (${trackedBefore} -> ${trackedAfter} tracked files)`).join(", ");
1860
- return {
1861
- ...observed,
1862
- outcome: "large_diff_blocked",
1863
- error: `Workspace safety guard blocked a destructive checkout reduction: ${summary}`
1864
- };
1982
+ return preserveLargeDiffCandidate(
1983
+ input,
1984
+ observed,
1985
+ `Workspace safety guard blocked a destructive checkout reduction: ${summary}`
1986
+ );
1865
1987
  }
1866
1988
  if (paths.length > 0 && diffSizeBytes > MAX_WORKSPACE_SYNC_DIFF_BYTES && !input.confirmedLargeDiff && !input.trigger.canonicalCheckoutOnly) {
1867
- return {
1868
- ...observed,
1869
- outcome: "large_diff_blocked",
1870
- error: `Workspace safety guard blocked a ${diffSizeBytes}-byte diff above the ${MAX_WORKSPACE_SYNC_DIFF_BYTES}-byte automatic publication limit.`
1871
- };
1989
+ return preserveLargeDiffCandidate(
1990
+ input,
1991
+ observed,
1992
+ `Workspace safety guard blocked a ${diffSizeBytes}-byte diff above the ${MAX_WORKSPACE_SYNC_DIFF_BYTES}-byte automatic publication limit.`
1993
+ );
1872
1994
  }
1873
1995
  if (stagedWorkingPaths.length > 0) {
1874
1996
  runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit workspace changes");
@@ -1880,15 +2002,12 @@ async function synchronizeWorkspace(rawInput) {
1880
2002
  const hasUnpushedCommit = candidateHead ? !hasRemoteMain || !tryGit(input, input.shadowRoot, ["merge-base", "--is-ancestor", candidateHead, `origin/${WORKSPACE_BRANCH}`]) : false;
1881
2003
  if (hasUnpushedCommit && inboundConflictsAtStart.length > 0) {
1882
2004
  const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...inboundConflictsAtStart])].sort();
1883
- return {
1884
- ...observed,
1885
- outcome: "failed",
1886
- candidateHead: candidateHead ?? void 0,
1887
- affectedProjects: affectedProjects(conflictPaths),
1888
- affectedPaths: reportedPaths(conflictPaths),
1889
- error: "Inbound workspace update overlaps local visible changes; the old-base candidate was preserved for reconciliation.",
1890
- gitStatus: gitStatus(input)
1891
- };
2005
+ return preserveWorkspaceConflictCandidate(input, observed, {
2006
+ baseRevision: candidateHead,
2007
+ expectedHead: revParse(input, `origin/${WORKSPACE_BRANCH}`),
2008
+ conflictPaths,
2009
+ error: "Inbound workspace update overlaps local visible changes; the old-base candidate was preserved for reconciliation."
2010
+ });
1892
2011
  }
1893
2012
  if (!hasUnpushedCommit) {
1894
2013
  const localHead = revParse(input, "HEAD");
@@ -1897,22 +2016,21 @@ async function synchronizeWorkspace(rawInput) {
1897
2016
  runGit(input, input.shadowRoot, ["reset", "--hard", `origin/${WORKSPACE_BRANCH}`], "update workspace from canonical state");
1898
2017
  runGit(input, input.shadowRoot, ["clean", "-fd"], "clean updated workspace");
1899
2018
  assertPublishedCanonicalCheckoutTree(input, currentRemoteHead, sampledCanonicalCheckoutTreeHash);
1900
- mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots, hydrationPreimages);
1901
- const inboundConflicts = input.trigger.canonicalCheckoutOnly ? [] : localHead ? hydrationConflictPaths(input, hydrationPreimages, localHead, currentRemoteHead) : visibleHydrationConflictPaths(input, hydrationPreimages, currentRemoteHead);
2019
+ if (!isCanonicalRemediationSync) {
2020
+ mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots, hydrationPreimages);
2021
+ }
2022
+ const inboundConflicts = input.trigger.canonicalCheckoutOnly || isCanonicalRemediationSync ? [] : localHead ? hydrationConflictPaths(input, hydrationPreimages, localHead, currentRemoteHead) : visibleHydrationConflictPaths(input, hydrationPreimages, currentRemoteHead);
1902
2023
  if (inboundConflicts.length > 0 && localHead) {
1903
2024
  runGit(input, input.shadowRoot, ["reset", "--hard", localHead], "preserve old workspace base after inbound conflict");
1904
2025
  runGit(input, input.shadowRoot, ["clean", "-fd"], "clean preserved old workspace base");
1905
2026
  const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...inboundConflicts])].sort();
1906
- return {
1907
- ...observed,
1908
- outcome: "failed",
1909
- candidateHead: candidateHead ?? void 0,
2027
+ return preserveWorkspaceConflictCandidate(input, observed, {
2028
+ baseRevision: localHead,
2029
+ expectedHead: currentRemoteHead,
1910
2030
  publishedHead: currentRemoteHead,
1911
- affectedProjects: affectedProjects(conflictPaths),
1912
- affectedPaths: reportedPaths(conflictPaths),
1913
- error: "Inbound workspace update overlaps local visible changes; disjoint updates were hydrated and the old base was preserved for reconciliation.",
1914
- gitStatus: gitStatus(input)
1915
- };
2031
+ conflictPaths,
2032
+ error: "Inbound workspace update overlaps local visible changes; disjoint updates were hydrated and the old base was preserved for reconciliation."
2033
+ });
1916
2034
  }
1917
2035
  return {
1918
2036
  ...observed,
@@ -1925,8 +2043,10 @@ async function synchronizeWorkspace(rawInput) {
1925
2043
  const authoritativeHead = currentRemoteHead ?? localHead;
1926
2044
  if (authoritativeHead) {
1927
2045
  assertPublishedCanonicalCheckoutTree(input, authoritativeHead, sampledCanonicalCheckoutTreeHash);
1928
- mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots, hydrationPreimages);
1929
- const hydrationConflicts2 = input.trigger.canonicalCheckoutOnly ? [] : fastForwardedFromHead ? hydrationConflictPaths(input, hydrationPreimages, fastForwardedFromHead, authoritativeHead) : visibleHydrationConflictPaths(input, hydrationPreimages, authoritativeHead);
2046
+ if (!isCanonicalRemediationSync) {
2047
+ mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots, hydrationPreimages);
2048
+ }
2049
+ const hydrationConflicts2 = input.trigger.canonicalCheckoutOnly || isCanonicalRemediationSync ? [] : fastForwardedFromHead ? hydrationConflictPaths(input, hydrationPreimages, fastForwardedFromHead, authoritativeHead) : visibleHydrationConflictPaths(input, hydrationPreimages, authoritativeHead);
1930
2050
  if (hydrationConflicts2.length > 0 && fastForwardedFromHead) {
1931
2051
  runGit(
1932
2052
  input,
@@ -1935,16 +2055,13 @@ async function synchronizeWorkspace(rawInput) {
1935
2055
  "preserve old workspace base after new-checkout fast-forward conflict"
1936
2056
  );
1937
2057
  runGit(input, input.shadowRoot, ["clean", "-fd"], "clean preserved new-checkout workspace base");
1938
- return {
1939
- ...observed,
1940
- outcome: "failed",
1941
- candidateHead: candidateHead ?? void 0,
2058
+ return preserveWorkspaceConflictCandidate(input, observed, {
2059
+ baseRevision: fastForwardedFromHead,
2060
+ expectedHead: currentRemoteHead,
1942
2061
  publishedHead: currentRemoteHead ?? void 0,
1943
- affectedProjects: affectedProjects(hydrationConflicts2),
1944
- affectedPaths: reportedPaths(hydrationConflicts2),
1945
- error: "Workspace fast-forward for a new checkout overlaps local visible changes; compatible updates were hydrated and the old base was preserved for reconciliation.",
1946
- gitStatus: gitStatus(input)
1947
- };
2062
+ conflictPaths: hydrationConflicts2,
2063
+ error: "Workspace fast-forward for a new checkout overlaps local visible changes; compatible updates were hydrated and the old base was preserved for reconciliation."
2064
+ });
1948
2065
  }
1949
2066
  }
1950
2067
  return {
@@ -1964,17 +2081,14 @@ async function synchronizeWorkspace(rawInput) {
1964
2081
  const conflicted = runGitResult(input, input.shadowRoot, ["diff", "--name-only", "--diff-filter=U", "-z"]);
1965
2082
  const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...conflicted.stdout.split("\0").filter(Boolean)])].sort();
1966
2083
  runGit(input, input.shadowRoot, ["rebase", "--abort"], "preserve workspace candidate after rebase conflict");
1967
- return {
1968
- ...observed,
1969
- outcome: "failed",
2084
+ return preserveWorkspaceConflictCandidate(input, observed, {
2085
+ baseRevision: candidateHead,
1970
2086
  expectedHead,
1971
- candidateHead: candidateHead ?? void 0,
1972
2087
  rebaseCount,
1973
- affectedProjects: affectedProjects(conflictPaths),
1974
- affectedPaths: reportedPaths(conflictPaths),
1975
- error: `Workspace candidate conflicts with canonical head ${expectedHead}; local changes were preserved for reconciliation.`,
1976
- gitStatus: gitStatus(input)
1977
- };
2088
+ preserveShadowCandidate: isCanonicalRemediationSync,
2089
+ conflictPaths,
2090
+ error: `Workspace candidate conflicts with canonical head ${expectedHead}; local changes were preserved for reconciliation.`
2091
+ });
1978
2092
  }
1979
2093
  candidateHead = revParse(input, "HEAD");
1980
2094
  assertPublishedCanonicalCheckoutTree(input, "HEAD", sampledCanonicalCheckoutTreeHash);
@@ -1990,8 +2104,10 @@ async function synchronizeWorkspace(rawInput) {
1990
2104
  };
1991
2105
  }
1992
2106
  assertPublishedCanonicalCheckoutTree(input, candidateHead, sampledCanonicalCheckoutTreeHash);
1993
- mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots, hydrationPreimages);
1994
- const hydrationConflicts = input.trigger.canonicalCheckoutOnly ? [] : candidateHeadBeforeRebase ? hydrationConflictPaths(input, hydrationPreimages, candidateHeadBeforeRebase, "HEAD") : visibleHydrationConflictPaths(input, hydrationPreimages, "HEAD");
2107
+ if (!isCanonicalRemediationSync) {
2108
+ mirrorShadowWorkspaceToVisible(input, mirroredGitlinkProjection.opaqueRoots, hydrationPreimages);
2109
+ }
2110
+ const hydrationConflicts = input.trigger.canonicalCheckoutOnly || isCanonicalRemediationSync ? [] : candidateHeadBeforeRebase ? hydrationConflictPaths(input, hydrationPreimages, candidateHeadBeforeRebase, "HEAD") : visibleHydrationConflictPaths(input, hydrationPreimages, "HEAD");
1995
2111
  if (hydrationConflicts.length > 0 && candidateHeadBeforeRebase) {
1996
2112
  runGit(
1997
2113
  input,
@@ -2001,17 +2117,13 @@ async function synchronizeWorkspace(rawInput) {
2001
2117
  );
2002
2118
  runGit(input, input.shadowRoot, ["clean", "-fd"], "clean old-base workspace candidate");
2003
2119
  const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...hydrationConflicts])].sort();
2004
- return {
2005
- ...observed,
2006
- outcome: "failed",
2120
+ return preserveWorkspaceConflictCandidate(input, observed, {
2121
+ baseRevision: candidateHeadBeforeRebase,
2007
2122
  expectedHead,
2008
- candidateHead,
2009
2123
  rebaseCount,
2010
- affectedProjects: affectedProjects(conflictPaths),
2011
- affectedPaths: reportedPaths(conflictPaths),
2012
- error: "Workspace changed while its candidate was reconciling; visible changes and the old-base candidate were preserved.",
2013
- gitStatus: gitStatus(input)
2014
- };
2124
+ conflictPaths,
2125
+ error: "Workspace changed while its candidate was reconciling; visible changes and the old-base candidate were preserved."
2126
+ });
2015
2127
  }
2016
2128
  const upload = uploadWorkspaceCandidate(input, candidateHead);
2017
2129
  if (upload.exitCode !== 0) {
@@ -2043,6 +2155,79 @@ async function synchronizeWorkspace(rawInput) {
2043
2155
  };
2044
2156
  }
2045
2157
  }
2158
+ async function convergeWorkspaceHead(rawInput, options) {
2159
+ const input = {
2160
+ ...rawInput,
2161
+ projects: workspaceProjectsForSync(rawInput.projects, rawInput.trigger)
2162
+ };
2163
+ if (input.trigger.canonicalCheckoutOnly) {
2164
+ throw new Error("Ordinary inbound convergence cannot target a canonical resolver checkout");
2165
+ }
2166
+ ensureShadowWorkspace(input);
2167
+ const fetchedLocalHead = revParse(input, "HEAD");
2168
+ const fetchedCanonicalHead = revParse(input, `origin/${WORKSPACE_BRANCH}`);
2169
+ if (!options.hydrateVisible) {
2170
+ return {
2171
+ localHead: fetchedLocalHead,
2172
+ canonicalHead: fetchedCanonicalHead,
2173
+ localChanges: false,
2174
+ hydrated: false,
2175
+ hydrationConflicts: []
2176
+ };
2177
+ }
2178
+ resetUncommittedShadowSnapshot(input);
2179
+ restoreUnscopedCanonicalCheckoutSubtrees(input);
2180
+ const localHead = fetchedLocalHead;
2181
+ const canonicalHead = fetchedCanonicalHead;
2182
+ const projection = mirrorVisibleWorkspaceToShadow(input);
2183
+ runGit(
2184
+ input,
2185
+ input.shadowRoot,
2186
+ ["add", "-A", "--", ".", ...projection.opaqueRoots.map((root) => `:(exclude,literal)${root}`)],
2187
+ "stage inbound workspace observation"
2188
+ );
2189
+ forceStageCanonicalCheckoutFiles(input, projection.entries, projection.opaqueRoots);
2190
+ const stagedTree = runGit(input, input.shadowRoot, ["write-tree"], "write inbound workspace observation tree");
2191
+ const headTree = localHead ? revParse(input, `${localHead}^{tree}`) : null;
2192
+ const localChanges = stagedTree !== headTree;
2193
+ if (!options.hydrateVisible || localChanges || !canonicalHead || localHead === canonicalHead) {
2194
+ resetUncommittedShadowSnapshot(input);
2195
+ return { localHead, canonicalHead, localChanges, hydrated: false, hydrationConflicts: [] };
2196
+ }
2197
+ const preimages = projection.visiblePreimages ?? hydrationPreimagesFromShadowRevision(input, localHead);
2198
+ const hydrationConflicts = localHead ? hydrationConflictPaths(input, preimages, localHead, canonicalHead) : [];
2199
+ if (hydrationConflicts.length > 0) {
2200
+ resetUncommittedShadowSnapshot(input);
2201
+ return { localHead, canonicalHead, localChanges: true, hydrated: false, hydrationConflicts };
2202
+ }
2203
+ runGit(input, input.shadowRoot, ["reset", "--hard", canonicalHead], "fast-forward workspace to available canonical head");
2204
+ runGit(input, input.shadowRoot, ["clean", "-fd"], "clean fast-forwarded workspace");
2205
+ mirrorShadowWorkspaceToVisible(input, projection.opaqueRoots, preimages);
2206
+ return { localHead: canonicalHead, canonicalHead, localChanges: false, hydrated: true, hydrationConflicts: [] };
2207
+ }
2208
+ function calculateWorkspaceLocalDiffFingerprint(rawInput) {
2209
+ const input = {
2210
+ ...rawInput,
2211
+ projects: workspaceProjectsForSync(rawInput.projects, rawInput.trigger)
2212
+ };
2213
+ if (!import_node_fs.default.existsSync(import_node_path.default.join(input.shadowRoot, ".git"))) {
2214
+ throw new Error("Cannot inspect local workspace changes before first bootstrap");
2215
+ }
2216
+ resetUncommittedShadowSnapshot(input);
2217
+ restoreUnscopedCanonicalCheckoutSubtrees(input);
2218
+ const projection = mirrorVisibleWorkspaceToShadow(input);
2219
+ runGit(
2220
+ input,
2221
+ input.shadowRoot,
2222
+ ["add", "-A", "--", ".", ...projection.opaqueRoots.map((root) => `:(exclude,literal)${root}`)],
2223
+ "stage local workspace fingerprint"
2224
+ );
2225
+ forceStageCanonicalCheckoutFiles(input, projection.entries, projection.opaqueRoots);
2226
+ const stagedTree = runGit(input, input.shadowRoot, ["write-tree"], "write local workspace fingerprint tree");
2227
+ const headTree = revParse(input, "HEAD^{tree}");
2228
+ resetUncommittedShadowSnapshot(input);
2229
+ return stagedTree === headTree ? (0, import_node_crypto.createHash)("sha256").update("").digest("hex") : (0, import_node_crypto.createHash)("sha256").update(stagedTree).digest("hex");
2230
+ }
2046
2231
  function calculateWorkspaceDiffFingerprint(rawInput) {
2047
2232
  const input = {
2048
2233
  ...rawInput,
@@ -2096,6 +2281,9 @@ class WorkspaceSyncSingleFlight {
2096
2281
  fingerprintPrepared(prepare) {
2097
2282
  return this.enqueue(() => calculateWorkspaceDiffFingerprint(prepare()));
2098
2283
  }
2284
+ runExclusive(operation) {
2285
+ return this.enqueue(operation);
2286
+ }
2099
2287
  runMutation(operation) {
2100
2288
  return this.mutationGate.runMutation(operation);
2101
2289
  }
@@ -2112,11 +2300,13 @@ class WorkspaceSyncSingleFlight {
2112
2300
  WORKSPACE_BRANCH,
2113
2301
  WORKSPACE_INTENT_REF_PREFIX,
2114
2302
  WORKSPACE_NATIVE_PLANS_RELATIVE_PATH,
2115
- WORKSPACE_PERIODIC_SCAN_INTERVAL_MS,
2303
+ WORKSPACE_PUBLICATION_REF_PREFIX,
2116
2304
  WorkspaceSyncSingleFlight,
2117
2305
  assertCanonicalCheckoutIndexMatchesWorktree,
2118
2306
  assertWorkspaceQuarantineRef,
2119
2307
  calculateWorkspaceDiffFingerprint,
2308
+ calculateWorkspaceLocalDiffFingerprint,
2309
+ convergeWorkspaceHead,
2120
2310
  encodeWorkspaceBranch,
2121
2311
  mirrorShadowWorkspaceToVisible,
2122
2312
  mirrorVisibleWorkspaceToShadow,