@remixhq/core 0.1.17 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/collab.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  writeCollabBinding,
9
9
  writeCollabBindingSnapshot,
10
10
  writeJsonAtomic
11
- } from "./chunk-YCFLOHJV.js";
11
+ } from "./chunk-DBVN42RF.js";
12
12
  import {
13
13
  applyUnifiedDiffToWorktree,
14
14
  assertRepoSnapshotUnchanged,
@@ -30,13 +30,13 @@ import {
30
30
  requireCurrentBranch,
31
31
  setRemoteOriginUrl,
32
32
  summarizeUnifiedDiff
33
- } from "./chunk-WT6VRLXU.js";
33
+ } from "./chunk-S4ECO35X.js";
34
34
  import {
35
35
  REMIX_ERROR_CODES
36
36
  } from "./chunk-GC2MOT3U.js";
37
37
  import {
38
38
  RemixError
39
- } from "./chunk-YZ34ICNN.js";
39
+ } from "./chunk-7XJGOKEO.js";
40
40
 
41
41
  // src/application/collab/appDeltaCache.ts
42
42
  var APP_DELTA_CACHE_TTL_MS = 5e3;
@@ -47,6 +47,8 @@ function buildAppDeltaCacheKey(appId, payload) {
47
47
  appId,
48
48
  payload.baseHeadHash,
49
49
  payload.targetHeadHash ?? "",
50
+ payload.baseRevisionId ?? "",
51
+ payload.targetRevisionId ?? "",
50
52
  payload.localSnapshotHash ?? "",
51
53
  payload.repoFingerprint ?? "",
52
54
  payload.remoteUrl ?? "",
@@ -189,9 +191,6 @@ function getAsyncJobDir(jobId) {
189
191
  function getAsyncJobFilePath(jobId) {
190
192
  return path.join(getAsyncJobDir(jobId), "job.json");
191
193
  }
192
- function getAsyncJobBundlePath(jobId) {
193
- return path.join(getAsyncJobDir(jobId), "bundle.bundle");
194
- }
195
194
  function getLogsRoot() {
196
195
  return path.join(getCollabStateRoot(), "logs");
197
196
  }
@@ -493,11 +492,11 @@ async function readLocalBaseline(params) {
493
492
  const raw = await fs2.readFile(getBaselinePath(params), "utf8");
494
493
  const parsed = JSON.parse(raw);
495
494
  if (!parsed || typeof parsed !== "object") return null;
496
- if (parsed.schemaVersion !== 1 || typeof parsed.key !== "string" || typeof parsed.repoRoot !== "string") {
495
+ if (![1, 2].includes(Number(parsed.schemaVersion)) || typeof parsed.key !== "string" || typeof parsed.repoRoot !== "string") {
497
496
  return null;
498
497
  }
499
498
  return {
500
- schemaVersion: 1,
499
+ schemaVersion: Number(parsed.schemaVersion) === 2 ? 2 : 1,
501
500
  key: parsed.key,
502
501
  repoRoot: parsed.repoRoot,
503
502
  repoFingerprint: parsed.repoFingerprint ?? null,
@@ -506,6 +505,8 @@ async function readLocalBaseline(params) {
506
505
  branchName: parsed.branchName ?? null,
507
506
  lastSnapshotId: parsed.lastSnapshotId ?? null,
508
507
  lastSnapshotHash: parsed.lastSnapshotHash ?? null,
508
+ lastServerRevisionId: parsed.lastServerRevisionId ?? null,
509
+ lastServerTreeHash: parsed.lastServerTreeHash ?? null,
509
510
  lastServerHeadHash: parsed.lastServerHeadHash ?? null,
510
511
  lastSeenLocalCommitHash: parsed.lastSeenLocalCommitHash ?? null,
511
512
  updatedAt: String(parsed.updatedAt ?? "")
@@ -517,7 +518,7 @@ async function readLocalBaseline(params) {
517
518
  async function writeLocalBaseline(baseline) {
518
519
  const key = buildLaneStateKey(baseline);
519
520
  const normalized = {
520
- schemaVersion: 1,
521
+ schemaVersion: 2,
521
522
  key,
522
523
  repoRoot: baseline.repoRoot,
523
524
  repoFingerprint: baseline.repoFingerprint ?? null,
@@ -526,6 +527,8 @@ async function writeLocalBaseline(baseline) {
526
527
  branchName: baseline.branchName ?? null,
527
528
  lastSnapshotId: baseline.lastSnapshotId ?? null,
528
529
  lastSnapshotHash: baseline.lastSnapshotHash ?? null,
530
+ lastServerRevisionId: baseline.lastServerRevisionId ?? null,
531
+ lastServerTreeHash: baseline.lastServerTreeHash ?? null,
529
532
  lastServerHeadHash: baseline.lastServerHeadHash ?? null,
530
533
  lastSeenLocalCommitHash: baseline.lastSeenLocalCommitHash ?? null,
531
534
  updatedAt: baseline.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -912,6 +915,7 @@ function normalizeJob2(input) {
912
915
  prompt: input.prompt,
913
916
  assistantResponse: input.assistantResponse,
914
917
  baselineSnapshotId: input.baselineSnapshotId ?? null,
918
+ baselineServerRevisionId: input.baselineServerRevisionId ?? null,
915
919
  baselineServerHeadHash: input.baselineServerHeadHash ?? null,
916
920
  currentSnapshotId: input.currentSnapshotId,
917
921
  capturedAt: input.capturedAt ?? now,
@@ -946,6 +950,7 @@ async function readPendingFinalizeJob(jobId) {
946
950
  prompt: String(parsed.prompt ?? ""),
947
951
  assistantResponse: String(parsed.assistantResponse ?? ""),
948
952
  baselineSnapshotId: parsed.baselineSnapshotId ?? null,
953
+ baselineServerRevisionId: parsed.baselineServerRevisionId ?? null,
949
954
  baselineServerHeadHash: parsed.baselineServerHeadHash ?? null,
950
955
  currentSnapshotId: String(parsed.currentSnapshotId ?? ""),
951
956
  capturedAt: parsed.capturedAt,
@@ -1462,6 +1467,15 @@ function shouldRequireRemoteLaneForCurrentBranch(params) {
1462
1467
  if (params.currentBranch === defaultBranch) return false;
1463
1468
  return !params.binding.laneId || params.binding.currentAppId === params.binding.upstreamAppId;
1464
1469
  }
1470
+ function resolveLaneLookupProjectId(params) {
1471
+ const currentBranch = normalizeBranchName(params.currentBranch);
1472
+ const defaultBranch = normalizeBranchName(params.defaultBranch);
1473
+ const localProjectId = params.localBinding.projectId ?? null;
1474
+ if (currentBranch && currentBranch !== defaultBranch && localProjectId) {
1475
+ return localProjectId;
1476
+ }
1477
+ return params.explicitRootProjectId ?? (params.requireRemoteLane ? void 0 : localProjectId ?? params.fallbackProjectId ?? void 0);
1478
+ }
1465
1479
  async function persistResolvedLane(repoRoot, binding) {
1466
1480
  await writeCollabBinding(repoRoot, {
1467
1481
  projectId: binding.projectId,
@@ -1540,7 +1554,14 @@ async function resolveActiveLaneBindingUncached(params, state) {
1540
1554
  };
1541
1555
  }
1542
1556
  const laneResp2 = await params.api.resolveProjectLaneBinding({
1543
- projectId: state.explicitRootBinding?.projectId ?? (requireRemoteLane ? void 0 : localBinding.projectId ?? state.projectId ?? void 0),
1557
+ projectId: resolveLaneLookupProjectId({
1558
+ explicitRootProjectId: state.explicitRootBinding?.projectId,
1559
+ localBinding,
1560
+ currentBranch,
1561
+ defaultBranch: state.defaultBranch,
1562
+ requireRemoteLane,
1563
+ fallbackProjectId: state.projectId
1564
+ }),
1544
1565
  repoFingerprint: state.repoFingerprint ?? void 0,
1545
1566
  remoteUrl: state.remoteUrl ?? void 0,
1546
1567
  defaultBranch: state.defaultBranch ?? void 0,
@@ -1701,6 +1722,8 @@ function buildBaseState() {
1701
1722
  branchName: null,
1702
1723
  localCommitHash: null,
1703
1724
  currentSnapshotHash: null,
1725
+ currentServerRevisionId: null,
1726
+ currentServerTreeHash: null,
1704
1727
  currentServerHeadHash: null,
1705
1728
  currentServerHeadCommitId: null,
1706
1729
  worktreeClean: false,
@@ -1734,6 +1757,8 @@ function buildBaseState() {
1734
1757
  baseline: {
1735
1758
  lastSnapshotId: null,
1736
1759
  lastSnapshotHash: null,
1760
+ lastServerRevisionId: null,
1761
+ lastServerTreeHash: null,
1737
1762
  lastServerHeadHash: null,
1738
1763
  lastSeenLocalCommitHash: null
1739
1764
  }
@@ -1860,6 +1885,8 @@ async function collabDetectRepoState(params) {
1860
1885
  summarizeAsyncJobs({ repoRoot, branchName: binding.branchName ?? null })
1861
1886
  ]);
1862
1887
  const appHead = unwrapResponseObject(headResp, "app head");
1888
+ detected.currentServerRevisionId = appHead.headRevisionId ?? null;
1889
+ detected.currentServerTreeHash = appHead.treeHash ?? null;
1863
1890
  detected.currentServerHeadHash = appHead.headCommitHash;
1864
1891
  detected.currentServerHeadCommitId = appHead.headCommitId;
1865
1892
  detected.currentSnapshotHash = inspection.snapshotHash;
@@ -1868,6 +1895,8 @@ async function collabDetectRepoState(params) {
1868
1895
  detected.baseline = {
1869
1896
  lastSnapshotId: baseline?.lastSnapshotId ?? null,
1870
1897
  lastSnapshotHash: baseline?.lastSnapshotHash ?? null,
1898
+ lastServerRevisionId: baseline?.lastServerRevisionId ?? null,
1899
+ lastServerTreeHash: baseline?.lastServerTreeHash ?? null,
1871
1900
  lastServerHeadHash: baseline?.lastServerHeadHash ?? null,
1872
1901
  lastSeenLocalCommitHash: baseline?.lastSeenLocalCommitHash ?? null
1873
1902
  };
@@ -1877,6 +1906,7 @@ async function collabDetectRepoState(params) {
1877
1906
  const bootstrapResp = await params.api.getAppDelta(binding.currentAppId, {
1878
1907
  baseHeadHash: localCommitHash,
1879
1908
  targetHeadHash: appHead.headCommitHash,
1909
+ targetRevisionId: appHead.headRevisionId,
1880
1910
  repoFingerprint: binding.repoFingerprint ?? void 0,
1881
1911
  remoteUrl: binding.remoteUrl ?? void 0,
1882
1912
  defaultBranch: binding.defaultBranch ?? void 0
@@ -1899,7 +1929,7 @@ async function collabDetectRepoState(params) {
1899
1929
  }
1900
1930
  }
1901
1931
  detected.repoState = "external_local_base_changed";
1902
- detected.hint = "No local Remix baseline exists for this lane yet. Run `remix collab re-anchor` to anchor this checkout.";
1932
+ detected.hint = "No local Remix revision baseline exists for this lane yet. Run `remix collab init` or sync this lane to seed the baseline.";
1903
1933
  return detected;
1904
1934
  }
1905
1935
  const localHeadMovedSinceBaseline = Boolean(baseline.lastSeenLocalCommitHash) && localCommitHash !== baseline.lastSeenLocalCommitHash;
@@ -1918,7 +1948,30 @@ async function collabDetectRepoState(params) {
1918
1948
  return detected;
1919
1949
  }
1920
1950
  const localChanged = inspection.snapshotHash !== baseline.lastSnapshotHash;
1921
- const serverChanged = appHead.headCommitHash !== baseline.lastServerHeadHash;
1951
+ const serverHeadChanged = appHead.headCommitHash !== baseline.lastServerHeadHash;
1952
+ const revisionChanged = Boolean(
1953
+ baseline.lastServerRevisionId && (appHead.headRevisionId ?? null) !== baseline.lastServerRevisionId
1954
+ );
1955
+ const equivalentRevisionDrift = revisionChanged && !serverHeadChanged;
1956
+ if (equivalentRevisionDrift) {
1957
+ await writeLocalBaseline({
1958
+ repoRoot,
1959
+ repoFingerprint: binding.repoFingerprint,
1960
+ laneId: binding.laneId,
1961
+ currentAppId: binding.currentAppId,
1962
+ branchName: binding.branchName,
1963
+ lastSnapshotId: baseline.lastSnapshotId,
1964
+ lastSnapshotHash: baseline.lastSnapshotHash,
1965
+ lastServerRevisionId: appHead.headRevisionId ?? null,
1966
+ lastServerTreeHash: appHead.treeHash ?? baseline.lastServerTreeHash ?? null,
1967
+ lastServerHeadHash: appHead.headCommitHash,
1968
+ lastSeenLocalCommitHash: baseline.lastSeenLocalCommitHash
1969
+ });
1970
+ detected.baseline.lastServerRevisionId = appHead.headRevisionId ?? null;
1971
+ detected.baseline.lastServerTreeHash = appHead.treeHash ?? baseline.lastServerTreeHash ?? null;
1972
+ detected.baseline.lastServerHeadHash = appHead.headCommitHash;
1973
+ }
1974
+ const serverChanged = serverHeadChanged;
1922
1975
  if (!localChanged && !serverChanged) {
1923
1976
  detected.repoState = "idle";
1924
1977
  return detected;
@@ -2347,6 +2400,7 @@ function buildWorkspaceMetadata(params) {
2347
2400
  recordingMode: "boundary_delta",
2348
2401
  baselineSnapshotId: params.baselineSnapshotId,
2349
2402
  currentSnapshotId: params.currentSnapshotId,
2403
+ baselineServerRevisionId: params.baselineServerRevisionId ?? null,
2350
2404
  baselineServerHeadHash: params.baselineServerHeadHash,
2351
2405
  currentSnapshotHash: params.currentSnapshotHash,
2352
2406
  localCommitHash: params.localCommitHash,
@@ -2365,6 +2419,59 @@ function buildWorkspaceMetadata(params) {
2365
2419
  }
2366
2420
  return metadata;
2367
2421
  }
2422
+ async function findExistingChangeStepByIdempotency(params) {
2423
+ const idempotencyKey = params.idempotencyKey?.trim();
2424
+ if (!idempotencyKey) return null;
2425
+ const resp = await params.api.listChangeSteps(params.appId, { limit: 1, idempotencyKey });
2426
+ const responseObject = unwrapResponseObject(
2427
+ resp,
2428
+ "change step list"
2429
+ );
2430
+ const steps = Array.isArray(responseObject) ? responseObject : Array.isArray(responseObject.items) ? responseObject.items : [];
2431
+ return steps.find((step) => step.idempotencyKey === idempotencyKey) ?? null;
2432
+ }
2433
+ async function writeBaselineFromSucceededChangeStep(params) {
2434
+ const nextServerHeadHash = typeof params.changeStep.headCommitHash === "string" ? params.changeStep.headCommitHash.trim() : "";
2435
+ if (!nextServerHeadHash) {
2436
+ throw buildFinalizeCliError({
2437
+ message: "Backend returned a succeeded change step without a head commit hash.",
2438
+ exitCode: 1,
2439
+ hint: "This is a backend invariant violation; retry will not help. Run `remix collab status` before trying again.",
2440
+ disposition: "terminal",
2441
+ reason: "missing_head_commit_hash"
2442
+ });
2443
+ }
2444
+ let nextServerRevisionId = typeof params.changeStep.resultRevisionId === "string" ? params.changeStep.resultRevisionId.trim() : "";
2445
+ let nextServerTreeHash = null;
2446
+ if (!nextServerRevisionId) {
2447
+ const freshHeadResp = await params.api.getAppHead(params.job.currentAppId);
2448
+ const freshHead = unwrapResponseObject(freshHeadResp, "app head");
2449
+ if (freshHead.headCommitHash !== nextServerHeadHash || !freshHead.headRevisionId) {
2450
+ throw buildFinalizeCliError({
2451
+ message: "Backend returned a succeeded change step without a matching result revision.",
2452
+ exitCode: 1,
2453
+ hint: "The local baseline was not advanced because the post-step revision could not be verified. Restart the backend/CLI and retry after checking `remix collab status`.",
2454
+ disposition: "terminal",
2455
+ reason: "missing_result_revision_id"
2456
+ });
2457
+ }
2458
+ nextServerRevisionId = freshHead.headRevisionId;
2459
+ nextServerTreeHash = freshHead.treeHash ?? null;
2460
+ }
2461
+ await writeLocalBaseline({
2462
+ repoRoot: params.job.repoRoot,
2463
+ repoFingerprint: params.job.repoFingerprint,
2464
+ laneId: params.job.laneId,
2465
+ currentAppId: params.job.currentAppId,
2466
+ branchName: params.job.branchName,
2467
+ lastSnapshotId: params.snapshot.id,
2468
+ lastSnapshotHash: params.snapshot.snapshotHash,
2469
+ lastServerRevisionId: nextServerRevisionId,
2470
+ lastServerTreeHash: nextServerTreeHash,
2471
+ lastServerHeadHash: nextServerHeadHash,
2472
+ lastSeenLocalCommitHash: params.snapshot.localCommitHash
2473
+ });
2474
+ }
2368
2475
  async function harvestPreTurnEvents(repoRoot, fromCommit, toCommit) {
2369
2476
  if (!toCommit) return null;
2370
2477
  try {
@@ -2425,12 +2532,12 @@ async function processClaimedPendingFinalizeJobInner(params) {
2425
2532
  throw buildFinalizeCliError({
2426
2533
  message: "Local baseline is missing for this queued finalize job.",
2427
2534
  exitCode: 2,
2428
- hint: "Run `remix collab re-anchor` to anchor the repository again.",
2535
+ hint: "Run `remix collab init` to seed this checkout's revision baseline.",
2429
2536
  disposition: "terminal",
2430
2537
  reason: "baseline_missing"
2431
2538
  });
2432
2539
  }
2433
- const baselineDrifted = baseline.lastSnapshotId !== job.baselineSnapshotId || baseline.lastServerHeadHash !== job.baselineServerHeadHash;
2540
+ const baselineDrifted = baseline.lastSnapshotId !== job.baselineSnapshotId || (job.baselineServerRevisionId ? baseline.lastServerRevisionId !== job.baselineServerRevisionId : false) || baseline.lastServerHeadHash !== job.baselineServerHeadHash;
2434
2541
  const appHead = unwrapResponseObject(appHeadResp, "app head");
2435
2542
  const remoteUrl = readMetadataString(job, "remoteUrl");
2436
2543
  const defaultBranch = readMetadataString(job, "defaultBranch");
@@ -2453,12 +2560,13 @@ async function processClaimedPendingFinalizeJobInner(params) {
2453
2560
  throw buildFinalizeCliError({
2454
2561
  message: "Finalize queue baseline drifted before this job was processed.",
2455
2562
  exitCode: 1,
2456
- hint: "Process queued finalize jobs in capture order, or re-anchor the repository before retrying.",
2563
+ hint: "Process queued finalize jobs in capture order, or run `remix collab init` to refresh the revision baseline before retrying.",
2457
2564
  disposition: "terminal",
2458
2565
  reason: "baseline_drifted"
2459
2566
  });
2460
2567
  }
2461
- if (appHead.headCommitHash !== job.baselineServerHeadHash) {
2568
+ const serverStillAtBaseline = job.baselineServerRevisionId ? appHead.headRevisionId === job.baselineServerRevisionId : appHead.headCommitHash === job.baselineServerHeadHash;
2569
+ if (!serverStillAtBaseline) {
2462
2570
  throw buildFinalizeCliError({
2463
2571
  message: "Server lane changed before a no-diff turn could be recorded.",
2464
2572
  exitCode: 2,
@@ -2480,6 +2588,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
2480
2588
  defaultBranch,
2481
2589
  baselineSnapshotId: job.baselineSnapshotId,
2482
2590
  currentSnapshotId: job.currentSnapshotId,
2591
+ baselineServerRevisionId: job.baselineServerRevisionId,
2483
2592
  baselineServerHeadHash: job.baselineServerHeadHash,
2484
2593
  currentSnapshotHash: snapshot.snapshotHash,
2485
2594
  localCommitHash: snapshot.localCommitHash,
@@ -2500,6 +2609,8 @@ async function processClaimedPendingFinalizeJobInner(params) {
2500
2609
  branchName: job.branchName,
2501
2610
  lastSnapshotId: snapshot.id,
2502
2611
  lastSnapshotHash: snapshot.snapshotHash,
2612
+ lastServerRevisionId: appHead.headRevisionId ?? null,
2613
+ lastServerTreeHash: appHead.treeHash ?? null,
2503
2614
  lastServerHeadHash: appHead.headCommitHash,
2504
2615
  lastSeenLocalCommitHash: snapshot.localCommitHash
2505
2616
  });
@@ -2520,14 +2631,14 @@ async function processClaimedPendingFinalizeJobInner(params) {
2520
2631
  };
2521
2632
  }
2522
2633
  const localBaselineAdvanced = baseline.lastSnapshotId !== job.baselineSnapshotId;
2523
- const serverHeadAdvanced = appHead.headCommitHash !== job.baselineServerHeadHash;
2634
+ const serverHeadAdvanced = job.baselineServerRevisionId ? appHead.headRevisionId !== job.baselineServerRevisionId : appHead.headCommitHash !== job.baselineServerHeadHash;
2524
2635
  if (baselineDrifted) {
2525
2636
  const consistentAdvance = localBaselineAdvanced && serverHeadAdvanced;
2526
2637
  if (!consistentAdvance) {
2527
2638
  throw buildFinalizeCliError({
2528
2639
  message: `Finalize queue baseline advanced inconsistently before this job was processed (localBaselineAdvanced=${localBaselineAdvanced}, serverHeadAdvanced=${serverHeadAdvanced}, jobBaselineSnapshotId=${job.baselineSnapshotId ?? "null"}, liveBaselineSnapshotId=${baseline.lastSnapshotId ?? "null"}, jobBaselineServerHeadHash=${job.baselineServerHeadHash ?? "null"}, liveBaselineServerHeadHash=${baseline.lastServerHeadHash ?? "null"}, currentAppHeadHash=${appHead.headCommitHash}). This indicates local Remix state diverged from the backend in a way that should not be reachable in normal operation; please report this as a bug.`,
2529
2640
  exitCode: 1,
2530
- hint: "Run `remix collab status` to inspect, then `remix collab re-anchor` only if the lane has no valid baseline.",
2641
+ hint: "Run `remix collab status` to inspect, then sync or reconcile before retrying.",
2531
2642
  disposition: "terminal",
2532
2643
  reason: "baseline_drifted"
2533
2644
  });
@@ -2535,6 +2646,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
2535
2646
  }
2536
2647
  let submissionDiff = diffResult.diff;
2537
2648
  let submissionBaseHeadHash = job.baselineServerHeadHash;
2649
+ let submissionBaseRevisionId = job.baselineServerRevisionId;
2538
2650
  let replayedFromBaseHash = null;
2539
2651
  if (!submissionBaseHeadHash) {
2540
2652
  throw buildFinalizeCliError({
@@ -2545,6 +2657,34 @@ async function processClaimedPendingFinalizeJobInner(params) {
2545
2657
  });
2546
2658
  }
2547
2659
  const replayNeeded = appHead.headCommitHash !== submissionBaseHeadHash || baselineDrifted;
2660
+ if (replayNeeded) {
2661
+ const existingChangeStep = await findExistingChangeStepByIdempotency({
2662
+ api: params.api,
2663
+ appId: job.currentAppId,
2664
+ idempotencyKey: job.idempotencyKey
2665
+ });
2666
+ if (existingChangeStep) {
2667
+ const changeStep2 = existingChangeStep.status === "succeeded" ? existingChangeStep : await pollChangeStep(params.api, job.currentAppId, existingChangeStep.id);
2668
+ invalidateAppHeadCache(job.currentAppId);
2669
+ invalidateAppDeltaCacheForApp(job.currentAppId);
2670
+ await writeBaselineFromSucceededChangeStep({ api: params.api, job, snapshot, changeStep: changeStep2 });
2671
+ await updatePendingFinalizeJob(job.id, {
2672
+ status: "completed",
2673
+ metadata: { changeStepId: String(changeStep2.id ?? "") }
2674
+ });
2675
+ return {
2676
+ mode: "changed_turn",
2677
+ idempotencyKey: job.idempotencyKey ?? "",
2678
+ queued: false,
2679
+ jobId: job.id,
2680
+ repoState,
2681
+ changeStep: changeStep2,
2682
+ collabTurn: null,
2683
+ autoSync: null,
2684
+ warnings: []
2685
+ };
2686
+ }
2687
+ }
2548
2688
  if (replayNeeded) {
2549
2689
  try {
2550
2690
  const replayResp = await params.api.startChangeStepReplay(job.currentAppId, {
@@ -2552,7 +2692,9 @@ async function processClaimedPendingFinalizeJobInner(params) {
2552
2692
  assistantResponse: job.assistantResponse,
2553
2693
  diff: diffResult.diff,
2554
2694
  baseCommitHash: submissionBaseHeadHash,
2695
+ baseRevisionId: job.baselineServerRevisionId,
2555
2696
  targetHeadCommitHash: appHead.headCommitHash,
2697
+ targetRevisionId: appHead.headRevisionId,
2556
2698
  expectedPaths: diffResult.changedPaths,
2557
2699
  actor,
2558
2700
  workspaceMetadata: buildWorkspaceMetadata({
@@ -2562,6 +2704,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
2562
2704
  defaultBranch,
2563
2705
  baselineSnapshotId: job.baselineSnapshotId,
2564
2706
  currentSnapshotId: job.currentSnapshotId,
2707
+ baselineServerRevisionId: job.baselineServerRevisionId,
2565
2708
  baselineServerHeadHash: job.baselineServerHeadHash,
2566
2709
  currentSnapshotHash: snapshot.snapshotHash,
2567
2710
  localCommitHash: snapshot.localCommitHash,
@@ -2587,6 +2730,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
2587
2730
  submissionDiff = replayDiff.diff;
2588
2731
  replayedFromBaseHash = submissionBaseHeadHash;
2589
2732
  submissionBaseHeadHash = appHead.headCommitHash;
2733
+ submissionBaseRevisionId = appHead.headRevisionId;
2590
2734
  } catch (error) {
2591
2735
  if (error instanceof RemixError && error.finalizeDisposition === void 0) {
2592
2736
  const detail = error.hint ? `${error.message} (${error.hint})` : error.message;
@@ -2608,6 +2752,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
2608
2752
  assistantResponse: job.assistantResponse,
2609
2753
  diff: submissionDiff,
2610
2754
  baseCommitHash: submissionBaseHeadHash,
2755
+ baseRevisionId: submissionBaseRevisionId,
2611
2756
  headCommitHash: submissionBaseHeadHash,
2612
2757
  changedFilesCount: diffResult.stats.changedFilesCount,
2613
2758
  insertions: diffResult.stats.insertions,
@@ -2620,6 +2765,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
2620
2765
  defaultBranch,
2621
2766
  baselineSnapshotId: job.baselineSnapshotId,
2622
2767
  currentSnapshotId: job.currentSnapshotId,
2768
+ baselineServerRevisionId: job.baselineServerRevisionId,
2623
2769
  baselineServerHeadHash: job.baselineServerHeadHash,
2624
2770
  currentSnapshotHash: snapshot.snapshotHash,
2625
2771
  localCommitHash: snapshot.localCommitHash,
@@ -2636,27 +2782,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
2636
2782
  const changeStep = await pollChangeStep(params.api, job.currentAppId, String(createdStep.id));
2637
2783
  invalidateAppHeadCache(job.currentAppId);
2638
2784
  invalidateAppDeltaCacheForApp(job.currentAppId);
2639
- const nextServerHeadHash = typeof changeStep.headCommitHash === "string" ? changeStep.headCommitHash.trim() : "";
2640
- if (!nextServerHeadHash) {
2641
- throw buildFinalizeCliError({
2642
- message: "Backend returned a succeeded change step without a head commit hash.",
2643
- exitCode: 1,
2644
- hint: "This is a backend invariant violation; retry will not help. Re-anchor and try again.",
2645
- disposition: "terminal",
2646
- reason: "missing_head_commit_hash"
2647
- });
2648
- }
2649
- await writeLocalBaseline({
2650
- repoRoot: job.repoRoot,
2651
- repoFingerprint: job.repoFingerprint,
2652
- laneId: job.laneId,
2653
- currentAppId: job.currentAppId,
2654
- branchName: job.branchName,
2655
- lastSnapshotId: snapshot.id,
2656
- lastSnapshotHash: snapshot.snapshotHash,
2657
- lastServerHeadHash: nextServerHeadHash,
2658
- lastSeenLocalCommitHash: snapshot.localCommitHash
2659
- });
2785
+ await writeBaselineFromSucceededChangeStep({ api: params.api, job, snapshot, changeStep });
2660
2786
  await updatePendingFinalizeJob(job.id, {
2661
2787
  status: "completed",
2662
2788
  metadata: { changeStepId: String(changeStep.id ?? "") }
@@ -2702,6 +2828,7 @@ async function enqueueCapturedFinalizeTurn(params) {
2702
2828
  prompt: params.prompt,
2703
2829
  assistantResponse: params.assistantResponse,
2704
2830
  baselineSnapshotId: params.baselineSnapshotId,
2831
+ baselineServerRevisionId: params.baselineServerRevisionId ?? null,
2705
2832
  baselineServerHeadHash: params.baselineServerHeadHash,
2706
2833
  currentSnapshotId: params.currentSnapshotId,
2707
2834
  idempotencyKey: params.idempotencyKey,
@@ -2802,17 +2929,6 @@ async function collabFinalizeTurn(params) {
2802
2929
  });
2803
2930
  }
2804
2931
  }
2805
- const pendingReAnchor = await findPendingAsyncJob({
2806
- repoRoot,
2807
- branchName: binding.branchName ?? null,
2808
- kind: "re_anchor"
2809
- });
2810
- if (pendingReAnchor) {
2811
- throw new RemixError("Cannot finalize a turn while a re-anchor is still processing.", {
2812
- exitCode: 2,
2813
- hint: `Re-anchor job ${pendingReAnchor.id} is still in the background queue. Run \`remix collab status\` to check progress.`
2814
- });
2815
- }
2816
2932
  const detected = await collabDetectRepoState({
2817
2933
  api: params.api,
2818
2934
  cwd: repoRoot,
@@ -2853,9 +2969,16 @@ async function collabFinalizeTurn(params) {
2853
2969
  hint: detected.hint
2854
2970
  });
2855
2971
  }
2972
+ if (detected.repoState === "both_changed") {
2973
+ throw new RemixError("Local and server changes must be reconciled before finalizing this turn.", {
2974
+ code: "reconcile_required",
2975
+ exitCode: 2,
2976
+ hint: detected.hint || "Run `remix collab reconcile --dry-run` to inspect recovery options before retrying."
2977
+ });
2978
+ }
2856
2979
  if (detected.repoState === "external_local_base_changed") {
2857
- throw new RemixError("The local checkout must be re-anchored before finalizing this turn.", {
2858
- code: "re_anchor_required",
2980
+ throw new RemixError("The local checkout is missing a Remix revision baseline for this lane.", {
2981
+ code: "baseline_missing",
2859
2982
  exitCode: 2,
2860
2983
  hint: detected.hint
2861
2984
  });
@@ -2867,8 +2990,9 @@ async function collabFinalizeTurn(params) {
2867
2990
  });
2868
2991
  if (!baseline) {
2869
2992
  throw new RemixError("Local Remix baseline is missing for this lane.", {
2993
+ code: "baseline_missing",
2870
2994
  exitCode: 2,
2871
- hint: "Run `remix collab re-anchor` to create a fresh baseline."
2995
+ hint: "Run `remix collab init` or sync this lane to create a fresh revision baseline."
2872
2996
  });
2873
2997
  }
2874
2998
  const snapshot = await captureLocalSnapshot({
@@ -2879,10 +3003,11 @@ async function collabFinalizeTurn(params) {
2879
3003
  });
2880
3004
  const mode = snapshot.snapshotHash === baseline.lastSnapshotHash ? "no_diff_turn" : "changed_turn";
2881
3005
  const idempotencyKey = params.idempotencyKey?.trim() || buildDeterministicIdempotencyKey({
2882
- kind: "collab_finalize_turn_boundary_v1",
3006
+ kind: "collab_finalize_turn_boundary_v2",
2883
3007
  appId: binding.currentAppId,
2884
3008
  laneId: binding.laneId,
2885
3009
  baselineSnapshotId: baseline.lastSnapshotId,
3010
+ baselineServerRevisionId: baseline.lastServerRevisionId,
2886
3011
  baselineServerHeadHash: baseline.lastServerHeadHash,
2887
3012
  currentSnapshotId: snapshot.id,
2888
3013
  currentSnapshotHash: snapshot.snapshotHash,
@@ -2902,6 +3027,7 @@ async function collabFinalizeTurn(params) {
2902
3027
  prompt,
2903
3028
  assistantResponse,
2904
3029
  baselineSnapshotId: baseline.lastSnapshotId,
3030
+ baselineServerRevisionId: baseline.lastServerRevisionId,
2905
3031
  baselineServerHeadHash: baseline.lastServerHeadHash,
2906
3032
  currentSnapshotId: snapshot.id,
2907
3033
  idempotencyKey,
@@ -2950,9 +3076,10 @@ var FINALIZE_PREFLIGHT_FAILURE_CODES = [
2950
3076
  // Server has commits we don't. Fix: `remix collab sync` (safe to
2951
3077
  // auto-run for fast-forward; non-FF refused by the command itself).
2952
3078
  "pull_required",
2953
- // Local base hash doesn't match the recorded baseline (force-push,
2954
- // hard reset, rebase). Fix: `remix collab re-anchor`.
2955
- "re_anchor_required"
3079
+ // Both local and server changed. Fix: inspect and apply reconcile.
3080
+ "reconcile_required",
3081
+ // Local revision baseline is missing. Fix: `remix collab init` or sync.
3082
+ "baseline_missing"
2956
3083
  ];
2957
3084
  var CODE_SET = new Set(FINALIZE_PREFLIGHT_FAILURE_CODES);
2958
3085
  function isFinalizePreflightFailureCode(value) {
@@ -3011,7 +3138,7 @@ async function collabRecordingPreflight(params) {
3011
3138
  if (detected.status === "branch_mismatch") return { status: "branch_mismatch", ...base };
3012
3139
  if (detected.repoState === "server_only_changed") return { status: "pull_required", ...base };
3013
3140
  if (detected.repoState === "both_changed") return { status: "reconcile_required", ...base };
3014
- if (detected.repoState === "external_local_base_changed") return { status: "re_anchor_required", ...base };
3141
+ if (detected.repoState === "external_local_base_changed") return { status: "baseline_missing", ...base };
3015
3142
  return { status: "ready", ...base };
3016
3143
  }
3017
3144
 
@@ -3238,7 +3365,7 @@ async function ensureWorkspaceMatchesBaseline(params) {
3238
3365
  if (!baseline?.lastSnapshotHash || !baseline.lastServerHeadHash) {
3239
3366
  throw new RemixError("Local Remix baseline is missing for this lane.", {
3240
3367
  exitCode: 2,
3241
- hint: "Run `remix collab re-anchor` to create a fresh baseline before applying server changes."
3368
+ hint: "Run `remix collab init` or sync from a checkout with a valid revision baseline before applying server changes."
3242
3369
  });
3243
3370
  }
3244
3371
  const inspection = await inspectLocalSnapshot({
@@ -3312,11 +3439,12 @@ async function collabSync(params) {
3312
3439
  const repoSnapshot = await captureRepoSnapshot(repoRoot, { includeWorkspaceDiffHash: true });
3313
3440
  const bootstrapFromLocalHead = !detected.baseline.lastSnapshotHash || !detected.baseline.lastServerHeadHash;
3314
3441
  let baselineServerHeadHash;
3442
+ let baselineServerRevisionId = null;
3315
3443
  if (bootstrapFromLocalHead) {
3316
3444
  if (!headCommitHash) {
3317
3445
  throw new RemixError("Failed to resolve local HEAD commit for the initial sync bootstrap.", {
3318
3446
  exitCode: 1,
3319
- hint: "Retry after Git HEAD is available, or run `remix collab re-anchor` if this checkout has no local Remix baseline yet."
3447
+ hint: "Retry after Git HEAD is available, or run `remix collab init` to seed this checkout's revision baseline."
3320
3448
  });
3321
3449
  }
3322
3450
  baselineServerHeadHash = headCommitHash;
@@ -3331,13 +3459,15 @@ async function collabSync(params) {
3331
3459
  if (!baseline.lastServerHeadHash) {
3332
3460
  throw new RemixError("Local Remix baseline is missing the last acknowledged server head.", {
3333
3461
  exitCode: 2,
3334
- hint: "Run `remix collab re-anchor` to create a fresh baseline before pulling server changes."
3462
+ hint: "Run `remix collab init` or sync from a checkout with a valid revision baseline before pulling server changes."
3335
3463
  });
3336
3464
  }
3337
3465
  baselineServerHeadHash = baseline.lastServerHeadHash;
3466
+ baselineServerRevisionId = baseline.lastServerRevisionId;
3338
3467
  }
3339
3468
  const deltaResp = await params.api.getAppDelta(binding.currentAppId, {
3340
3469
  baseHeadHash: baselineServerHeadHash,
3470
+ baseRevisionId: baselineServerRevisionId,
3341
3471
  repoFingerprint: binding.repoFingerprint ?? void 0,
3342
3472
  remoteUrl: binding.remoteUrl ?? void 0,
3343
3473
  defaultBranch: binding.defaultBranch ?? void 0
@@ -3361,13 +3491,54 @@ async function collabSync(params) {
3361
3491
  applied: false,
3362
3492
  dryRun: params.dryRun
3363
3493
  };
3364
- if (params.dryRun || delta.status === "up_to_date") {
3494
+ if (params.dryRun) {
3365
3495
  return previewResult;
3366
3496
  }
3497
+ if (delta.status === "up_to_date") {
3498
+ if (!bootstrapFromLocalHead) {
3499
+ return previewResult;
3500
+ }
3501
+ return withRepoMutationLock(
3502
+ {
3503
+ cwd: repoRoot,
3504
+ operation: "collabSync"
3505
+ },
3506
+ async ({ repoRoot: lockedRepoRoot, warnings }) => {
3507
+ await assertRepoSnapshotUnchanged(lockedRepoRoot, repoSnapshot, {
3508
+ operation: "`remix collab sync`",
3509
+ recoveryHint: "The repository changed before the first local Remix baseline could be created. Review the local changes and rerun `remix collab sync`."
3510
+ });
3511
+ const snapshot = await captureLocalSnapshot({
3512
+ repoRoot: lockedRepoRoot,
3513
+ repoFingerprint: binding.repoFingerprint,
3514
+ laneId: binding.laneId,
3515
+ branchName: binding.branchName
3516
+ });
3517
+ await writeLocalBaseline({
3518
+ repoRoot: lockedRepoRoot,
3519
+ repoFingerprint: binding.repoFingerprint,
3520
+ laneId: binding.laneId,
3521
+ currentAppId: binding.currentAppId,
3522
+ branchName: binding.branchName,
3523
+ lastSnapshotId: snapshot.id,
3524
+ lastSnapshotHash: snapshot.snapshotHash,
3525
+ lastServerRevisionId: delta.targetRevisionId ?? null,
3526
+ lastServerTreeHash: delta.targetTreeHash ?? null,
3527
+ lastServerHeadHash: delta.targetHeadHash,
3528
+ lastSeenLocalCommitHash: snapshot.localCommitHash
3529
+ });
3530
+ return {
3531
+ ...previewResult,
3532
+ localCommitHash: snapshot.localCommitHash,
3533
+ ...warnings.length > 0 ? { warnings } : {}
3534
+ };
3535
+ }
3536
+ );
3537
+ }
3367
3538
  if (delta.status === "base_unknown") {
3368
3539
  throw new RemixError("Direct pull is unavailable because Remix can no longer diff from the last acknowledged server head.", {
3369
3540
  exitCode: 2,
3370
- hint: "Run `remix collab reconcile --dry-run` to inspect recovery options before retrying. If this checkout has no local Remix baseline yet for this lane, `remix collab re-anchor` may be required."
3541
+ hint: "Run `remix collab reconcile --dry-run` to inspect recovery options before retrying. If this checkout has no local Remix baseline yet for this lane, run `remix collab init` to seed one."
3371
3542
  });
3372
3543
  }
3373
3544
  if (delta.status !== "delta_ready") {
@@ -3411,6 +3582,8 @@ async function collabSync(params) {
3411
3582
  branchName: binding.branchName,
3412
3583
  lastSnapshotId: snapshot.id,
3413
3584
  lastSnapshotHash: snapshot.snapshotHash,
3585
+ lastServerRevisionId: delta.targetRevisionId ?? null,
3586
+ lastServerTreeHash: delta.targetTreeHash ?? null,
3414
3587
  lastServerHeadHash: delta.targetHeadHash,
3415
3588
  lastSeenLocalCommitHash: snapshot.localCommitHash
3416
3589
  });
@@ -3705,6 +3878,8 @@ async function collabCheckout(params) {
3705
3878
  branchName: branchNameForBaseline,
3706
3879
  lastSnapshotId: snapshot.id,
3707
3880
  lastSnapshotHash: snapshot.snapshotHash,
3881
+ lastServerRevisionId: appHead.headRevisionId ?? null,
3882
+ lastServerTreeHash: appHead.treeHash ?? null,
3708
3883
  lastServerHeadHash: appHead.headCommitHash,
3709
3884
  lastSeenLocalCommitHash: snapshot.localCommitHash
3710
3885
  });
@@ -4090,25 +4265,21 @@ async function trySeedEquivalentBranchBaseline(params) {
4090
4265
  branchName: params.branchName,
4091
4266
  persistBlobs: false
4092
4267
  });
4093
- if (inspection.snapshotHash !== defaultBaseline.lastSnapshotHash) {
4268
+ if (inspection.snapshotHash !== defaultBaseline.lastSnapshotHash && inspection.localCommitHash !== defaultBaseline.lastSeenLocalCommitHash) {
4094
4269
  return null;
4095
4270
  }
4096
- const snapshot = await captureLocalSnapshot({
4097
- repoRoot: params.repoRoot,
4098
- repoFingerprint: params.repoFingerprint,
4099
- laneId: params.laneId,
4100
- branchName: params.branchName
4101
- });
4102
4271
  await writeLocalBaseline({
4103
4272
  repoRoot: params.repoRoot,
4104
4273
  repoFingerprint: params.repoFingerprint,
4105
4274
  laneId: params.laneId,
4106
4275
  currentAppId: params.currentAppId,
4107
4276
  branchName: params.branchName,
4108
- lastSnapshotId: snapshot.id,
4109
- lastSnapshotHash: snapshot.snapshotHash,
4277
+ lastSnapshotId: defaultBaseline.lastSnapshotId,
4278
+ lastSnapshotHash: defaultBaseline.lastSnapshotHash,
4279
+ lastServerRevisionId: params.appHeadRevisionId,
4280
+ lastServerTreeHash: params.appTreeHash,
4110
4281
  lastServerHeadHash: params.appHeadHash,
4111
- lastSeenLocalCommitHash: snapshot.localCommitHash
4282
+ lastSeenLocalCommitHash: inspection.localCommitHash
4112
4283
  });
4113
4284
  return "seeded";
4114
4285
  }
@@ -4118,11 +4289,11 @@ async function resolveInitBaselineStatus(params) {
4118
4289
  laneId: params.laneId,
4119
4290
  repoRoot: params.repoRoot
4120
4291
  });
4121
- if (baseline?.lastSnapshotHash && baseline.lastServerHeadHash) {
4292
+ if (baseline?.lastSnapshotHash && (baseline.lastServerRevisionId || baseline.lastServerHeadHash)) {
4122
4293
  return "existing";
4123
4294
  }
4124
4295
  const localHeadCommitHash = await getHeadCommitHash(params.repoRoot);
4125
- if (!localHeadCommitHash) return "requires_re_anchor";
4296
+ if (!localHeadCommitHash) return "baseline_missing";
4126
4297
  const appHead = unwrapResponseObject(
4127
4298
  await params.api.getAppHead(params.currentAppId),
4128
4299
  "app head"
@@ -4149,6 +4320,7 @@ async function resolveInitBaselineStatus(params) {
4149
4320
  const deltaResp = await params.api.getAppDelta(params.currentAppId, {
4150
4321
  baseHeadHash: localHeadCommitHash,
4151
4322
  targetHeadHash: appHead.headCommitHash,
4323
+ targetRevisionId: appHead.headRevisionId,
4152
4324
  localSnapshotHash,
4153
4325
  repoFingerprint: params.repoFingerprint,
4154
4326
  remoteUrl: params.remoteUrl ?? void 0,
@@ -4156,6 +4328,21 @@ async function resolveInitBaselineStatus(params) {
4156
4328
  });
4157
4329
  const delta = unwrapResponseObject(deltaResp, "app delta");
4158
4330
  if (delta.status === "up_to_date" || delta.status === "delta_ready") {
4331
+ const equivalentBaseline = await trySeedEquivalentBranchBaseline({
4332
+ repoRoot: params.repoRoot,
4333
+ repoFingerprint: params.repoFingerprint,
4334
+ laneId: params.laneId,
4335
+ currentAppId: params.currentAppId,
4336
+ upstreamAppId: params.upstreamAppId ?? null,
4337
+ branchName: params.branchName,
4338
+ defaultBranch: params.defaultBranch,
4339
+ appHeadHash: appHead.headCommitHash,
4340
+ appHeadRevisionId: appHead.headRevisionId ?? null,
4341
+ appTreeHash: appHead.treeHash ?? null
4342
+ });
4343
+ if (equivalentBaseline) {
4344
+ return equivalentBaseline;
4345
+ }
4159
4346
  return "requires_sync";
4160
4347
  }
4161
4348
  if (delta.status === "content_equivalent") {
@@ -4178,7 +4365,9 @@ async function resolveInitBaselineStatus(params) {
4178
4365
  upstreamAppId: params.upstreamAppId ?? null,
4179
4366
  branchName: params.branchName,
4180
4367
  defaultBranch: params.defaultBranch,
4181
- appHeadHash: appHead.headCommitHash
4368
+ appHeadHash: appHead.headCommitHash,
4369
+ appHeadRevisionId: appHead.headRevisionId ?? null,
4370
+ appTreeHash: appHead.treeHash ?? null
4182
4371
  });
4183
4372
  if (equivalentBaseline) {
4184
4373
  return equivalentBaseline;
@@ -4186,7 +4375,7 @@ async function resolveInitBaselineStatus(params) {
4186
4375
  }
4187
4376
  } catch {
4188
4377
  }
4189
- return "requires_re_anchor";
4378
+ return "baseline_missing";
4190
4379
  }
4191
4380
  async function seedImportedInitBaseline(params) {
4192
4381
  const appHead = unwrapResponseObject(
@@ -4207,6 +4396,8 @@ async function seedImportedInitBaseline(params) {
4207
4396
  branchName: params.branchName,
4208
4397
  lastSnapshotId: snapshot.id,
4209
4398
  lastSnapshotHash: snapshot.snapshotHash,
4399
+ lastServerRevisionId: appHead.headRevisionId ?? null,
4400
+ lastServerTreeHash: appHead.treeHash ?? null,
4210
4401
  lastServerHeadHash: appHead.headCommitHash,
4211
4402
  lastSeenLocalCommitHash: snapshot.localCommitHash
4212
4403
  });
@@ -4220,7 +4411,6 @@ async function collabInit(params) {
4220
4411
  },
4221
4412
  async ({ repoRoot, warnings }) => {
4222
4413
  await ensureGitInfoExcludeEntries(repoRoot, [".remix/"]);
4223
- await ensureCleanWorktree(repoRoot, "`remix collab init`");
4224
4414
  if (params.path?.trim()) {
4225
4415
  throw new RemixError("`remix collab init --path` is not supported.", {
4226
4416
  exitCode: 2,
@@ -4228,6 +4418,10 @@ async function collabInit(params) {
4228
4418
  });
4229
4419
  }
4230
4420
  const localBindingState = await readCollabBindingState(repoRoot, { persist: true });
4421
+ const hasExistingBinding = localBindingState != null && Object.keys(localBindingState.branchBindings ?? {}).length > 0;
4422
+ if (params.forceNew || !hasExistingBinding) {
4423
+ await ensureCleanWorktree(repoRoot, "`remix collab init`");
4424
+ }
4231
4425
  const persistedRemoteUrl = normalizeGitRemote(localBindingState?.remoteUrl ?? null);
4232
4426
  const currentBranch = await getCurrentBranch(repoRoot);
4233
4427
  const defaultBranch = localBindingState?.defaultBranch ?? await getDefaultBranch(repoRoot) ?? currentBranch;
@@ -5081,10 +5275,11 @@ async function collabList(params) {
5081
5275
  };
5082
5276
  }
5083
5277
 
5084
- // src/application/collab/collabReAnchor.ts
5085
- import { randomUUID as randomUUID5 } from "crypto";
5278
+ // src/application/collab/collabReconcile.ts
5086
5279
  import fs11 from "fs/promises";
5280
+ import os5 from "os";
5087
5281
  import path10 from "path";
5282
+ import { execa as execa3 } from "execa";
5088
5283
 
5089
5284
  // src/application/collab/pendingFinalize.ts
5090
5285
  function hasPendingFinalize(summary) {
@@ -5094,258 +5289,7 @@ function buildPendingFinalizeHint() {
5094
5289
  return "Drain or await the local finalize queue first, then retry after the queued Remix turn finishes recording remotely.";
5095
5290
  }
5096
5291
 
5097
- // src/application/collab/collabReAnchor.ts
5098
- async function collabReAnchor(params) {
5099
- const repoRoot = await findGitRoot(params.cwd);
5100
- const binding = await ensureActiveLaneBinding({
5101
- repoRoot,
5102
- api: params.api,
5103
- operation: "`remix collab re-anchor`"
5104
- });
5105
- if (!binding) {
5106
- throw new RemixError("Repository is not bound to Remix.", {
5107
- exitCode: 2,
5108
- hint: "Run `remix collab init` first."
5109
- });
5110
- }
5111
- const detected = await collabDetectRepoState({
5112
- api: params.api,
5113
- cwd: repoRoot,
5114
- allowBranchMismatch: params.allowBranchMismatch
5115
- });
5116
- if (detected.status === "metadata_conflict" || detected.status === "branch_mismatch") {
5117
- throw new RemixError("Repository must be realigned before seeding a fresh local Remix baseline.", {
5118
- exitCode: 2,
5119
- hint: detected.hint
5120
- });
5121
- }
5122
- if (detected.status !== "ready" || !detected.binding) {
5123
- throw new RemixError(detected.hint || "Repository is not ready for re-anchor.", {
5124
- exitCode: 2,
5125
- hint: detected.hint
5126
- });
5127
- }
5128
- if (detected.repoState === "server_only_changed") {
5129
- throw new RemixError("This checkout is already on a server-known base and only needs a local pull.", {
5130
- exitCode: 2,
5131
- hint: "Run `remix collab sync` instead of `remix collab re-anchor`."
5132
- });
5133
- }
5134
- if (detected.repoState === "both_changed") {
5135
- throw new RemixError("Both the local workspace and the server lane changed since the last agreed baseline.", {
5136
- exitCode: 2,
5137
- hint: "Run `remix collab reconcile` to replay the local boundary onto the newer server head."
5138
- });
5139
- }
5140
- if (detected.repoState === "local_only_changed") {
5141
- if (hasPendingFinalize(detected.pendingFinalize)) {
5142
- throw new RemixError("Re-anchor is not needed while queued Remix turn recording is still processing.", {
5143
- exitCode: 2,
5144
- hint: buildPendingFinalizeHint()
5145
- });
5146
- }
5147
- throw new RemixError("Re-anchor is not the right command for local content changes.", {
5148
- exitCode: 2,
5149
- hint: "Remix is source-blind: any local content change since the last recorded turn \u2014 including manual commits, pulls, merges, and rebases \u2014 is recorded with `remix collab finalize-turn`. Use `remix collab re-anchor` only when no local Remix baseline exists yet for this lane (status reports `re_anchor`)."
5150
- });
5151
- }
5152
- if (detected.repoState === "idle") {
5153
- throw new RemixError("This checkout is already aligned with Remix.", {
5154
- exitCode: 2,
5155
- hint: "No re-anchor step is needed. Re-anchor only applies when no local Remix baseline exists yet for this lane."
5156
- });
5157
- }
5158
- await ensureCleanWorktree(repoRoot, "`remix collab re-anchor`");
5159
- const branch = await requireCurrentBranch(repoRoot);
5160
- const headCommitHash = await getHeadCommitHash(repoRoot);
5161
- if (!headCommitHash) {
5162
- throw new RemixError("Failed to resolve local HEAD commit.", { exitCode: 1 });
5163
- }
5164
- if (params.asyncSubmit && !params.dryRun) {
5165
- const pending = await findPendingAsyncJob({
5166
- repoRoot,
5167
- branchName: binding.branchName ?? branch,
5168
- kind: "re_anchor"
5169
- });
5170
- if (pending) {
5171
- return {
5172
- status: "queued",
5173
- queued: true,
5174
- jobId: pending.id,
5175
- repoRoot,
5176
- branch,
5177
- currentAppId: binding.currentAppId,
5178
- dryRun: false,
5179
- applied: false
5180
- };
5181
- }
5182
- }
5183
- const preflightResp = await params.api.preflightAppReconcile(binding.currentAppId, {
5184
- localHeadCommitHash: headCommitHash,
5185
- repoFingerprint: binding.repoFingerprint ?? void 0,
5186
- remoteUrl: binding.remoteUrl ?? void 0,
5187
- defaultBranch: binding.defaultBranch ?? void 0
5188
- });
5189
- const preflight = unwrapResponseObject(preflightResp, "reconcile preflight");
5190
- if (preflight.status === "metadata_conflict") {
5191
- throw new RemixError("Local repository metadata conflicts with the bound Remix app.", {
5192
- exitCode: 2,
5193
- hint: preflight.warnings.join("\n") || "Run the command from the correct bound repository."
5194
- });
5195
- }
5196
- const preview = {
5197
- status: preflight.status === "up_to_date" ? "reanchored" : "re_anchor_required",
5198
- repoRoot,
5199
- branch,
5200
- currentAppId: binding.currentAppId,
5201
- localHeadCommitHash: headCommitHash,
5202
- targetHeadCommitHash: preflight.targetHeadCommitHash,
5203
- targetHeadCommitId: preflight.targetHeadCommitId,
5204
- warnings: preflight.warnings,
5205
- applied: false,
5206
- dryRun: params.dryRun === true
5207
- };
5208
- if (params.dryRun) {
5209
- return preview;
5210
- }
5211
- let anchoredServerHeadHash = preflight.targetHeadCommitHash;
5212
- if (params.asyncSubmit && preflight.status === "ready_to_reconcile") {
5213
- const failed = await findFailedAsyncJob({
5214
- repoRoot,
5215
- branchName: binding.branchName ?? branch,
5216
- kind: "re_anchor"
5217
- });
5218
- if (failed) {
5219
- await deleteAsyncJob(failed.id);
5220
- }
5221
- const { bundlePath: tmpBundlePath, headCommitHash: bundledHeadCommitHash } = await createGitBundle(
5222
- repoRoot,
5223
- "re-anchor.bundle"
5224
- );
5225
- const tmpBundleDir = path10.dirname(tmpBundlePath);
5226
- try {
5227
- const jobId = randomUUID5();
5228
- const durableBundlePath = getAsyncJobBundlePath(jobId);
5229
- await fs11.mkdir(getAsyncJobDir(jobId), { recursive: true });
5230
- try {
5231
- await fs11.rename(tmpBundlePath, durableBundlePath);
5232
- } catch (error) {
5233
- if (error?.code !== "EXDEV") throw error;
5234
- await fs11.copyFile(tmpBundlePath, durableBundlePath);
5235
- await fs11.unlink(tmpBundlePath).catch(() => void 0);
5236
- }
5237
- const bundleSha = await sha256FileHex(durableBundlePath);
5238
- const job = await enqueueAsyncJob({
5239
- id: jobId,
5240
- kind: "re_anchor",
5241
- status: "queued",
5242
- repoRoot,
5243
- repoFingerprint: binding.repoFingerprint,
5244
- branchName: binding.branchName ?? branch,
5245
- laneId: binding.laneId,
5246
- retryCount: 0,
5247
- error: null,
5248
- idempotencyKey: null,
5249
- payload: {
5250
- bundlePath: durableBundlePath,
5251
- bundleSha256: bundleSha,
5252
- localHeadCommitHash: bundledHeadCommitHash,
5253
- targetHeadCommitHash: preflight.targetHeadCommitHash,
5254
- appId: binding.currentAppId
5255
- }
5256
- });
5257
- await logDrainerEvent(job.id, "submitted", { kind: "re_anchor" });
5258
- return {
5259
- status: "queued",
5260
- queued: true,
5261
- jobId: job.id,
5262
- repoRoot,
5263
- branch,
5264
- currentAppId: binding.currentAppId,
5265
- localHeadCommitHash: bundledHeadCommitHash,
5266
- targetHeadCommitHash: preflight.targetHeadCommitHash,
5267
- warnings: preflight.warnings,
5268
- dryRun: false,
5269
- applied: false
5270
- };
5271
- } finally {
5272
- await fs11.rm(tmpBundleDir, { recursive: true, force: true }).catch(() => void 0);
5273
- }
5274
- }
5275
- if (preflight.status === "ready_to_reconcile") {
5276
- const { bundlePath, headCommitHash: bundledHeadCommitHash } = await createGitBundle(repoRoot, "re-anchor.bundle");
5277
- const bundleTempDir = path10.dirname(bundlePath);
5278
- try {
5279
- const bundleStat = await fs11.stat(bundlePath);
5280
- const checksumSha256 = await sha256FileHex(bundlePath);
5281
- const presignResp = await params.api.presignImportUploadFirstParty({
5282
- file: {
5283
- name: path10.basename(bundlePath),
5284
- mimeType: "application/x-git-bundle",
5285
- size: bundleStat.size,
5286
- checksumSha256
5287
- }
5288
- });
5289
- const uploadTarget = unwrapResponseObject(presignResp, "import upload target");
5290
- await uploadPresigned({
5291
- uploadUrl: String(uploadTarget.uploadUrl),
5292
- filePath: bundlePath,
5293
- headers: uploadTarget.headers ?? {}
5294
- });
5295
- const startResp = await params.api.startAppReconcile(binding.currentAppId, {
5296
- uploadId: String(uploadTarget.uploadId),
5297
- localHeadCommitHash: bundledHeadCommitHash,
5298
- repoFingerprint: binding.repoFingerprint ?? void 0,
5299
- remoteUrl: binding.remoteUrl ?? void 0,
5300
- defaultBranch: binding.defaultBranch ?? void 0,
5301
- idempotencyKey: buildDeterministicIdempotencyKey({
5302
- kind: "collab_re_anchor_v1",
5303
- appId: binding.currentAppId,
5304
- localHeadCommitHash: bundledHeadCommitHash,
5305
- targetHeadCommitHash: preflight.targetHeadCommitHash
5306
- })
5307
- });
5308
- const started = unwrapResponseObject(startResp, "reconcile");
5309
- const reconcile = await pollReconcile(params.api, binding.currentAppId, started.id);
5310
- anchoredServerHeadHash = reconcile.reconciledHeadCommitHash ?? reconcile.targetHeadCommitHash ?? preflight.targetHeadCommitHash;
5311
- } finally {
5312
- await fs11.rm(bundleTempDir, { recursive: true, force: true });
5313
- }
5314
- }
5315
- const snapshot = await captureLocalSnapshot({
5316
- repoRoot,
5317
- repoFingerprint: binding.repoFingerprint,
5318
- laneId: binding.laneId,
5319
- branchName: binding.branchName
5320
- });
5321
- await writeLocalBaseline({
5322
- repoRoot,
5323
- repoFingerprint: binding.repoFingerprint,
5324
- laneId: binding.laneId,
5325
- currentAppId: binding.currentAppId,
5326
- branchName: binding.branchName,
5327
- lastSnapshotId: snapshot.id,
5328
- lastSnapshotHash: snapshot.snapshotHash,
5329
- lastServerHeadHash: anchoredServerHeadHash,
5330
- lastSeenLocalCommitHash: snapshot.localCommitHash
5331
- });
5332
- return {
5333
- ...preview,
5334
- status: "reanchored",
5335
- targetHeadCommitHash: anchoredServerHeadHash,
5336
- applied: true,
5337
- dryRun: false
5338
- };
5339
- }
5340
- async function collabReAnchorSubmit(params) {
5341
- return collabReAnchor({ ...params, asyncSubmit: true });
5342
- }
5343
-
5344
5292
  // src/application/collab/collabReconcile.ts
5345
- import fs12 from "fs/promises";
5346
- import os5 from "os";
5347
- import path11 from "path";
5348
- import { execa as execa3 } from "execa";
5349
5293
  async function reconcileBothChanged(params) {
5350
5294
  const repoRoot = await findGitRoot(params.cwd);
5351
5295
  const binding = await ensureActiveLaneBinding({
@@ -5368,7 +5312,7 @@ async function reconcileBothChanged(params) {
5368
5312
  if (!baseline?.lastSnapshotId || !baseline.lastServerHeadHash) {
5369
5313
  throw new RemixError("Local Remix baseline is missing for this lane.", {
5370
5314
  exitCode: 2,
5371
- hint: "Run `remix collab re-anchor` to create a fresh baseline first."
5315
+ hint: "Run `remix collab init` or sync from a checkout with a valid revision baseline first."
5372
5316
  });
5373
5317
  }
5374
5318
  const currentSnapshot = await captureLocalSnapshot({
@@ -5391,6 +5335,7 @@ async function reconcileBothChanged(params) {
5391
5335
  params.api.getAppHead(binding.currentAppId),
5392
5336
  params.api.getAppDelta(binding.currentAppId, {
5393
5337
  baseHeadHash: baseline.lastServerHeadHash,
5338
+ baseRevisionId: baseline.lastServerRevisionId,
5394
5339
  repoFingerprint: binding.repoFingerprint ?? void 0,
5395
5340
  remoteUrl: binding.remoteUrl ?? void 0,
5396
5341
  defaultBranch: binding.defaultBranch ?? void 0
@@ -5408,7 +5353,7 @@ async function reconcileBothChanged(params) {
5408
5353
  if (delta.status === "base_unknown") {
5409
5354
  throw new RemixError("Reconcile cannot pull the newer server state from the last acknowledged baseline.", {
5410
5355
  exitCode: 2,
5411
- hint: "Run `remix collab re-anchor` to re-anchor this checkout before retrying."
5356
+ hint: "Run `remix collab init` to seed a fresh revision baseline for this checkout before retrying."
5412
5357
  });
5413
5358
  }
5414
5359
  if (delta.status !== "delta_ready" && delta.status !== "up_to_date") {
@@ -5439,7 +5384,9 @@ async function reconcileBothChanged(params) {
5439
5384
  assistantResponse: "Replay the local boundary delta onto the latest server head without recording a new change step.",
5440
5385
  diff: diffResult.diff,
5441
5386
  baseCommitHash: baseline.lastServerHeadHash,
5387
+ baseRevisionId: baseline.lastServerRevisionId,
5442
5388
  targetHeadCommitHash: appHead.headCommitHash,
5389
+ targetRevisionId: appHead.headRevisionId,
5443
5390
  expectedPaths: diffResult.changedPaths,
5444
5391
  workspaceMetadata: {
5445
5392
  recordingMode: "boundary_delta",
@@ -5447,6 +5394,7 @@ async function reconcileBothChanged(params) {
5447
5394
  branch,
5448
5395
  baselineSnapshotId: baseline.lastSnapshotId,
5449
5396
  currentSnapshotId: currentSnapshot.id,
5397
+ baselineServerRevisionId: baseline.lastServerRevisionId,
5450
5398
  baselineServerHeadHash: baseline.lastServerHeadHash,
5451
5399
  currentSnapshotHash: currentSnapshot.snapshotHash,
5452
5400
  localCommitHash: currentSnapshot.localCommitHash,
@@ -5465,12 +5413,12 @@ async function reconcileBothChanged(params) {
5465
5413
  const replay = await pollChangeStepReplay(params.api, binding.currentAppId, String(replayStart.id));
5466
5414
  const replayDiffResp = await params.api.getChangeStepReplayDiff(binding.currentAppId, replay.id);
5467
5415
  const replayDiff = unwrapResponseObject(replayDiffResp, "change step replay diff");
5468
- const tempRoot = await fs12.mkdtemp(path11.join(os5.tmpdir(), "remix-reconcile-"));
5416
+ const tempRoot = await fs11.mkdtemp(path10.join(os5.tmpdir(), "remix-reconcile-"));
5469
5417
  let serverHeadSnapshot = null;
5470
5418
  let mergedSnapshot = null;
5471
5419
  try {
5472
- const tempRepoRoot = path11.join(tempRoot, "repo");
5473
- await fs12.mkdir(tempRepoRoot, { recursive: true });
5420
+ const tempRepoRoot = path10.join(tempRoot, "repo");
5421
+ await fs11.mkdir(tempRepoRoot, { recursive: true });
5474
5422
  await execa3("git", ["init"], { cwd: tempRepoRoot, stderr: "ignore" });
5475
5423
  await materializeLocalSnapshot(baseline.lastSnapshotId, tempRepoRoot);
5476
5424
  if (delta.status === "delta_ready" && delta.diff.trim()) {
@@ -5492,7 +5440,7 @@ async function reconcileBothChanged(params) {
5492
5440
  branchName: binding.branchName
5493
5441
  });
5494
5442
  } finally {
5495
- await fs12.rm(tempRoot, { recursive: true, force: true }).catch(() => void 0);
5443
+ await fs11.rm(tempRoot, { recursive: true, force: true }).catch(() => void 0);
5496
5444
  }
5497
5445
  if (!serverHeadSnapshot || !mergedSnapshot) {
5498
5446
  throw new RemixError("Failed to materialize the reconciled local workspace.", { exitCode: 1 });
@@ -5529,6 +5477,8 @@ async function reconcileBothChanged(params) {
5529
5477
  branchName: binding.branchName,
5530
5478
  lastSnapshotId: serverHeadSnapshot.id,
5531
5479
  lastSnapshotHash: serverHeadSnapshot.snapshotHash,
5480
+ lastServerRevisionId: appHead.headRevisionId ?? null,
5481
+ lastServerTreeHash: appHead.treeHash ?? null,
5532
5482
  lastServerHeadHash: appHead.headCommitHash,
5533
5483
  lastSeenLocalCommitHash: restoredSnapshot.localCommitHash
5534
5484
  });
@@ -5564,7 +5514,10 @@ async function collabReconcile(params) {
5564
5514
  return reconcileBothChanged(params);
5565
5515
  }
5566
5516
  if (detected.repoState === "external_local_base_changed") {
5567
- return collabReAnchor(params);
5517
+ throw new RemixError("This checkout needs a local Remix revision baseline before reconciliation.", {
5518
+ exitCode: 2,
5519
+ hint: detected.hint || "Run `remix collab init` or `remix collab sync` to seed the baseline."
5520
+ });
5568
5521
  }
5569
5522
  if (detected.repoState === "local_only_changed") {
5570
5523
  if (hasPendingFinalize(detected.pendingFinalize)) {
@@ -5675,6 +5628,8 @@ async function collabRemix(params) {
5675
5628
  branchName: branchNameForBaseline,
5676
5629
  lastSnapshotId: snapshot.id,
5677
5630
  lastSnapshotHash: snapshot.snapshotHash,
5631
+ lastServerRevisionId: appHead.headRevisionId ?? null,
5632
+ lastServerTreeHash: appHead.treeHash ?? null,
5678
5633
  lastServerHeadHash: appHead.headCommitHash,
5679
5634
  lastSeenLocalCommitHash: snapshot.localCommitHash
5680
5635
  });
@@ -5799,11 +5754,15 @@ function createBaseStatus() {
5799
5754
  baseline: {
5800
5755
  lastSnapshotId: null,
5801
5756
  lastSnapshotHash: null,
5757
+ lastServerRevisionId: null,
5758
+ lastServerTreeHash: null,
5802
5759
  lastServerHeadHash: null,
5803
5760
  lastSeenLocalCommitHash: null
5804
5761
  },
5805
5762
  current: {
5806
5763
  snapshotHash: null,
5764
+ serverRevisionId: null,
5765
+ serverTreeHash: null,
5807
5766
  serverHeadHash: null,
5808
5767
  serverHeadCommitId: null,
5809
5768
  localCommitHash: null
@@ -5890,6 +5849,8 @@ async function collabStatus(params) {
5890
5849
  status.alignment.baseline = detected.baseline;
5891
5850
  status.alignment.current = {
5892
5851
  snapshotHash: detected.currentSnapshotHash,
5852
+ serverRevisionId: detected.currentServerRevisionId,
5853
+ serverTreeHash: detected.currentServerTreeHash,
5893
5854
  serverHeadHash: detected.currentServerHeadHash,
5894
5855
  serverHeadCommitId: detected.currentServerHeadCommitId,
5895
5856
  localCommitHash: detected.localCommitHash
@@ -5953,7 +5914,7 @@ async function collabStatus(params) {
5953
5914
  status.reconcile.canApply = !status.repo.branchMismatch;
5954
5915
  status.recommendedAction = "reconcile";
5955
5916
  } else if (detected.repoState === "external_local_base_changed") {
5956
- status.recommendedAction = "re_anchor";
5917
+ status.recommendedAction = "init";
5957
5918
  addBlockedReason(status.sync, "baseline_missing");
5958
5919
  addBlockedReason(status.reconcile, "baseline_missing");
5959
5920
  } else if (detected.repoState === "local_only_changed") {
@@ -6117,8 +6078,8 @@ async function collabView(params) {
6117
6078
  }
6118
6079
 
6119
6080
  // src/application/collab/collabAsyncProcessing.ts
6120
- import fs13 from "fs/promises";
6121
- import path12 from "path";
6081
+ import fs12 from "fs/promises";
6082
+ import path11 from "path";
6122
6083
  var MAX_TRANSIENT_RETRIES = 5;
6123
6084
  var TRANSIENT_NETWORK_CODES = /* @__PURE__ */ new Set([
6124
6085
  "ECONNREFUSED",
@@ -6214,10 +6175,10 @@ async function processInitJob(job, api) {
6214
6175
  try {
6215
6176
  await updateAsyncJob(job.id, { status: "submitting", error: null });
6216
6177
  await logDrainerEvent(job.id, "claimed", { kind: "init" });
6217
- const bundleStat = await fs13.stat(job.payload.bundlePath);
6178
+ const bundleStat = await fs12.stat(job.payload.bundlePath);
6218
6179
  const presignResp = await api.presignImportUploadFirstParty({
6219
6180
  file: {
6220
- name: path12.basename(job.payload.bundlePath),
6181
+ name: path11.basename(job.payload.bundlePath),
6221
6182
  mimeType: "application/x-git-bundle",
6222
6183
  size: bundleStat.size,
6223
6184
  checksumSha256: job.payload.bundleSha256
@@ -6234,7 +6195,7 @@ async function processInitJob(job, api) {
6234
6195
  await updateAsyncJob(job.id, { status: "server_processing" });
6235
6196
  const importResp = await api.importFromUploadFirstParty({
6236
6197
  uploadId: String(presign.uploadId),
6237
- appName: job.payload.appName?.trim() || path12.basename(job.repoRoot),
6198
+ appName: job.payload.appName?.trim() || path11.basename(job.repoRoot),
6238
6199
  platform: "generic",
6239
6200
  isPublic: false,
6240
6201
  branch: job.payload.defaultBranch && job.branchName && job.branchName !== job.payload.defaultBranch ? job.payload.defaultBranch : job.branchName ?? void 0,
@@ -6428,7 +6389,7 @@ async function processInitPostJob(job, api) {
6428
6389
  if (outcome.status === "failed") {
6429
6390
  const bindingPath = getCollabBindingPath(job.repoRoot);
6430
6391
  try {
6431
- await fs13.unlink(bindingPath);
6392
+ await fs12.unlink(bindingPath);
6432
6393
  await logDrainerEvent(job.id, "binding_cleared", {
6433
6394
  kind: "init_post",
6434
6395
  appId: job.payload.appId,
@@ -6469,10 +6430,10 @@ async function processReAnchorJob(job, api) {
6469
6430
  }
6470
6431
  let anchoredServerHeadHash = preflight.targetHeadCommitHash;
6471
6432
  if (preflight.status === "ready_to_reconcile") {
6472
- const bundleStat = await fs13.stat(job.payload.bundlePath);
6433
+ const bundleStat = await fs12.stat(job.payload.bundlePath);
6473
6434
  const presignResp = await api.presignImportUploadFirstParty({
6474
6435
  file: {
6475
- name: path12.basename(job.payload.bundlePath),
6436
+ name: path11.basename(job.payload.bundlePath),
6476
6437
  mimeType: "application/x-git-bundle",
6477
6438
  size: bundleStat.size,
6478
6439
  checksumSha256: job.payload.bundleSha256
@@ -6576,9 +6537,9 @@ async function collabReAnchorProcess(jobId, opts) {
6576
6537
  }
6577
6538
  async function acquireDrainerPidLock() {
6578
6539
  const pidPath = getDrainerPidPath();
6579
- await fs13.mkdir(path12.dirname(pidPath), { recursive: true });
6540
+ await fs12.mkdir(path11.dirname(pidPath), { recursive: true });
6580
6541
  try {
6581
- const existing = await fs13.readFile(pidPath, "utf8").catch(() => "");
6542
+ const existing = await fs12.readFile(pidPath, "utf8").catch(() => "");
6582
6543
  const existingPid = parseInt(existing.trim(), 10);
6583
6544
  if (Number.isFinite(existingPid) && existingPid > 0 && existingPid !== process.pid) {
6584
6545
  try {
@@ -6588,13 +6549,13 @@ async function acquireDrainerPidLock() {
6588
6549
  if (error?.code !== "ESRCH") return null;
6589
6550
  }
6590
6551
  }
6591
- await fs13.writeFile(pidPath, String(process.pid), "utf8");
6552
+ await fs12.writeFile(pidPath, String(process.pid), "utf8");
6592
6553
  return {
6593
6554
  release: async () => {
6594
6555
  try {
6595
- const current = (await fs13.readFile(pidPath, "utf8")).trim();
6556
+ const current = (await fs12.readFile(pidPath, "utf8")).trim();
6596
6557
  if (current === String(process.pid)) {
6597
- await fs13.unlink(pidPath).catch(() => void 0);
6558
+ await fs12.unlink(pidPath).catch(() => void 0);
6598
6559
  }
6599
6560
  } catch {
6600
6561
  }
@@ -6645,9 +6606,6 @@ export {
6645
6606
  collabList,
6646
6607
  collabListMembers,
6647
6608
  collabListMergeRequests,
6648
- collabReAnchor,
6649
- collabReAnchorProcess,
6650
- collabReAnchorSubmit,
6651
6609
  collabReconcile,
6652
6610
  collabRecordingPreflight,
6653
6611
  collabReject,
@@ -6675,6 +6633,7 @@ export {
6675
6633
  processPendingFinalizeJob,
6676
6634
  pruneTerminalAsyncJobs,
6677
6635
  readAsyncJob,
6636
+ readLocalSnapshot,
6678
6637
  readPendingFinalizeJob,
6679
6638
  requeuePendingFinalizeJob,
6680
6639
  summarizeAsyncJobs,