@ricsam/r5d-worker 0.0.61 → 0.0.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/main.cjs +22 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-convergence.cjs +26 -2
- package/dist/cjs/workspace-sync.cjs +175 -18
- package/dist/mjs/main.mjs +23 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-convergence.mjs +25 -2
- package/dist/mjs/workspace-sync.mjs +175 -18
- package/dist/types/workspace-convergence.d.ts +12 -1
- package/dist/types/workspace-sync.d.ts +9 -0
- package/package.json +1 -1
package/dist/cjs/main.cjs
CHANGED
|
@@ -2489,6 +2489,7 @@ async function startWorker(options) {
|
|
|
2489
2489
|
requestId: options2.requestId,
|
|
2490
2490
|
trigger,
|
|
2491
2491
|
dirtyGeneration: snapshot2.dirtyGeneration,
|
|
2492
|
+
visibleBaselineHead: workspaceConvergence.visibleBaselineHead(),
|
|
2492
2493
|
explicit: true,
|
|
2493
2494
|
workInFlight: false,
|
|
2494
2495
|
requestedAt: Date.now()
|
|
@@ -2525,6 +2526,7 @@ async function startWorker(options) {
|
|
|
2525
2526
|
requestId,
|
|
2526
2527
|
trigger,
|
|
2527
2528
|
dirtyGeneration: snapshot.dirtyGeneration,
|
|
2529
|
+
visibleBaselineHead: workspaceConvergence.visibleBaselineHead(),
|
|
2528
2530
|
explicit: options2.requestId !== void 0,
|
|
2529
2531
|
workInFlight: false,
|
|
2530
2532
|
requestedAt: Date.now()
|
|
@@ -2728,6 +2730,7 @@ async function startWorker(options) {
|
|
|
2728
2730
|
const newVisibleCheckouts = [...newVisibleCheckoutByKey.values()];
|
|
2729
2731
|
const input = workspaceSyncInput(trigger, {
|
|
2730
2732
|
...overrides,
|
|
2733
|
+
...shouldEnsureCheckouts && !trigger.canonicalCheckoutOnly ? { visibleBaselineHead: workspaceConvergence.visibleBaselineHead() } : {},
|
|
2731
2734
|
...newVisibleCheckouts.length > 0 ? { newVisibleCheckouts } : {}
|
|
2732
2735
|
});
|
|
2733
2736
|
synchronizeStartedAt = Date.now();
|
|
@@ -2845,7 +2848,8 @@ async function startWorker(options) {
|
|
|
2845
2848
|
canonicalResolverCheckout: true,
|
|
2846
2849
|
browserPortForwarding: true,
|
|
2847
2850
|
globalWorkspaceSyncLeaseV1: true,
|
|
2848
|
-
eventualWorkspaceSyncV1: true
|
|
2851
|
+
eventualWorkspaceSyncV1: true,
|
|
2852
|
+
workspaceIncidentCandidatePinV1: true
|
|
2849
2853
|
},
|
|
2850
2854
|
projectRoot: projectsRoot,
|
|
2851
2855
|
artifactRoot,
|
|
@@ -2912,7 +2916,15 @@ async function startWorker(options) {
|
|
|
2912
2916
|
hydrateVisible: firstBootstrap && !activeWorkspaceIncidentId
|
|
2913
2917
|
});
|
|
2914
2918
|
if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
|
|
2915
|
-
|
|
2919
|
+
if ((0, import_workspace_convergence.workspaceBootstrapEstablishesVisibleBaseline)({
|
|
2920
|
+
hydrated: bootstrapped.hydrated,
|
|
2921
|
+
firstBootstrap,
|
|
2922
|
+
incidentActive: Boolean(activeWorkspaceIncidentId),
|
|
2923
|
+
currentLocalHead: workspaceConvergence.snapshot().localHead,
|
|
2924
|
+
currentVisibleBaselineHead: workspaceConvergence.visibleBaselineHead()
|
|
2925
|
+
})) {
|
|
2926
|
+
workspaceConvergence.setLocalHead(bootstrapped.localHead);
|
|
2927
|
+
}
|
|
2916
2928
|
workspaceConvergence.observeCanonicalHead(bootstrapped.canonicalHead);
|
|
2917
2929
|
for (const checkout of currentCheckouts) {
|
|
2918
2930
|
const manifest = manifestByProjectId.get(checkout.projectId);
|
|
@@ -2985,6 +2997,7 @@ async function startWorker(options) {
|
|
|
2985
2997
|
quarantineRef: message.quarantineRef,
|
|
2986
2998
|
confirmedLargeDiff: message.confirmedLargeDiff,
|
|
2987
2999
|
confirmationReason: message.confirmationReason,
|
|
3000
|
+
expectedIncidentCandidateHead: message.expectedIncidentCandidateHead,
|
|
2988
3001
|
resetToCanonical: message.resetToCanonical,
|
|
2989
3002
|
skipVisibleMirror: message.skipVisibleMirror
|
|
2990
3003
|
}
|
|
@@ -3063,6 +3076,7 @@ async function startWorker(options) {
|
|
|
3063
3076
|
attemptId: message.attemptId,
|
|
3064
3077
|
quarantineRef: message.quarantineRef,
|
|
3065
3078
|
expectedCanonicalHead: message.expectedHead,
|
|
3079
|
+
visibleBaselineHead: publication.visibleBaselineHead,
|
|
3066
3080
|
newVisibleCheckouts: [...createdCheckouts, ...pendingTargets]
|
|
3067
3081
|
})
|
|
3068
3082
|
);
|
|
@@ -3140,6 +3154,7 @@ async function startWorker(options) {
|
|
|
3140
3154
|
published: true
|
|
3141
3155
|
});
|
|
3142
3156
|
} else if (message.status === "blocked" && workspaceIncidentOrdering.permitsBlockedResponse(message.incidentId)) {
|
|
3157
|
+
if (message.visibleBaselineHead) workspaceConvergence.setVisibleBaselineHead(message.visibleBaselineHead);
|
|
3143
3158
|
workspaceConvergence.block();
|
|
3144
3159
|
if (message.incidentId) activeWorkspaceIncidentId = message.incidentId;
|
|
3145
3160
|
} else {
|
|
@@ -3191,7 +3206,11 @@ async function startWorker(options) {
|
|
|
3191
3206
|
if (message.type === "workspace_incident_updated") {
|
|
3192
3207
|
workspaceIncidentOrdering.observe(message);
|
|
3193
3208
|
const previousIncidentId = activeWorkspaceIncidentId;
|
|
3194
|
-
|
|
3209
|
+
const nextIncidentId = (0, import_workspace_incident_state.applyWorkspaceIncidentUpdate)(activeWorkspaceIncidentId, message);
|
|
3210
|
+
if (message.incidentId && (previousIncidentId === message.incidentId || nextIncidentId === message.incidentId) && message.originWorkerLabel === label && message.visibleBaselineHead) {
|
|
3211
|
+
workspaceConvergence.setVisibleBaselineHead(message.visibleBaselineHead);
|
|
3212
|
+
}
|
|
3213
|
+
activeWorkspaceIncidentId = nextIncidentId;
|
|
3195
3214
|
const releasedHead = (0, import_workspace_incident_state.releasePendingWorkspaceHead)(
|
|
3196
3215
|
previousIncidentId,
|
|
3197
3216
|
activeWorkspaceIncidentId,
|
package/dist/cjs/package.json
CHANGED
|
@@ -36,6 +36,7 @@ __export(workspace_convergence_exports, {
|
|
|
36
36
|
processWorkspaceConvergenceState: () => processWorkspaceConvergenceState,
|
|
37
37
|
readWorkspaceConvergenceState: () => readWorkspaceConvergenceState,
|
|
38
38
|
waitForWorkspacePublicationOnShutdown: () => waitForWorkspacePublicationOnShutdown,
|
|
39
|
+
workspaceBootstrapEstablishesVisibleBaseline: () => workspaceBootstrapEstablishesVisibleBaseline,
|
|
39
40
|
workspacePublicationIncidentAction: () => workspacePublicationIncidentAction,
|
|
40
41
|
workspacePublicationTelemetry: () => workspacePublicationTelemetry
|
|
41
42
|
});
|
|
@@ -113,6 +114,7 @@ class ExplicitWorkspacePublicationQueue {
|
|
|
113
114
|
}
|
|
114
115
|
const EMPTY_STATE = {
|
|
115
116
|
localHead: null,
|
|
117
|
+
visibleBaselineHead: null,
|
|
116
118
|
desiredCanonicalHead: null,
|
|
117
119
|
dirtyGeneration: 0,
|
|
118
120
|
publishedGeneration: 0
|
|
@@ -126,11 +128,12 @@ function validGeneration(value) {
|
|
|
126
128
|
function readWorkspaceConvergenceState(statePath) {
|
|
127
129
|
try {
|
|
128
130
|
const parsed = JSON.parse(import_node_fs.default.readFileSync(statePath, "utf8"));
|
|
129
|
-
if (!validHead(parsed.localHead) || !validHead(parsed.desiredCanonicalHead) || !validGeneration(parsed.dirtyGeneration) || !validGeneration(parsed.publishedGeneration) || parsed.publishedGeneration > parsed.dirtyGeneration) {
|
|
131
|
+
if (!validHead(parsed.localHead) || !(parsed.visibleBaselineHead === void 0 || validHead(parsed.visibleBaselineHead)) || !validHead(parsed.desiredCanonicalHead) || !validGeneration(parsed.dirtyGeneration) || !validGeneration(parsed.publishedGeneration) || parsed.publishedGeneration > parsed.dirtyGeneration) {
|
|
130
132
|
return { ...EMPTY_STATE };
|
|
131
133
|
}
|
|
132
134
|
return {
|
|
133
135
|
localHead: parsed.localHead,
|
|
136
|
+
visibleBaselineHead: parsed.visibleBaselineHead === void 0 ? parsed.localHead : parsed.visibleBaselineHead,
|
|
134
137
|
desiredCanonicalHead: parsed.desiredCanonicalHead,
|
|
135
138
|
dirtyGeneration: parsed.dirtyGeneration,
|
|
136
139
|
publishedGeneration: parsed.publishedGeneration
|
|
@@ -149,11 +152,26 @@ class WorkspaceConvergenceState {
|
|
|
149
152
|
state;
|
|
150
153
|
publicationState;
|
|
151
154
|
snapshot() {
|
|
152
|
-
return {
|
|
155
|
+
return {
|
|
156
|
+
localHead: this.state.localHead,
|
|
157
|
+
desiredCanonicalHead: this.state.desiredCanonicalHead,
|
|
158
|
+
dirtyGeneration: this.state.dirtyGeneration,
|
|
159
|
+
publishedGeneration: this.state.publishedGeneration,
|
|
160
|
+
publicationState: this.publicationState
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
visibleBaselineHead() {
|
|
164
|
+
return this.state.visibleBaselineHead;
|
|
153
165
|
}
|
|
154
166
|
setLocalHead(localHead) {
|
|
155
167
|
if (!validHead(localHead)) throw new Error("Invalid local workspace head");
|
|
156
168
|
this.state.localHead = localHead;
|
|
169
|
+
this.state.visibleBaselineHead = localHead;
|
|
170
|
+
this.persist();
|
|
171
|
+
}
|
|
172
|
+
setVisibleBaselineHead(visibleBaselineHead) {
|
|
173
|
+
if (!validHead(visibleBaselineHead)) throw new Error("Invalid visible workspace baseline head");
|
|
174
|
+
this.state.visibleBaselineHead = visibleBaselineHead;
|
|
157
175
|
this.persist();
|
|
158
176
|
}
|
|
159
177
|
observeCanonicalHead(canonicalHead) {
|
|
@@ -207,6 +225,7 @@ class WorkspaceConvergenceState {
|
|
|
207
225
|
if (input.published) {
|
|
208
226
|
this.state.publishedGeneration = Math.max(this.state.publishedGeneration, input.generation);
|
|
209
227
|
this.state.localHead = input.canonicalHead;
|
|
228
|
+
this.state.visibleBaselineHead = input.canonicalHead;
|
|
210
229
|
this.state.desiredCanonicalHead = input.canonicalHead;
|
|
211
230
|
}
|
|
212
231
|
this.publicationState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
@@ -229,6 +248,10 @@ function processWorkspaceConvergenceState(statePath) {
|
|
|
229
248
|
PROCESS_WORKSPACE_CONVERGENCE_STATES.set(key, created);
|
|
230
249
|
return created;
|
|
231
250
|
}
|
|
251
|
+
function workspaceBootstrapEstablishesVisibleBaseline(input) {
|
|
252
|
+
if (input.hydrated) return true;
|
|
253
|
+
return input.firstBootstrap && !input.incidentActive && input.currentLocalHead === null && input.currentVisibleBaselineHead === null;
|
|
254
|
+
}
|
|
232
255
|
async function waitForWorkspacePublicationOnShutdown(input) {
|
|
233
256
|
input.forcePublication();
|
|
234
257
|
const deadline = Date.now() + (input.timeoutMs ?? 1e4);
|
|
@@ -251,6 +274,7 @@ async function waitForWorkspacePublicationOnShutdown(input) {
|
|
|
251
274
|
processWorkspaceConvergenceState,
|
|
252
275
|
readWorkspaceConvergenceState,
|
|
253
276
|
waitForWorkspacePublicationOnShutdown,
|
|
277
|
+
workspaceBootstrapEstablishesVisibleBaseline,
|
|
254
278
|
workspacePublicationIncidentAction,
|
|
255
279
|
workspacePublicationTelemetry
|
|
256
280
|
});
|
|
@@ -1195,6 +1195,48 @@ function mirrorVisibleWorkspaceToShadow(input, excludedCheckouts = /* @__PURE__
|
|
|
1195
1195
|
projection.opaqueRoots = [...new Set(projection.opaqueRoots)].sort();
|
|
1196
1196
|
return projection;
|
|
1197
1197
|
}
|
|
1198
|
+
function visibleWorkspaceMatchesProjection(input, projection) {
|
|
1199
|
+
const preimages = projection.visiblePreimages;
|
|
1200
|
+
if (!preimages) return false;
|
|
1201
|
+
const workspacePlansRoot = localWorkspacePlansPath(input.plansRoot);
|
|
1202
|
+
const workspacePlansPreimage = preimages.get(workspacePlansRoot);
|
|
1203
|
+
if (!workspacePlansPreimage || !visibleRegularRootMatchesCopiedPreimage(
|
|
1204
|
+
input,
|
|
1205
|
+
workspacePlansRoot,
|
|
1206
|
+
[...workspacePlansPreimage.keys()].sort(),
|
|
1207
|
+
workspacePlansPreimage,
|
|
1208
|
+
(filePath) => filePath.endsWith(".plan.md")
|
|
1209
|
+
)) {
|
|
1210
|
+
return false;
|
|
1211
|
+
}
|
|
1212
|
+
for (const manifest of input.projects) {
|
|
1213
|
+
for (const branchName of manifest.branches) {
|
|
1214
|
+
const relativeRoot = workspaceProjectBranchRelativePath(manifest.projectId, branchName);
|
|
1215
|
+
const visibleRoot = visibleProjectBranchPath(input.projectsRoot, manifest, branchName);
|
|
1216
|
+
const projectPreimage = preimages.get(visibleRoot);
|
|
1217
|
+
if (!projectPreimage) return false;
|
|
1218
|
+
const gitlinks = [...projectPreimage].filter((entry) => entry[1].kind === "gitlink").map(([filePath, entry]) => ({ filePath, objectId: entry.objectId })).sort((left, right) => left.filePath.localeCompare(right.filePath));
|
|
1219
|
+
const ordinaryFiles = [...projectPreimage].filter(([, entry]) => entry.kind === "blob").map(([filePath]) => filePath).sort();
|
|
1220
|
+
const rootPrefix = `${relativeRoot}/`;
|
|
1221
|
+
const opaqueRoots = projection.opaqueRoots.filter((root) => root.startsWith(rootPrefix)).map((root) => root.slice(rootPrefix.length));
|
|
1222
|
+
if (!visibleProjectMatchesCopiedPreimage(input, visibleRoot, ordinaryFiles, gitlinks, projectPreimage, opaqueRoots)) {
|
|
1223
|
+
return false;
|
|
1224
|
+
}
|
|
1225
|
+
const plansRoot = localPlansBranchPath(input.plansRoot, manifest.projectId, branchName);
|
|
1226
|
+
const plansPreimage = preimages.get(plansRoot);
|
|
1227
|
+
if (!plansPreimage || !visibleRegularRootMatchesCopiedPreimage(
|
|
1228
|
+
input,
|
|
1229
|
+
plansRoot,
|
|
1230
|
+
[...plansPreimage.keys()].sort(),
|
|
1231
|
+
plansPreimage,
|
|
1232
|
+
(filePath) => filePath.endsWith(".plan.md")
|
|
1233
|
+
)) {
|
|
1234
|
+
return false;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
return true;
|
|
1239
|
+
}
|
|
1198
1240
|
function reconcileNewVisibleCheckouts(input) {
|
|
1199
1241
|
const projection = { entries: [], opaqueRoots: [], visiblePreimages: /* @__PURE__ */ new Map() };
|
|
1200
1242
|
for (const target of input.newVisibleCheckouts ?? []) {
|
|
@@ -1584,6 +1626,54 @@ function resetUncommittedShadowSnapshot(input) {
|
|
|
1584
1626
|
}
|
|
1585
1627
|
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean interrupted workspace snapshot");
|
|
1586
1628
|
}
|
|
1629
|
+
function restoreShadowToVisibleBaseline(input, canonicalHead) {
|
|
1630
|
+
if (input.skipVisibleMirror || input.trigger.canonicalCheckoutOnly) return void 0;
|
|
1631
|
+
if (input.visibleBaselineHead === void 0) {
|
|
1632
|
+
if (revParse(input, "HEAD") !== canonicalHead) {
|
|
1633
|
+
throw new Error("visible_workspace_baseline_missing: shadow HEAD differs from canonical main");
|
|
1634
|
+
}
|
|
1635
|
+
return void 0;
|
|
1636
|
+
}
|
|
1637
|
+
const baselineHead = input.visibleBaselineHead;
|
|
1638
|
+
const currentHead = revParse(input, "HEAD");
|
|
1639
|
+
if (!baselineHead) {
|
|
1640
|
+
if (canonicalHead) {
|
|
1641
|
+
throw new Error("Visible workspace has an unborn baseline after canonical main was created");
|
|
1642
|
+
}
|
|
1643
|
+
if (!currentHead) return null;
|
|
1644
|
+
resetUncommittedShadowSnapshot(input);
|
|
1645
|
+
const symbolicHead = runGit(input, input.shadowRoot, ["symbolic-ref", "-q", "HEAD"], "resolve unborn workspace branch");
|
|
1646
|
+
runGit(input, input.shadowRoot, ["update-ref", "-d", symbolicHead], "restore unborn visible workspace baseline");
|
|
1647
|
+
runGit(input, input.shadowRoot, ["read-tree", "--empty"], "clear unborn visible workspace index");
|
|
1648
|
+
runGit(input, input.shadowRoot, ["clean", "-fdx"], "clean unborn visible workspace baseline");
|
|
1649
|
+
return null;
|
|
1650
|
+
}
|
|
1651
|
+
if (currentHead === baselineHead) return baselineHead;
|
|
1652
|
+
if (revParse(input, `${baselineHead}^{commit}`) !== baselineHead) {
|
|
1653
|
+
throw new Error(`Visible workspace baseline ${baselineHead} is not available in the shadow repository`);
|
|
1654
|
+
}
|
|
1655
|
+
resetUncommittedShadowSnapshot(input);
|
|
1656
|
+
runGit(input, input.shadowRoot, ["reset", "--hard", baselineHead], "restore visible workspace publication baseline");
|
|
1657
|
+
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean restored visible workspace publication baseline");
|
|
1658
|
+
return baselineHead;
|
|
1659
|
+
}
|
|
1660
|
+
function restoreCleanShadowToExpectedIncidentCandidate(input) {
|
|
1661
|
+
const expectedHead = input.expectedIncidentCandidateHead;
|
|
1662
|
+
if (expectedHead === void 0) return;
|
|
1663
|
+
if (input.trigger.type !== "remediation" || input.trigger.canonicalCheckoutOnly || input.trigger.processRunId !== void 0 || input.trigger.toolCallId !== void 0 || input.skipVisibleMirror !== true || input.confirmedLargeDiff === true || input.resetToCanonical === true) {
|
|
1664
|
+
throw new Error("An expected incident candidate may only be used for stable remediation preparation");
|
|
1665
|
+
}
|
|
1666
|
+
const status = gitStatus(input);
|
|
1667
|
+
if (status) {
|
|
1668
|
+
throw new Error("incident_candidate_shadow_dirty: refusing to replace a dirty remediation shadow checkout");
|
|
1669
|
+
}
|
|
1670
|
+
if (revParse(input, `${expectedHead}^{commit}`) !== expectedHead) {
|
|
1671
|
+
throw new Error(`incident_candidate_unavailable: elected workspace candidate ${expectedHead} is not available`);
|
|
1672
|
+
}
|
|
1673
|
+
if (revParse(input, "HEAD") !== expectedHead) {
|
|
1674
|
+
runGit(input, input.shadowRoot, ["reset", "--hard", expectedHead], "restore elected workspace incident candidate");
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1587
1677
|
function activeWorkspaceRebase(input) {
|
|
1588
1678
|
for (const name of ["rebase-merge", "rebase-apply"]) {
|
|
1589
1679
|
const resolved = runGitResult(input, input.shadowRoot, ["rev-parse", "--git-path", name]);
|
|
@@ -1776,7 +1866,7 @@ function uploadWorkspaceCandidate(input, candidateHead) {
|
|
|
1776
1866
|
throw new Error("A server-issued workspace candidate ref is required before uploading a canonical workspace candidate");
|
|
1777
1867
|
}
|
|
1778
1868
|
assertWorkspaceQuarantineRef(quarantineRef);
|
|
1779
|
-
return runGitResult(input, input.shadowRoot, ["push", "origin",
|
|
1869
|
+
return runGitResult(input, input.shadowRoot, ["push", "origin", `${candidateHead}:${quarantineRef}`]);
|
|
1780
1870
|
}
|
|
1781
1871
|
function resetShadowToRemote(input, opaqueWorkspaceRoots = []) {
|
|
1782
1872
|
runGit(input, input.shadowRoot, ["fetch", "origin", "--prune"], "fetch canonical workspace before reset");
|
|
@@ -1785,7 +1875,9 @@ function resetShadowToRemote(input, opaqueWorkspaceRoots = []) {
|
|
|
1785
1875
|
runGitResult(input, input.shadowRoot, ["rebase", "--abort"]);
|
|
1786
1876
|
runGit(input, input.shadowRoot, ["reset", "--hard", `origin/${WORKSPACE_BRANCH}`], "reset workspace to canonical state");
|
|
1787
1877
|
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean reset workspace");
|
|
1788
|
-
if (!input.trigger.canonicalCheckoutOnly
|
|
1878
|
+
if (!input.trigger.canonicalCheckoutOnly && !input.skipVisibleMirror) {
|
|
1879
|
+
mirrorShadowWorkspaceToVisible(input, opaqueWorkspaceRoots);
|
|
1880
|
+
}
|
|
1789
1881
|
return remoteHead;
|
|
1790
1882
|
}
|
|
1791
1883
|
function baseResult(input, startingHead) {
|
|
@@ -1879,6 +1971,39 @@ function preserveWorkspaceConflictCandidate(input, observed, options) {
|
|
|
1879
1971
|
};
|
|
1880
1972
|
}
|
|
1881
1973
|
}
|
|
1974
|
+
function preserveInProgressWorkspaceRebaseConflict(input, observed, options) {
|
|
1975
|
+
try {
|
|
1976
|
+
assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, options.candidateHead);
|
|
1977
|
+
const upload = uploadWorkspaceCandidate(input, options.candidateHead);
|
|
1978
|
+
if (upload.exitCode !== 0) {
|
|
1979
|
+
throw new Error(upload.stderr || upload.stdout || "workspace rebase conflict candidate upload failed");
|
|
1980
|
+
}
|
|
1981
|
+
const conflictPaths = [...new Set(options.conflictPaths)].sort();
|
|
1982
|
+
return {
|
|
1983
|
+
...observed,
|
|
1984
|
+
outcome: "conflict_blocked",
|
|
1985
|
+
expectedHead: options.expectedHead,
|
|
1986
|
+
candidateHead: options.candidateHead,
|
|
1987
|
+
quarantineRef: input.quarantineRef,
|
|
1988
|
+
rebaseCount: options.rebaseCount,
|
|
1989
|
+
affectedProjects: affectedProjects(conflictPaths),
|
|
1990
|
+
affectedPaths: reportedPaths(conflictPaths),
|
|
1991
|
+
error: options.error,
|
|
1992
|
+
gitStatus: gitStatus(input)
|
|
1993
|
+
};
|
|
1994
|
+
} catch (error) {
|
|
1995
|
+
runGitResult(input, input.shadowRoot, ["rebase", "--abort"]);
|
|
1996
|
+
return {
|
|
1997
|
+
...observed,
|
|
1998
|
+
outcome: "failed",
|
|
1999
|
+
rebaseCount: options.rebaseCount,
|
|
2000
|
+
affectedProjects: affectedProjects(options.conflictPaths),
|
|
2001
|
+
affectedPaths: reportedPaths(options.conflictPaths),
|
|
2002
|
+
error: `Workspace synchronization found a rebase conflict but could not preserve its durable candidate: ${error instanceof Error ? error.message : String(error)}`,
|
|
2003
|
+
gitStatus: gitStatus(input)
|
|
2004
|
+
};
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
1882
2007
|
function preserveLargeDiffCandidate(input, observed, error) {
|
|
1883
2008
|
try {
|
|
1884
2009
|
if (stagedPaths(input).length > 0) {
|
|
@@ -1929,6 +2054,10 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1929
2054
|
error: `canonical_head_advanced: expected ${input.expectedCanonicalHead ?? "unborn"}, fetched ${remoteHeadAtStart ?? "unborn"}`
|
|
1930
2055
|
};
|
|
1931
2056
|
}
|
|
2057
|
+
const inProgressRemediation = remediationRebaseInProgressResult(input, result);
|
|
2058
|
+
if (inProgressRemediation) return inProgressRemediation;
|
|
2059
|
+
restoreCleanShadowToExpectedIncidentCandidate(input);
|
|
2060
|
+
const visibleBaselineHead = restoreShadowToVisibleBaseline(input, remoteHeadAtStart);
|
|
1932
2061
|
const inboundConflictsAtStart = input.skipVisibleMirror && !isCanonicalRemediationSync && remoteHeadAtStart ? visibleHydrationConflictPaths(input, hydrationPreimages, `origin/${WORKSPACE_BRANCH}`) : [];
|
|
1933
2062
|
if (input.resetToCanonical) {
|
|
1934
2063
|
if (input.trigger.canonicalCheckoutOnly) {
|
|
@@ -1943,8 +2072,6 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1943
2072
|
gitStatus: gitStatus(input)
|
|
1944
2073
|
};
|
|
1945
2074
|
}
|
|
1946
|
-
const inProgressRemediation = remediationRebaseInProgressResult(input, result);
|
|
1947
|
-
if (inProgressRemediation) return inProgressRemediation;
|
|
1948
2075
|
if (!input.skipVisibleMirror) resetUncommittedShadowSnapshot(input);
|
|
1949
2076
|
restoreUnscopedCanonicalCheckoutSubtrees(input);
|
|
1950
2077
|
if (!input.skipVisibleMirror) {
|
|
@@ -1954,6 +2081,10 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1954
2081
|
mergeGitlinkProjection(mirroredGitlinkProjection, reconcileNewVisibleCheckouts(input));
|
|
1955
2082
|
mirroredGitlinkProjection.opaqueRoots = [...new Set(mirroredGitlinkProjection.opaqueRoots)].sort();
|
|
1956
2083
|
hydrationPreimages = mirroredGitlinkProjection.visiblePreimages ?? hydrationPreimages;
|
|
2084
|
+
if (input.trigger.type === "remediation" && !visibleWorkspaceMatchesProjection(input, mirroredGitlinkProjection)) {
|
|
2085
|
+
resetUncommittedShadowSnapshot(input);
|
|
2086
|
+
throw new Error("visible_workspace_unstable: visible files changed while preparing the remediation snapshot");
|
|
2087
|
+
}
|
|
1957
2088
|
}
|
|
1958
2089
|
const stagePathspecs = mirroredGitlinkProjection.opaqueRoots.flatMap((root) => [`:(exclude,literal)${root}`]);
|
|
1959
2090
|
runGit(input, input.shadowRoot, ["add", "-A", "--", ".", ...stagePathspecs], "stage workspace changes");
|
|
@@ -1962,7 +2093,8 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1962
2093
|
if (redundantInboundCandidate) return redundantInboundCandidate;
|
|
1963
2094
|
const sampledCanonicalCheckoutTreeHash = sampleCanonicalCheckoutTree(input);
|
|
1964
2095
|
const stagedWorkingPaths = stagedPaths(input);
|
|
1965
|
-
const
|
|
2096
|
+
const preservesDivergentVisibleSnapshot = input.confirmedLargeDiff === true || input.trigger.type === "remediation" || input.trigger.type === "remediation_confirm";
|
|
2097
|
+
const baseRevision = preservesDivergentVisibleSnapshot ? remoteHeadAtStart : visibleBaselineHead !== void 0 ? visibleBaselineHead : candidateBaseRevision(input);
|
|
1966
2098
|
const paths = stagedPaths(input, baseRevision);
|
|
1967
2099
|
const diffSizeBytes = await stagedDiffSizeBytes(input, baseRevision);
|
|
1968
2100
|
const statusBeforeCommit = reportedStatus(gitStatus(input));
|
|
@@ -1975,16 +2107,12 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1975
2107
|
...sampledCanonicalCheckoutTreeHash ? { sampledCanonicalCheckoutTreeHash } : {}
|
|
1976
2108
|
};
|
|
1977
2109
|
const destructiveReductions = destructiveCheckoutReductions(input, baseRevision);
|
|
2110
|
+
let blockedDiffError;
|
|
1978
2111
|
if (destructiveReductions.length > 0 && !input.confirmedLargeDiff) {
|
|
1979
2112
|
const summary = destructiveReductions.map(({ root, trackedBefore, trackedAfter }) => `${root} (${trackedBefore} -> ${trackedAfter} tracked files)`).join(", ");
|
|
1980
|
-
|
|
1981
|
-
}
|
|
1982
|
-
|
|
1983
|
-
return preserveLargeDiffCandidate(
|
|
1984
|
-
input,
|
|
1985
|
-
observed,
|
|
1986
|
-
`Workspace safety guard blocked a ${diffSizeBytes}-byte diff above the ${MAX_WORKSPACE_SYNC_DIFF_BYTES}-byte automatic publication limit.`
|
|
1987
|
-
);
|
|
2113
|
+
blockedDiffError = `Workspace safety guard blocked a destructive checkout reduction: ${summary}`;
|
|
2114
|
+
} else if (paths.length > 0 && diffSizeBytes > MAX_WORKSPACE_SYNC_DIFF_BYTES && !input.confirmedLargeDiff && !input.trigger.canonicalCheckoutOnly) {
|
|
2115
|
+
blockedDiffError = `Workspace safety guard blocked a ${diffSizeBytes}-byte diff above the ${MAX_WORKSPACE_SYNC_DIFF_BYTES}-byte automatic publication limit.`;
|
|
1988
2116
|
}
|
|
1989
2117
|
if (stagedWorkingPaths.length > 0) {
|
|
1990
2118
|
runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit workspace changes");
|
|
@@ -1992,8 +2120,11 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1992
2120
|
let candidateHead = revParse(input, "HEAD");
|
|
1993
2121
|
const candidateHeadBeforeRebase = candidateHead;
|
|
1994
2122
|
if (candidateHead) assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, candidateHead);
|
|
1995
|
-
const
|
|
1996
|
-
const
|
|
2123
|
+
const remoteHeadBeforeRebase = revParse(input, `origin/${WORKSPACE_BRANCH}`);
|
|
2124
|
+
const hasRemoteMain = Boolean(remoteHeadBeforeRebase);
|
|
2125
|
+
const hasCapturedVisibleDelta = visibleBaselineHead === void 0 || paths.length > 0;
|
|
2126
|
+
const mustDropVisibleBaselinePrefix = !preservesDivergentVisibleSnapshot && visibleBaselineHead !== void 0 && visibleBaselineHead !== remoteHeadBeforeRebase && stagedWorkingPaths.length > 0;
|
|
2127
|
+
const hasUnpushedCommit = candidateHead && hasCapturedVisibleDelta ? mustDropVisibleBaselinePrefix || !hasRemoteMain || !tryGit(input, input.shadowRoot, ["merge-base", "--is-ancestor", candidateHead, `origin/${WORKSPACE_BRANCH}`]) : false;
|
|
1997
2128
|
if (hasUnpushedCommit && inboundConflictsAtStart.length > 0) {
|
|
1998
2129
|
const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...inboundConflictsAtStart])].sort();
|
|
1999
2130
|
return preserveWorkspaceConflictCandidate(input, observed, {
|
|
@@ -2067,13 +2198,36 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
2067
2198
|
};
|
|
2068
2199
|
}
|
|
2069
2200
|
let rebaseCount = 0;
|
|
2070
|
-
const expectedHead =
|
|
2071
|
-
if (
|
|
2072
|
-
|
|
2201
|
+
const expectedHead = remoteHeadBeforeRebase;
|
|
2202
|
+
if (mustDropVisibleBaselinePrefix && !expectedHead) {
|
|
2203
|
+
return {
|
|
2204
|
+
...observed,
|
|
2205
|
+
outcome: "failed",
|
|
2206
|
+
candidateHead: candidateHead ?? void 0,
|
|
2207
|
+
error: "Cannot normalize a visible workspace tail onto an unborn canonical workspace",
|
|
2208
|
+
gitStatus: gitStatus(input)
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
if (candidateHead && expectedHead && (mustDropVisibleBaselinePrefix || !tryGit(input, input.shadowRoot, ["merge-base", "--is-ancestor", expectedHead, candidateHead]))) {
|
|
2212
|
+
const rebase = runGitResult(
|
|
2213
|
+
input,
|
|
2214
|
+
input.shadowRoot,
|
|
2215
|
+
mustDropVisibleBaselinePrefix && visibleBaselineHead ? ["rebase", "--onto", `origin/${WORKSPACE_BRANCH}`, visibleBaselineHead] : ["rebase", `origin/${WORKSPACE_BRANCH}`]
|
|
2216
|
+
);
|
|
2073
2217
|
rebaseCount += 1;
|
|
2074
2218
|
if (rebase.exitCode !== 0) {
|
|
2075
2219
|
const conflicted = runGitResult(input, input.shadowRoot, ["diff", "--name-only", "--diff-filter=U", "-z"]);
|
|
2076
2220
|
const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...conflicted.stdout.split("\0").filter(Boolean)])].sort();
|
|
2221
|
+
if (mustDropVisibleBaselinePrefix) {
|
|
2222
|
+
const originalCandidateHead = revParse(input, "ORIG_HEAD") ?? candidateHead;
|
|
2223
|
+
return preserveInProgressWorkspaceRebaseConflict(input, observed, {
|
|
2224
|
+
candidateHead: originalCandidateHead,
|
|
2225
|
+
expectedHead,
|
|
2226
|
+
rebaseCount,
|
|
2227
|
+
conflictPaths,
|
|
2228
|
+
error: `Workspace tail conflicts with canonical head ${expectedHead}; local changes were preserved for reconciliation in the in-progress rebase.`
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2077
2231
|
runGit(input, input.shadowRoot, ["rebase", "--abort"], "preserve workspace candidate after rebase conflict");
|
|
2078
2232
|
return preserveWorkspaceConflictCandidate(input, observed, {
|
|
2079
2233
|
baseRevision: candidateHead,
|
|
@@ -2119,6 +2273,9 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
2119
2273
|
error: "Workspace changed while its candidate was reconciling; visible changes and the old-base candidate were preserved."
|
|
2120
2274
|
});
|
|
2121
2275
|
}
|
|
2276
|
+
if (blockedDiffError) {
|
|
2277
|
+
return preserveLargeDiffCandidate(input, { ...observed, rebaseCount }, blockedDiffError);
|
|
2278
|
+
}
|
|
2122
2279
|
const upload = uploadWorkspaceCandidate(input, candidateHead);
|
|
2123
2280
|
if (upload.exitCode !== 0) {
|
|
2124
2281
|
return {
|
package/dist/mjs/main.mjs
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
WorkspaceManifestRevisionFence,
|
|
23
23
|
processWorkspaceConvergenceState,
|
|
24
24
|
waitForWorkspacePublicationOnShutdown,
|
|
25
|
+
workspaceBootstrapEstablishesVisibleBaseline,
|
|
25
26
|
workspacePublicationIncidentAction,
|
|
26
27
|
workspacePublicationTelemetry
|
|
27
28
|
} from "./workspace-convergence.mjs";
|
|
@@ -2459,6 +2460,7 @@ async function startWorker(options) {
|
|
|
2459
2460
|
requestId: options2.requestId,
|
|
2460
2461
|
trigger,
|
|
2461
2462
|
dirtyGeneration: snapshot2.dirtyGeneration,
|
|
2463
|
+
visibleBaselineHead: workspaceConvergence.visibleBaselineHead(),
|
|
2462
2464
|
explicit: true,
|
|
2463
2465
|
workInFlight: false,
|
|
2464
2466
|
requestedAt: Date.now()
|
|
@@ -2495,6 +2497,7 @@ async function startWorker(options) {
|
|
|
2495
2497
|
requestId,
|
|
2496
2498
|
trigger,
|
|
2497
2499
|
dirtyGeneration: snapshot.dirtyGeneration,
|
|
2500
|
+
visibleBaselineHead: workspaceConvergence.visibleBaselineHead(),
|
|
2498
2501
|
explicit: options2.requestId !== void 0,
|
|
2499
2502
|
workInFlight: false,
|
|
2500
2503
|
requestedAt: Date.now()
|
|
@@ -2698,6 +2701,7 @@ async function startWorker(options) {
|
|
|
2698
2701
|
const newVisibleCheckouts = [...newVisibleCheckoutByKey.values()];
|
|
2699
2702
|
const input = workspaceSyncInput(trigger, {
|
|
2700
2703
|
...overrides,
|
|
2704
|
+
...shouldEnsureCheckouts && !trigger.canonicalCheckoutOnly ? { visibleBaselineHead: workspaceConvergence.visibleBaselineHead() } : {},
|
|
2701
2705
|
...newVisibleCheckouts.length > 0 ? { newVisibleCheckouts } : {}
|
|
2702
2706
|
});
|
|
2703
2707
|
synchronizeStartedAt = Date.now();
|
|
@@ -2815,7 +2819,8 @@ async function startWorker(options) {
|
|
|
2815
2819
|
canonicalResolverCheckout: true,
|
|
2816
2820
|
browserPortForwarding: true,
|
|
2817
2821
|
globalWorkspaceSyncLeaseV1: true,
|
|
2818
|
-
eventualWorkspaceSyncV1: true
|
|
2822
|
+
eventualWorkspaceSyncV1: true,
|
|
2823
|
+
workspaceIncidentCandidatePinV1: true
|
|
2819
2824
|
},
|
|
2820
2825
|
projectRoot: projectsRoot,
|
|
2821
2826
|
artifactRoot,
|
|
@@ -2882,7 +2887,15 @@ async function startWorker(options) {
|
|
|
2882
2887
|
hydrateVisible: firstBootstrap && !activeWorkspaceIncidentId
|
|
2883
2888
|
});
|
|
2884
2889
|
if (!workspaceManifestRevision.isCurrent(revisionToken)) return false;
|
|
2885
|
-
|
|
2890
|
+
if (workspaceBootstrapEstablishesVisibleBaseline({
|
|
2891
|
+
hydrated: bootstrapped.hydrated,
|
|
2892
|
+
firstBootstrap,
|
|
2893
|
+
incidentActive: Boolean(activeWorkspaceIncidentId),
|
|
2894
|
+
currentLocalHead: workspaceConvergence.snapshot().localHead,
|
|
2895
|
+
currentVisibleBaselineHead: workspaceConvergence.visibleBaselineHead()
|
|
2896
|
+
})) {
|
|
2897
|
+
workspaceConvergence.setLocalHead(bootstrapped.localHead);
|
|
2898
|
+
}
|
|
2886
2899
|
workspaceConvergence.observeCanonicalHead(bootstrapped.canonicalHead);
|
|
2887
2900
|
for (const checkout of currentCheckouts) {
|
|
2888
2901
|
const manifest = manifestByProjectId.get(checkout.projectId);
|
|
@@ -2955,6 +2968,7 @@ async function startWorker(options) {
|
|
|
2955
2968
|
quarantineRef: message.quarantineRef,
|
|
2956
2969
|
confirmedLargeDiff: message.confirmedLargeDiff,
|
|
2957
2970
|
confirmationReason: message.confirmationReason,
|
|
2971
|
+
expectedIncidentCandidateHead: message.expectedIncidentCandidateHead,
|
|
2958
2972
|
resetToCanonical: message.resetToCanonical,
|
|
2959
2973
|
skipVisibleMirror: message.skipVisibleMirror
|
|
2960
2974
|
}
|
|
@@ -3033,6 +3047,7 @@ async function startWorker(options) {
|
|
|
3033
3047
|
attemptId: message.attemptId,
|
|
3034
3048
|
quarantineRef: message.quarantineRef,
|
|
3035
3049
|
expectedCanonicalHead: message.expectedHead,
|
|
3050
|
+
visibleBaselineHead: publication.visibleBaselineHead,
|
|
3036
3051
|
newVisibleCheckouts: [...createdCheckouts, ...pendingTargets]
|
|
3037
3052
|
})
|
|
3038
3053
|
);
|
|
@@ -3110,6 +3125,7 @@ async function startWorker(options) {
|
|
|
3110
3125
|
published: true
|
|
3111
3126
|
});
|
|
3112
3127
|
} else if (message.status === "blocked" && workspaceIncidentOrdering.permitsBlockedResponse(message.incidentId)) {
|
|
3128
|
+
if (message.visibleBaselineHead) workspaceConvergence.setVisibleBaselineHead(message.visibleBaselineHead);
|
|
3113
3129
|
workspaceConvergence.block();
|
|
3114
3130
|
if (message.incidentId) activeWorkspaceIncidentId = message.incidentId;
|
|
3115
3131
|
} else {
|
|
@@ -3161,7 +3177,11 @@ async function startWorker(options) {
|
|
|
3161
3177
|
if (message.type === "workspace_incident_updated") {
|
|
3162
3178
|
workspaceIncidentOrdering.observe(message);
|
|
3163
3179
|
const previousIncidentId = activeWorkspaceIncidentId;
|
|
3164
|
-
|
|
3180
|
+
const nextIncidentId = applyWorkspaceIncidentUpdate(activeWorkspaceIncidentId, message);
|
|
3181
|
+
if (message.incidentId && (previousIncidentId === message.incidentId || nextIncidentId === message.incidentId) && message.originWorkerLabel === label && message.visibleBaselineHead) {
|
|
3182
|
+
workspaceConvergence.setVisibleBaselineHead(message.visibleBaselineHead);
|
|
3183
|
+
}
|
|
3184
|
+
activeWorkspaceIncidentId = nextIncidentId;
|
|
3165
3185
|
const releasedHead = releasePendingWorkspaceHead(
|
|
3166
3186
|
previousIncidentId,
|
|
3167
3187
|
activeWorkspaceIncidentId,
|
package/dist/mjs/package.json
CHANGED
|
@@ -71,6 +71,7 @@ class ExplicitWorkspacePublicationQueue {
|
|
|
71
71
|
}
|
|
72
72
|
const EMPTY_STATE = {
|
|
73
73
|
localHead: null,
|
|
74
|
+
visibleBaselineHead: null,
|
|
74
75
|
desiredCanonicalHead: null,
|
|
75
76
|
dirtyGeneration: 0,
|
|
76
77
|
publishedGeneration: 0
|
|
@@ -84,11 +85,12 @@ function validGeneration(value) {
|
|
|
84
85
|
function readWorkspaceConvergenceState(statePath) {
|
|
85
86
|
try {
|
|
86
87
|
const parsed = JSON.parse(fs.readFileSync(statePath, "utf8"));
|
|
87
|
-
if (!validHead(parsed.localHead) || !validHead(parsed.desiredCanonicalHead) || !validGeneration(parsed.dirtyGeneration) || !validGeneration(parsed.publishedGeneration) || parsed.publishedGeneration > parsed.dirtyGeneration) {
|
|
88
|
+
if (!validHead(parsed.localHead) || !(parsed.visibleBaselineHead === void 0 || validHead(parsed.visibleBaselineHead)) || !validHead(parsed.desiredCanonicalHead) || !validGeneration(parsed.dirtyGeneration) || !validGeneration(parsed.publishedGeneration) || parsed.publishedGeneration > parsed.dirtyGeneration) {
|
|
88
89
|
return { ...EMPTY_STATE };
|
|
89
90
|
}
|
|
90
91
|
return {
|
|
91
92
|
localHead: parsed.localHead,
|
|
93
|
+
visibleBaselineHead: parsed.visibleBaselineHead === void 0 ? parsed.localHead : parsed.visibleBaselineHead,
|
|
92
94
|
desiredCanonicalHead: parsed.desiredCanonicalHead,
|
|
93
95
|
dirtyGeneration: parsed.dirtyGeneration,
|
|
94
96
|
publishedGeneration: parsed.publishedGeneration
|
|
@@ -107,11 +109,26 @@ class WorkspaceConvergenceState {
|
|
|
107
109
|
state;
|
|
108
110
|
publicationState;
|
|
109
111
|
snapshot() {
|
|
110
|
-
return {
|
|
112
|
+
return {
|
|
113
|
+
localHead: this.state.localHead,
|
|
114
|
+
desiredCanonicalHead: this.state.desiredCanonicalHead,
|
|
115
|
+
dirtyGeneration: this.state.dirtyGeneration,
|
|
116
|
+
publishedGeneration: this.state.publishedGeneration,
|
|
117
|
+
publicationState: this.publicationState
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
visibleBaselineHead() {
|
|
121
|
+
return this.state.visibleBaselineHead;
|
|
111
122
|
}
|
|
112
123
|
setLocalHead(localHead) {
|
|
113
124
|
if (!validHead(localHead)) throw new Error("Invalid local workspace head");
|
|
114
125
|
this.state.localHead = localHead;
|
|
126
|
+
this.state.visibleBaselineHead = localHead;
|
|
127
|
+
this.persist();
|
|
128
|
+
}
|
|
129
|
+
setVisibleBaselineHead(visibleBaselineHead) {
|
|
130
|
+
if (!validHead(visibleBaselineHead)) throw new Error("Invalid visible workspace baseline head");
|
|
131
|
+
this.state.visibleBaselineHead = visibleBaselineHead;
|
|
115
132
|
this.persist();
|
|
116
133
|
}
|
|
117
134
|
observeCanonicalHead(canonicalHead) {
|
|
@@ -165,6 +182,7 @@ class WorkspaceConvergenceState {
|
|
|
165
182
|
if (input.published) {
|
|
166
183
|
this.state.publishedGeneration = Math.max(this.state.publishedGeneration, input.generation);
|
|
167
184
|
this.state.localHead = input.canonicalHead;
|
|
185
|
+
this.state.visibleBaselineHead = input.canonicalHead;
|
|
168
186
|
this.state.desiredCanonicalHead = input.canonicalHead;
|
|
169
187
|
}
|
|
170
188
|
this.publicationState = this.state.dirtyGeneration > this.state.publishedGeneration ? "scheduled" : "idle";
|
|
@@ -187,6 +205,10 @@ function processWorkspaceConvergenceState(statePath) {
|
|
|
187
205
|
PROCESS_WORKSPACE_CONVERGENCE_STATES.set(key, created);
|
|
188
206
|
return created;
|
|
189
207
|
}
|
|
208
|
+
function workspaceBootstrapEstablishesVisibleBaseline(input) {
|
|
209
|
+
if (input.hydrated) return true;
|
|
210
|
+
return input.firstBootstrap && !input.incidentActive && input.currentLocalHead === null && input.currentVisibleBaselineHead === null;
|
|
211
|
+
}
|
|
190
212
|
async function waitForWorkspacePublicationOnShutdown(input) {
|
|
191
213
|
input.forcePublication();
|
|
192
214
|
const deadline = Date.now() + (input.timeoutMs ?? 1e4);
|
|
@@ -208,6 +230,7 @@ export {
|
|
|
208
230
|
processWorkspaceConvergenceState,
|
|
209
231
|
readWorkspaceConvergenceState,
|
|
210
232
|
waitForWorkspacePublicationOnShutdown,
|
|
233
|
+
workspaceBootstrapEstablishesVisibleBaseline,
|
|
211
234
|
workspacePublicationIncidentAction,
|
|
212
235
|
workspacePublicationTelemetry
|
|
213
236
|
};
|
|
@@ -1144,6 +1144,48 @@ function mirrorVisibleWorkspaceToShadow(input, excludedCheckouts = /* @__PURE__
|
|
|
1144
1144
|
projection.opaqueRoots = [...new Set(projection.opaqueRoots)].sort();
|
|
1145
1145
|
return projection;
|
|
1146
1146
|
}
|
|
1147
|
+
function visibleWorkspaceMatchesProjection(input, projection) {
|
|
1148
|
+
const preimages = projection.visiblePreimages;
|
|
1149
|
+
if (!preimages) return false;
|
|
1150
|
+
const workspacePlansRoot = localWorkspacePlansPath(input.plansRoot);
|
|
1151
|
+
const workspacePlansPreimage = preimages.get(workspacePlansRoot);
|
|
1152
|
+
if (!workspacePlansPreimage || !visibleRegularRootMatchesCopiedPreimage(
|
|
1153
|
+
input,
|
|
1154
|
+
workspacePlansRoot,
|
|
1155
|
+
[...workspacePlansPreimage.keys()].sort(),
|
|
1156
|
+
workspacePlansPreimage,
|
|
1157
|
+
(filePath) => filePath.endsWith(".plan.md")
|
|
1158
|
+
)) {
|
|
1159
|
+
return false;
|
|
1160
|
+
}
|
|
1161
|
+
for (const manifest of input.projects) {
|
|
1162
|
+
for (const branchName of manifest.branches) {
|
|
1163
|
+
const relativeRoot = workspaceProjectBranchRelativePath(manifest.projectId, branchName);
|
|
1164
|
+
const visibleRoot = visibleProjectBranchPath(input.projectsRoot, manifest, branchName);
|
|
1165
|
+
const projectPreimage = preimages.get(visibleRoot);
|
|
1166
|
+
if (!projectPreimage) return false;
|
|
1167
|
+
const gitlinks = [...projectPreimage].filter((entry) => entry[1].kind === "gitlink").map(([filePath, entry]) => ({ filePath, objectId: entry.objectId })).sort((left, right) => left.filePath.localeCompare(right.filePath));
|
|
1168
|
+
const ordinaryFiles = [...projectPreimage].filter(([, entry]) => entry.kind === "blob").map(([filePath]) => filePath).sort();
|
|
1169
|
+
const rootPrefix = `${relativeRoot}/`;
|
|
1170
|
+
const opaqueRoots = projection.opaqueRoots.filter((root) => root.startsWith(rootPrefix)).map((root) => root.slice(rootPrefix.length));
|
|
1171
|
+
if (!visibleProjectMatchesCopiedPreimage(input, visibleRoot, ordinaryFiles, gitlinks, projectPreimage, opaqueRoots)) {
|
|
1172
|
+
return false;
|
|
1173
|
+
}
|
|
1174
|
+
const plansRoot = localPlansBranchPath(input.plansRoot, manifest.projectId, branchName);
|
|
1175
|
+
const plansPreimage = preimages.get(plansRoot);
|
|
1176
|
+
if (!plansPreimage || !visibleRegularRootMatchesCopiedPreimage(
|
|
1177
|
+
input,
|
|
1178
|
+
plansRoot,
|
|
1179
|
+
[...plansPreimage.keys()].sort(),
|
|
1180
|
+
plansPreimage,
|
|
1181
|
+
(filePath) => filePath.endsWith(".plan.md")
|
|
1182
|
+
)) {
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
return true;
|
|
1188
|
+
}
|
|
1147
1189
|
function reconcileNewVisibleCheckouts(input) {
|
|
1148
1190
|
const projection = { entries: [], opaqueRoots: [], visiblePreimages: /* @__PURE__ */ new Map() };
|
|
1149
1191
|
for (const target of input.newVisibleCheckouts ?? []) {
|
|
@@ -1533,6 +1575,54 @@ function resetUncommittedShadowSnapshot(input) {
|
|
|
1533
1575
|
}
|
|
1534
1576
|
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean interrupted workspace snapshot");
|
|
1535
1577
|
}
|
|
1578
|
+
function restoreShadowToVisibleBaseline(input, canonicalHead) {
|
|
1579
|
+
if (input.skipVisibleMirror || input.trigger.canonicalCheckoutOnly) return void 0;
|
|
1580
|
+
if (input.visibleBaselineHead === void 0) {
|
|
1581
|
+
if (revParse(input, "HEAD") !== canonicalHead) {
|
|
1582
|
+
throw new Error("visible_workspace_baseline_missing: shadow HEAD differs from canonical main");
|
|
1583
|
+
}
|
|
1584
|
+
return void 0;
|
|
1585
|
+
}
|
|
1586
|
+
const baselineHead = input.visibleBaselineHead;
|
|
1587
|
+
const currentHead = revParse(input, "HEAD");
|
|
1588
|
+
if (!baselineHead) {
|
|
1589
|
+
if (canonicalHead) {
|
|
1590
|
+
throw new Error("Visible workspace has an unborn baseline after canonical main was created");
|
|
1591
|
+
}
|
|
1592
|
+
if (!currentHead) return null;
|
|
1593
|
+
resetUncommittedShadowSnapshot(input);
|
|
1594
|
+
const symbolicHead = runGit(input, input.shadowRoot, ["symbolic-ref", "-q", "HEAD"], "resolve unborn workspace branch");
|
|
1595
|
+
runGit(input, input.shadowRoot, ["update-ref", "-d", symbolicHead], "restore unborn visible workspace baseline");
|
|
1596
|
+
runGit(input, input.shadowRoot, ["read-tree", "--empty"], "clear unborn visible workspace index");
|
|
1597
|
+
runGit(input, input.shadowRoot, ["clean", "-fdx"], "clean unborn visible workspace baseline");
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
if (currentHead === baselineHead) return baselineHead;
|
|
1601
|
+
if (revParse(input, `${baselineHead}^{commit}`) !== baselineHead) {
|
|
1602
|
+
throw new Error(`Visible workspace baseline ${baselineHead} is not available in the shadow repository`);
|
|
1603
|
+
}
|
|
1604
|
+
resetUncommittedShadowSnapshot(input);
|
|
1605
|
+
runGit(input, input.shadowRoot, ["reset", "--hard", baselineHead], "restore visible workspace publication baseline");
|
|
1606
|
+
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean restored visible workspace publication baseline");
|
|
1607
|
+
return baselineHead;
|
|
1608
|
+
}
|
|
1609
|
+
function restoreCleanShadowToExpectedIncidentCandidate(input) {
|
|
1610
|
+
const expectedHead = input.expectedIncidentCandidateHead;
|
|
1611
|
+
if (expectedHead === void 0) return;
|
|
1612
|
+
if (input.trigger.type !== "remediation" || input.trigger.canonicalCheckoutOnly || input.trigger.processRunId !== void 0 || input.trigger.toolCallId !== void 0 || input.skipVisibleMirror !== true || input.confirmedLargeDiff === true || input.resetToCanonical === true) {
|
|
1613
|
+
throw new Error("An expected incident candidate may only be used for stable remediation preparation");
|
|
1614
|
+
}
|
|
1615
|
+
const status = gitStatus(input);
|
|
1616
|
+
if (status) {
|
|
1617
|
+
throw new Error("incident_candidate_shadow_dirty: refusing to replace a dirty remediation shadow checkout");
|
|
1618
|
+
}
|
|
1619
|
+
if (revParse(input, `${expectedHead}^{commit}`) !== expectedHead) {
|
|
1620
|
+
throw new Error(`incident_candidate_unavailable: elected workspace candidate ${expectedHead} is not available`);
|
|
1621
|
+
}
|
|
1622
|
+
if (revParse(input, "HEAD") !== expectedHead) {
|
|
1623
|
+
runGit(input, input.shadowRoot, ["reset", "--hard", expectedHead], "restore elected workspace incident candidate");
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1536
1626
|
function activeWorkspaceRebase(input) {
|
|
1537
1627
|
for (const name of ["rebase-merge", "rebase-apply"]) {
|
|
1538
1628
|
const resolved = runGitResult(input, input.shadowRoot, ["rev-parse", "--git-path", name]);
|
|
@@ -1725,7 +1815,7 @@ function uploadWorkspaceCandidate(input, candidateHead) {
|
|
|
1725
1815
|
throw new Error("A server-issued workspace candidate ref is required before uploading a canonical workspace candidate");
|
|
1726
1816
|
}
|
|
1727
1817
|
assertWorkspaceQuarantineRef(quarantineRef);
|
|
1728
|
-
return runGitResult(input, input.shadowRoot, ["push", "origin",
|
|
1818
|
+
return runGitResult(input, input.shadowRoot, ["push", "origin", `${candidateHead}:${quarantineRef}`]);
|
|
1729
1819
|
}
|
|
1730
1820
|
function resetShadowToRemote(input, opaqueWorkspaceRoots = []) {
|
|
1731
1821
|
runGit(input, input.shadowRoot, ["fetch", "origin", "--prune"], "fetch canonical workspace before reset");
|
|
@@ -1734,7 +1824,9 @@ function resetShadowToRemote(input, opaqueWorkspaceRoots = []) {
|
|
|
1734
1824
|
runGitResult(input, input.shadowRoot, ["rebase", "--abort"]);
|
|
1735
1825
|
runGit(input, input.shadowRoot, ["reset", "--hard", `origin/${WORKSPACE_BRANCH}`], "reset workspace to canonical state");
|
|
1736
1826
|
runGit(input, input.shadowRoot, ["clean", "-fd"], "clean reset workspace");
|
|
1737
|
-
if (!input.trigger.canonicalCheckoutOnly
|
|
1827
|
+
if (!input.trigger.canonicalCheckoutOnly && !input.skipVisibleMirror) {
|
|
1828
|
+
mirrorShadowWorkspaceToVisible(input, opaqueWorkspaceRoots);
|
|
1829
|
+
}
|
|
1738
1830
|
return remoteHead;
|
|
1739
1831
|
}
|
|
1740
1832
|
function baseResult(input, startingHead) {
|
|
@@ -1828,6 +1920,39 @@ function preserveWorkspaceConflictCandidate(input, observed, options) {
|
|
|
1828
1920
|
};
|
|
1829
1921
|
}
|
|
1830
1922
|
}
|
|
1923
|
+
function preserveInProgressWorkspaceRebaseConflict(input, observed, options) {
|
|
1924
|
+
try {
|
|
1925
|
+
assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, options.candidateHead);
|
|
1926
|
+
const upload = uploadWorkspaceCandidate(input, options.candidateHead);
|
|
1927
|
+
if (upload.exitCode !== 0) {
|
|
1928
|
+
throw new Error(upload.stderr || upload.stdout || "workspace rebase conflict candidate upload failed");
|
|
1929
|
+
}
|
|
1930
|
+
const conflictPaths = [...new Set(options.conflictPaths)].sort();
|
|
1931
|
+
return {
|
|
1932
|
+
...observed,
|
|
1933
|
+
outcome: "conflict_blocked",
|
|
1934
|
+
expectedHead: options.expectedHead,
|
|
1935
|
+
candidateHead: options.candidateHead,
|
|
1936
|
+
quarantineRef: input.quarantineRef,
|
|
1937
|
+
rebaseCount: options.rebaseCount,
|
|
1938
|
+
affectedProjects: affectedProjects(conflictPaths),
|
|
1939
|
+
affectedPaths: reportedPaths(conflictPaths),
|
|
1940
|
+
error: options.error,
|
|
1941
|
+
gitStatus: gitStatus(input)
|
|
1942
|
+
};
|
|
1943
|
+
} catch (error) {
|
|
1944
|
+
runGitResult(input, input.shadowRoot, ["rebase", "--abort"]);
|
|
1945
|
+
return {
|
|
1946
|
+
...observed,
|
|
1947
|
+
outcome: "failed",
|
|
1948
|
+
rebaseCount: options.rebaseCount,
|
|
1949
|
+
affectedProjects: affectedProjects(options.conflictPaths),
|
|
1950
|
+
affectedPaths: reportedPaths(options.conflictPaths),
|
|
1951
|
+
error: `Workspace synchronization found a rebase conflict but could not preserve its durable candidate: ${error instanceof Error ? error.message : String(error)}`,
|
|
1952
|
+
gitStatus: gitStatus(input)
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1831
1956
|
function preserveLargeDiffCandidate(input, observed, error) {
|
|
1832
1957
|
try {
|
|
1833
1958
|
if (stagedPaths(input).length > 0) {
|
|
@@ -1878,6 +2003,10 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1878
2003
|
error: `canonical_head_advanced: expected ${input.expectedCanonicalHead ?? "unborn"}, fetched ${remoteHeadAtStart ?? "unborn"}`
|
|
1879
2004
|
};
|
|
1880
2005
|
}
|
|
2006
|
+
const inProgressRemediation = remediationRebaseInProgressResult(input, result);
|
|
2007
|
+
if (inProgressRemediation) return inProgressRemediation;
|
|
2008
|
+
restoreCleanShadowToExpectedIncidentCandidate(input);
|
|
2009
|
+
const visibleBaselineHead = restoreShadowToVisibleBaseline(input, remoteHeadAtStart);
|
|
1881
2010
|
const inboundConflictsAtStart = input.skipVisibleMirror && !isCanonicalRemediationSync && remoteHeadAtStart ? visibleHydrationConflictPaths(input, hydrationPreimages, `origin/${WORKSPACE_BRANCH}`) : [];
|
|
1882
2011
|
if (input.resetToCanonical) {
|
|
1883
2012
|
if (input.trigger.canonicalCheckoutOnly) {
|
|
@@ -1892,8 +2021,6 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1892
2021
|
gitStatus: gitStatus(input)
|
|
1893
2022
|
};
|
|
1894
2023
|
}
|
|
1895
|
-
const inProgressRemediation = remediationRebaseInProgressResult(input, result);
|
|
1896
|
-
if (inProgressRemediation) return inProgressRemediation;
|
|
1897
2024
|
if (!input.skipVisibleMirror) resetUncommittedShadowSnapshot(input);
|
|
1898
2025
|
restoreUnscopedCanonicalCheckoutSubtrees(input);
|
|
1899
2026
|
if (!input.skipVisibleMirror) {
|
|
@@ -1903,6 +2030,10 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1903
2030
|
mergeGitlinkProjection(mirroredGitlinkProjection, reconcileNewVisibleCheckouts(input));
|
|
1904
2031
|
mirroredGitlinkProjection.opaqueRoots = [...new Set(mirroredGitlinkProjection.opaqueRoots)].sort();
|
|
1905
2032
|
hydrationPreimages = mirroredGitlinkProjection.visiblePreimages ?? hydrationPreimages;
|
|
2033
|
+
if (input.trigger.type === "remediation" && !visibleWorkspaceMatchesProjection(input, mirroredGitlinkProjection)) {
|
|
2034
|
+
resetUncommittedShadowSnapshot(input);
|
|
2035
|
+
throw new Error("visible_workspace_unstable: visible files changed while preparing the remediation snapshot");
|
|
2036
|
+
}
|
|
1906
2037
|
}
|
|
1907
2038
|
const stagePathspecs = mirroredGitlinkProjection.opaqueRoots.flatMap((root) => [`:(exclude,literal)${root}`]);
|
|
1908
2039
|
runGit(input, input.shadowRoot, ["add", "-A", "--", ".", ...stagePathspecs], "stage workspace changes");
|
|
@@ -1911,7 +2042,8 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1911
2042
|
if (redundantInboundCandidate) return redundantInboundCandidate;
|
|
1912
2043
|
const sampledCanonicalCheckoutTreeHash = sampleCanonicalCheckoutTree(input);
|
|
1913
2044
|
const stagedWorkingPaths = stagedPaths(input);
|
|
1914
|
-
const
|
|
2045
|
+
const preservesDivergentVisibleSnapshot = input.confirmedLargeDiff === true || input.trigger.type === "remediation" || input.trigger.type === "remediation_confirm";
|
|
2046
|
+
const baseRevision = preservesDivergentVisibleSnapshot ? remoteHeadAtStart : visibleBaselineHead !== void 0 ? visibleBaselineHead : candidateBaseRevision(input);
|
|
1915
2047
|
const paths = stagedPaths(input, baseRevision);
|
|
1916
2048
|
const diffSizeBytes = await stagedDiffSizeBytes(input, baseRevision);
|
|
1917
2049
|
const statusBeforeCommit = reportedStatus(gitStatus(input));
|
|
@@ -1924,16 +2056,12 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1924
2056
|
...sampledCanonicalCheckoutTreeHash ? { sampledCanonicalCheckoutTreeHash } : {}
|
|
1925
2057
|
};
|
|
1926
2058
|
const destructiveReductions = destructiveCheckoutReductions(input, baseRevision);
|
|
2059
|
+
let blockedDiffError;
|
|
1927
2060
|
if (destructiveReductions.length > 0 && !input.confirmedLargeDiff) {
|
|
1928
2061
|
const summary = destructiveReductions.map(({ root, trackedBefore, trackedAfter }) => `${root} (${trackedBefore} -> ${trackedAfter} tracked files)`).join(", ");
|
|
1929
|
-
|
|
1930
|
-
}
|
|
1931
|
-
|
|
1932
|
-
return preserveLargeDiffCandidate(
|
|
1933
|
-
input,
|
|
1934
|
-
observed,
|
|
1935
|
-
`Workspace safety guard blocked a ${diffSizeBytes}-byte diff above the ${MAX_WORKSPACE_SYNC_DIFF_BYTES}-byte automatic publication limit.`
|
|
1936
|
-
);
|
|
2062
|
+
blockedDiffError = `Workspace safety guard blocked a destructive checkout reduction: ${summary}`;
|
|
2063
|
+
} else if (paths.length > 0 && diffSizeBytes > MAX_WORKSPACE_SYNC_DIFF_BYTES && !input.confirmedLargeDiff && !input.trigger.canonicalCheckoutOnly) {
|
|
2064
|
+
blockedDiffError = `Workspace safety guard blocked a ${diffSizeBytes}-byte diff above the ${MAX_WORKSPACE_SYNC_DIFF_BYTES}-byte automatic publication limit.`;
|
|
1937
2065
|
}
|
|
1938
2066
|
if (stagedWorkingPaths.length > 0) {
|
|
1939
2067
|
runGit(input, input.shadowRoot, ["commit", "-m", commitMessage(input)], "commit workspace changes");
|
|
@@ -1941,8 +2069,11 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
1941
2069
|
let candidateHead = revParse(input, "HEAD");
|
|
1942
2070
|
const candidateHeadBeforeRebase = candidateHead;
|
|
1943
2071
|
if (candidateHead) assertUnscopedCanonicalCheckoutSubtreesMatchRemote(input, candidateHead);
|
|
1944
|
-
const
|
|
1945
|
-
const
|
|
2072
|
+
const remoteHeadBeforeRebase = revParse(input, `origin/${WORKSPACE_BRANCH}`);
|
|
2073
|
+
const hasRemoteMain = Boolean(remoteHeadBeforeRebase);
|
|
2074
|
+
const hasCapturedVisibleDelta = visibleBaselineHead === void 0 || paths.length > 0;
|
|
2075
|
+
const mustDropVisibleBaselinePrefix = !preservesDivergentVisibleSnapshot && visibleBaselineHead !== void 0 && visibleBaselineHead !== remoteHeadBeforeRebase && stagedWorkingPaths.length > 0;
|
|
2076
|
+
const hasUnpushedCommit = candidateHead && hasCapturedVisibleDelta ? mustDropVisibleBaselinePrefix || !hasRemoteMain || !tryGit(input, input.shadowRoot, ["merge-base", "--is-ancestor", candidateHead, `origin/${WORKSPACE_BRANCH}`]) : false;
|
|
1946
2077
|
if (hasUnpushedCommit && inboundConflictsAtStart.length > 0) {
|
|
1947
2078
|
const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...inboundConflictsAtStart])].sort();
|
|
1948
2079
|
return preserveWorkspaceConflictCandidate(input, observed, {
|
|
@@ -2016,13 +2147,36 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
2016
2147
|
};
|
|
2017
2148
|
}
|
|
2018
2149
|
let rebaseCount = 0;
|
|
2019
|
-
const expectedHead =
|
|
2020
|
-
if (
|
|
2021
|
-
|
|
2150
|
+
const expectedHead = remoteHeadBeforeRebase;
|
|
2151
|
+
if (mustDropVisibleBaselinePrefix && !expectedHead) {
|
|
2152
|
+
return {
|
|
2153
|
+
...observed,
|
|
2154
|
+
outcome: "failed",
|
|
2155
|
+
candidateHead: candidateHead ?? void 0,
|
|
2156
|
+
error: "Cannot normalize a visible workspace tail onto an unborn canonical workspace",
|
|
2157
|
+
gitStatus: gitStatus(input)
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
if (candidateHead && expectedHead && (mustDropVisibleBaselinePrefix || !tryGit(input, input.shadowRoot, ["merge-base", "--is-ancestor", expectedHead, candidateHead]))) {
|
|
2161
|
+
const rebase = runGitResult(
|
|
2162
|
+
input,
|
|
2163
|
+
input.shadowRoot,
|
|
2164
|
+
mustDropVisibleBaselinePrefix && visibleBaselineHead ? ["rebase", "--onto", `origin/${WORKSPACE_BRANCH}`, visibleBaselineHead] : ["rebase", `origin/${WORKSPACE_BRANCH}`]
|
|
2165
|
+
);
|
|
2022
2166
|
rebaseCount += 1;
|
|
2023
2167
|
if (rebase.exitCode !== 0) {
|
|
2024
2168
|
const conflicted = runGitResult(input, input.shadowRoot, ["diff", "--name-only", "--diff-filter=U", "-z"]);
|
|
2025
2169
|
const conflictPaths = [.../* @__PURE__ */ new Set([...paths, ...conflicted.stdout.split("\0").filter(Boolean)])].sort();
|
|
2170
|
+
if (mustDropVisibleBaselinePrefix) {
|
|
2171
|
+
const originalCandidateHead = revParse(input, "ORIG_HEAD") ?? candidateHead;
|
|
2172
|
+
return preserveInProgressWorkspaceRebaseConflict(input, observed, {
|
|
2173
|
+
candidateHead: originalCandidateHead,
|
|
2174
|
+
expectedHead,
|
|
2175
|
+
rebaseCount,
|
|
2176
|
+
conflictPaths,
|
|
2177
|
+
error: `Workspace tail conflicts with canonical head ${expectedHead}; local changes were preserved for reconciliation in the in-progress rebase.`
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
2026
2180
|
runGit(input, input.shadowRoot, ["rebase", "--abort"], "preserve workspace candidate after rebase conflict");
|
|
2027
2181
|
return preserveWorkspaceConflictCandidate(input, observed, {
|
|
2028
2182
|
baseRevision: candidateHead,
|
|
@@ -2068,6 +2222,9 @@ async function synchronizeWorkspace(rawInput) {
|
|
|
2068
2222
|
error: "Workspace changed while its candidate was reconciling; visible changes and the old-base candidate were preserved."
|
|
2069
2223
|
});
|
|
2070
2224
|
}
|
|
2225
|
+
if (blockedDiffError) {
|
|
2226
|
+
return preserveLargeDiffCandidate(input, { ...observed, rebaseCount }, blockedDiffError);
|
|
2227
|
+
}
|
|
2071
2228
|
const upload = uploadWorkspaceCandidate(input, candidateHead);
|
|
2072
2229
|
if (upload.exitCode !== 0) {
|
|
2073
2230
|
return {
|
|
@@ -46,7 +46,9 @@ export declare class ExplicitWorkspacePublicationQueue<TTrigger> {
|
|
|
46
46
|
enqueue(publication: ExplicitWorkspacePublication<TTrigger>, pendingRequestId?: string): void;
|
|
47
47
|
next(): ExplicitWorkspacePublication<TTrigger> | undefined;
|
|
48
48
|
}
|
|
49
|
-
type PersistedWorkspaceConvergenceState = Omit<WorkspaceConvergenceSnapshot, "publicationState"
|
|
49
|
+
type PersistedWorkspaceConvergenceState = Omit<WorkspaceConvergenceSnapshot, "publicationState"> & {
|
|
50
|
+
visibleBaselineHead: string | null;
|
|
51
|
+
};
|
|
50
52
|
export declare function readWorkspaceConvergenceState(statePath: string): PersistedWorkspaceConvergenceState;
|
|
51
53
|
export declare class WorkspaceConvergenceState {
|
|
52
54
|
private readonly statePath;
|
|
@@ -54,7 +56,9 @@ export declare class WorkspaceConvergenceState {
|
|
|
54
56
|
private publicationState;
|
|
55
57
|
constructor(statePath: string);
|
|
56
58
|
snapshot(): WorkspaceConvergenceSnapshot;
|
|
59
|
+
visibleBaselineHead(): string | null;
|
|
57
60
|
setLocalHead(localHead: string | null): void;
|
|
61
|
+
setVisibleBaselineHead(visibleBaselineHead: string | null): void;
|
|
58
62
|
observeCanonicalHead(canonicalHead: string | null): boolean;
|
|
59
63
|
markDirty(): number;
|
|
60
64
|
schedule(): void;
|
|
@@ -73,6 +77,13 @@ export declare class WorkspaceConvergenceState {
|
|
|
73
77
|
private persist;
|
|
74
78
|
}
|
|
75
79
|
export declare function processWorkspaceConvergenceState(statePath: string): WorkspaceConvergenceState;
|
|
80
|
+
export declare function workspaceBootstrapEstablishesVisibleBaseline(input: {
|
|
81
|
+
hydrated: boolean;
|
|
82
|
+
firstBootstrap: boolean;
|
|
83
|
+
incidentActive: boolean;
|
|
84
|
+
currentLocalHead: string | null;
|
|
85
|
+
currentVisibleBaselineHead: string | null;
|
|
86
|
+
}): boolean;
|
|
76
87
|
export declare function waitForWorkspacePublicationOnShutdown(input: {
|
|
77
88
|
snapshot: () => WorkspaceConvergenceSnapshot;
|
|
78
89
|
forcePublication: () => void;
|
|
@@ -78,6 +78,15 @@ export type WorkspaceSyncInput = {
|
|
|
78
78
|
quarantineRef?: string;
|
|
79
79
|
/** Exact canonical head fenced by publication authority. */
|
|
80
80
|
expectedCanonicalHead?: string | null;
|
|
81
|
+
/** Elected incident candidate pinned only while preparing remediation. */
|
|
82
|
+
expectedIncidentCandidateHead?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Commit whose tree the visible workspace most recently reflected.
|
|
85
|
+
* Exact remediation may advance the disposable shadow checkout without
|
|
86
|
+
* hydrating visible files. A later visible snapshot must therefore rebuild
|
|
87
|
+
* its delta from this durable baseline before rebasing onto canonical main.
|
|
88
|
+
*/
|
|
89
|
+
visibleBaselineHead?: string | null;
|
|
81
90
|
confirmedLargeDiff?: boolean;
|
|
82
91
|
confirmationReason?: string;
|
|
83
92
|
resetToCanonical?: boolean;
|