@astrosheep/keiyaku 2.9.12 → 2.9.13
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/README.md +22 -18
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +1 -0
- package/build/agents/providers/claude-agent-sdk/adapter.js +2 -2
- package/build/cli/commands/akuma/akuma/handler.js +2 -1
- package/build/cli/commands/akuma/akuma/meta.js +4 -3
- package/build/cli/commands/akuma/catalog.js +2 -0
- package/build/cli/commands/akuma/list/handler.js +1 -1
- package/build/cli/commands/akuma/list/meta-list.js +12 -0
- package/build/cli/commands/akuma/list/meta-ls.js +2 -2
- package/build/cli/commands/akuma.js +5 -0
- package/build/cli/commands/contract/amend/meta.js +4 -3
- package/build/cli/commands/contract/arc/meta.js +2 -0
- package/build/cli/commands/contract/audit/handler.js +28 -2
- package/build/cli/commands/contract/audit/meta.js +4 -3
- package/build/cli/commands/contract/petition/handler.js +2 -1
- package/build/cli/commands/contract/petition/meta.js +2 -2
- package/build/cli/commands/contract/renew/handler.js +44 -3
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +12 -9
- package/build/cli/commands/projection/call/handler.js +1 -1
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -2
- package/build/cli/commands/projection/{revive → fork}/handler.js +10 -5
- package/build/cli/commands/projection/fork/meta.js +12 -0
- package/build/cli/commands/projection/history/handler.js +46 -2
- package/build/cli/commands/projection/history/meta.js +3 -3
- package/build/cli/commands/projection/kill/meta.js +2 -2
- package/build/cli/commands/projection/status/handler.js +29 -13
- package/build/cli/commands/projection/status/meta.js +14 -3
- package/build/cli/commands/projection/tell/handler.js +8 -4
- package/build/cli/commands/projection/tell/meta.js +1 -1
- package/build/cli/commands/projection/wait/handler.js +45 -20
- package/build/cli/commands/projection/wait/meta.js +1 -1
- package/build/cli/commands/shared.js +1 -1
- package/build/cli/commands/system/catalog.js +2 -0
- package/build/cli/commands/task/add/handler.js +28 -12
- package/build/cli/commands/task/add/meta.js +18 -6
- package/build/cli/commands/task/catalog.js +2 -0
- package/build/cli/commands/task/compose/handler.js +2 -2
- package/build/cli/commands/task/compose/meta.js +2 -2
- package/build/cli/commands/task/doctor/handler.js +2 -3
- package/build/cli/commands/task/doctor/meta.js +2 -2
- package/build/cli/commands/task/done/meta.js +2 -2
- package/build/cli/commands/task/drop/handler.js +7 -2
- package/build/cli/commands/task/drop/meta.js +6 -3
- package/build/cli/commands/task/hold/meta.js +2 -2
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/ls/meta.js +2 -2
- package/build/cli/commands/task/note/handler.js +6 -0
- package/build/cli/commands/task/note/meta.js +11 -0
- package/build/cli/commands/task/resume/meta.js +2 -2
- package/build/cli/commands/task/shared.js +29 -15
- package/build/cli/commands/task/show/meta.js +2 -2
- package/build/cli/commands/task/start/meta.js +2 -2
- package/build/cli/commands/task/stop/meta.js +2 -2
- package/build/cli/commands/task/update/meta.js +3 -2
- package/build/cli/commands/verification/handler.js +43 -0
- package/build/cli/commands/verification/meta.js +13 -0
- package/build/cli/completion.js +16 -13
- package/build/cli/flags.js +43 -11
- package/build/cli/help.js +29 -30
- package/build/cli/index.js +6 -5
- package/build/cli/parse-flags.js +39 -0
- package/build/cli/parse-metadata.js +5 -2
- package/build/cli/parse-selectors.js +18 -0
- package/build/cli/parse.js +21 -8
- package/build/cli/render/arc.js +12 -3
- package/build/cli/render/audit.js +2 -2
- package/build/cli/render/call.js +4 -4
- package/build/cli/render/kanshi.js +122 -85
- package/build/cli/render/misc.js +2 -2
- package/build/cli/render/petition.js +16 -1
- package/build/cli/render/projection-history.js +17 -4
- package/build/cli/render/shared.js +5 -5
- package/build/cli/render/status-indicator.js +47 -0
- package/build/cli/render/status.js +106 -76
- package/build/cli/render/success-response.js +20 -5
- package/build/cli/render/task.js +6 -5
- package/build/cli/render/tell.js +10 -4
- package/build/cli/render/tool-command-normalization.js +137 -0
- package/build/cli/render/tool-presentation.js +2 -1
- package/build/cli/render/verification.js +23 -0
- package/build/cli/render/wait.js +101 -33
- package/build/cli/types.js +2 -1
- package/build/config/akuma-loader.js +21 -1
- package/build/config/settings/knobs.js +7 -1
- package/build/config/settings/schema.js +10 -5
- package/build/core/amend.js +102 -17
- package/build/core/arc.js +4 -4
- package/build/core/audit/candidate.js +10 -18
- package/build/core/audit/coordinates.js +12 -17
- package/build/core/audit/evidence.js +6 -4
- package/build/core/audit/facade.js +3 -3
- package/build/core/audit/report.js +1 -1
- package/build/core/bind-reconciliation.js +42 -23
- package/build/core/bind.js +182 -81
- package/build/core/call/call.js +6 -7
- package/build/core/call/context.js +43 -30
- package/build/core/call/execution.js +21 -6
- package/build/core/call/prompt.js +11 -42
- package/build/core/contract-carrier-runtime.js +209 -105
- package/build/core/contract-carrier.js +156 -73
- package/build/core/contract-view.js +42 -5
- package/build/core/contract.js +2 -1
- package/build/core/derived-replay.js +168 -35
- package/build/core/draft.js +9 -5
- package/build/core/forfeit.js +1 -1
- package/build/core/lifecycle-history.js +13 -0
- package/build/core/lifecycle-recovery.js +32 -27
- package/build/core/lifecycle-runner.js +14 -14
- package/build/core/log.js +1 -1
- package/build/core/outcome-base.js +2 -2
- package/build/core/projection/akuma-name.js +43 -0
- package/build/core/projection/generation/database.js +141 -79
- package/build/core/projection/generation/ledger.js +91 -0
- package/build/core/projection/generation/model.js +18 -13
- package/build/core/projection/generation/projection-generation-continuation.js +1 -1
- package/build/core/projection/generation/projection-generation-doorbell.js +88 -0
- package/build/core/projection/generation/projection-generation-execution.js +24 -0
- package/build/core/projection/generation/projection-generation-identity.js +4 -0
- package/build/core/projection/generation/projection-generation-launcher.js +72 -138
- package/build/core/projection/generation/projection-generation-process.js +16 -15
- package/build/core/projection/generation/projection-generation-runner.js +125 -79
- package/build/core/projection/generation/projection-generation-runtime.js +10 -61
- package/build/core/projection/generation/protocol.js +68 -0
- package/build/core/projection/generation/store.js +17 -7
- package/build/core/projection/generation/transitions.js +14 -14
- package/build/core/projection/index.js +5 -5
- package/build/core/projection/projection-core.js +6 -2
- package/build/core/projection/projection-execution-observer.js +1 -1
- package/build/core/projection/projection-history.js +7 -7
- package/build/core/projection/projection-kill.js +15 -6
- package/build/core/projection/projection-life-observer.js +8 -8
- package/build/core/projection/projection-mint.js +6 -8
- package/build/core/projection/projection-status-observation.js +77 -8
- package/build/core/projection/projection-status.js +151 -4
- package/build/core/projection/projection-wait.js +46 -27
- package/build/core/projection/projection-wake.js +27 -89
- package/build/core/projection/tell/launch-store.js +1 -1
- package/build/core/projection/tell/store.js +1 -1
- package/build/core/renew-build.js +204 -74
- package/build/core/renew-plan.js +34 -9
- package/build/core/renew-rewrite.js +56 -15
- package/build/core/renew.js +39 -35
- package/build/core/repository-ledger/accepted-fold-read.js +6 -3
- package/build/core/repository-ledger/codec.js +11 -5
- package/build/core/repository-ledger/current-state-store.js +13 -16
- package/build/core/repository-ledger/fold-repository.js +5 -6
- package/build/core/repository-ledger/identity.js +60 -0
- package/build/core/repository-ledger/inventory.js +3 -3
- package/build/core/repository-ledger/read-model.js +54 -6
- package/build/core/repository-ledger/write-transaction.js +9 -4
- package/build/core/run-control/connection-bound-close.js +41 -0
- package/build/core/run-control/detached-lease-bootstrap.js +208 -0
- package/build/core/run-control/detached-process.js +14 -0
- package/build/core/run-control/index.js +6 -0
- package/build/core/{process-group.js → run-control/process-tree.js} +1 -1
- package/build/core/run-control/runner-lease.js +172 -0
- package/build/core/{sqlite-process-lifetime-lock.js → run-control/sqlite-process-lifetime-lock.js} +4 -24
- package/build/core/seal.js +44 -32
- package/build/core/settlement/claim-delivery.js +22 -9
- package/build/core/settlement/claim.js +8 -6
- package/build/core/settlement/petition-claim-gates.js +9 -8
- package/build/core/settlement/petition-head-guard.js +5 -6
- package/build/core/settlement/petition-preview.js +21 -5
- package/build/core/settlement/petition.js +51 -17
- package/build/core/settlement/settlement.js +20 -15
- package/build/core/settlement/verdict.js +2 -2
- package/build/core/settlement/verification.js +492 -364
- package/build/core/status/board.js +184 -75
- package/build/core/status/drift.js +8 -10
- package/build/core/task/board.js +1 -0
- package/build/core/task/commands.js +25 -0
- package/build/core/task/compose.js +3 -2
- package/build/core/task/document.js +102 -39
- package/build/core/task/index.js +3 -3
- package/build/core/task/settlement-git.js +1 -4
- package/build/core/task/settlement-policy.js +27 -14
- package/build/core/task/source-board.js +1 -0
- package/build/core/task/task-bind-preparation.js +59 -9
- package/build/core/task/task-contract.js +2 -2
- package/build/core/task/task-store-repository.js +1 -0
- package/build/core/task/task.js +2 -2
- package/build/core/transcripts.js +4 -4
- package/build/core/verification-declaration.js +95 -0
- package/build/core/worktree-bootstrap.js +1 -1
- package/build/core/worktree-path.js +7 -11
- package/build/flow-error.js +9 -1
- package/build/generated/version.js +2 -2
- package/build/git/branches.js +41 -70
- package/build/git/commits.js +92 -21
- package/build/git/core.js +9 -161
- package/build/git/diff/preview.js +4 -4
- package/build/git/diff/read.js +4 -4
- package/build/git/diff/structured.js +5 -5
- package/build/git/process.js +229 -0
- package/build/git/refs.js +30 -5
- package/build/git/staging.js +39 -115
- package/build/git/streaming-batch.js +9 -16
- package/build/git/worktree.js +104 -91
- package/build/index.js +7 -0
- package/package.json +1 -1
- package/skills/keiyaku-akuma/SKILL.md +21 -20
- package/skills/keiyaku-task/SKILL.md +38 -4
- package/skills/keiyaku-workflow/SKILL.md +51 -11
- package/build/cli/commands/projection/revive/meta.js +0 -12
- package/build/core/projection/projection-life-protocol.js +0 -115
- package/build/core/projection/projection-runner-lock.js +0 -211
- package/build/core/projection/tell/database.js +0 -127
- package/build/core/settlement/verification-coordination.js +0 -305
- package/build/core/settlement/verification-supervisor.js +0 -275
|
@@ -3,7 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { assertHostObjectId, deriveHostGitCoordinates, readCommitObject } from "./repository-ledger/git-object-store.js";
|
|
4
4
|
import { runGitProcess, runGitProcessBytes } from "../git/core.js";
|
|
5
5
|
import { stableRepoRoot } from "./worktree-path.js";
|
|
6
|
-
const SCRATCH_VERSION =
|
|
6
|
+
const SCRATCH_VERSION = 3;
|
|
7
7
|
const SCRATCH_DIRECTORY = path.join("keiyaku", "renew-scratch");
|
|
8
8
|
const SCRATCH_STATE_FILE = "state.json";
|
|
9
9
|
const SCRATCH_FILES_DIRECTORY = "files";
|
|
@@ -136,11 +136,31 @@ async function mergeCommitTree(input) {
|
|
|
136
136
|
await assertTree(input.cwd, input.coordinates, tree);
|
|
137
137
|
if (result.status === 0)
|
|
138
138
|
return { status: "clean", tree };
|
|
139
|
-
const
|
|
140
|
-
const match =
|
|
141
|
-
return match ? [match[1]] : [];
|
|
142
|
-
})
|
|
143
|
-
|
|
139
|
+
const index = lines.flatMap((line) => {
|
|
140
|
+
const match = /^(\d{6}) ([0-9a-f]+) ([123])\t(.+)$/.exec(line);
|
|
141
|
+
return match ? [{ mode: match[1], objectId: match[2], stage: Number(match[3]), path: match[4] }] : [];
|
|
142
|
+
});
|
|
143
|
+
const paths = Array.from(new Set(index.map((entry) => entry.path)));
|
|
144
|
+
return { status: "conflict", tree, paths, index };
|
|
145
|
+
}
|
|
146
|
+
/** Replay a resolved delivery tree from its captured target onto a later target. */
|
|
147
|
+
export async function replayResolvedDelivery(input) {
|
|
148
|
+
for (const commit of [input.capturedTarget, input.latestTarget]) {
|
|
149
|
+
assertHostObjectId(input.coordinates, commit);
|
|
150
|
+
await readCommitObject(input.cwd, input.coordinates, commit);
|
|
151
|
+
}
|
|
152
|
+
const resolvedCommit = await buildCommit(input.cwd, input.coordinates, input.identity, input.resolvedTree, input.capturedTarget);
|
|
153
|
+
const replay = await mergeCommitTree({
|
|
154
|
+
cwd: input.cwd,
|
|
155
|
+
coordinates: input.coordinates,
|
|
156
|
+
original: {
|
|
157
|
+
...input.identity,
|
|
158
|
+
objectId: resolvedCommit,
|
|
159
|
+
parent: input.capturedTarget,
|
|
160
|
+
},
|
|
161
|
+
currentHead: input.latestTarget,
|
|
162
|
+
});
|
|
163
|
+
return { ...replay, resolvedCommit };
|
|
144
164
|
}
|
|
145
165
|
export async function buildCommit(cwd, coordinates, original, tree, parent) {
|
|
146
166
|
await assertTree(cwd, coordinates, tree);
|
|
@@ -170,41 +190,64 @@ export function interrupted(signal) {
|
|
|
170
190
|
});
|
|
171
191
|
}
|
|
172
192
|
}
|
|
173
|
-
async function materializeConflict(cwd, scratch,
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
193
|
+
async function materializeConflict(cwd, scratch, state, create) {
|
|
194
|
+
if (create) {
|
|
195
|
+
await successfulGit(cwd, ["worktree", "add", "--detach", scratch.files, state.currentHead]);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
await validateScratchWorktree(cwd, scratch, state, { requireRecordedHead: false });
|
|
199
|
+
await successfulGit(scratch.files, ["reset", "--hard", state.currentHead]);
|
|
200
|
+
await successfulGit(scratch.files, ["clean", "-ffdx"]);
|
|
201
|
+
}
|
|
202
|
+
await successfulGit(scratch.files, ["read-tree", "--reset", "-u", state.conflictTree]);
|
|
203
|
+
await successfulGit(scratch.files, [
|
|
204
|
+
"update-index",
|
|
205
|
+
"--force-remove",
|
|
206
|
+
"--",
|
|
207
|
+
...Array.from(new Set(state.conflictIndex.map((entry) => entry.path))),
|
|
208
|
+
]);
|
|
209
|
+
await successfulGit(scratch.files, ["update-index", "--index-info"], { input: state.conflictIndex.map((entry) => `${entry.mode} ${entry.objectId} ${entry.stage}\t${entry.path}\n`).join("") });
|
|
210
|
+
}
|
|
211
|
+
async function scratchCommonDirectory(cwd) {
|
|
212
|
+
const commonDirectory = (await successfulGit(cwd, [
|
|
213
|
+
"rev-parse",
|
|
214
|
+
"--path-format=absolute",
|
|
215
|
+
"--git-common-dir",
|
|
216
|
+
])).trim();
|
|
217
|
+
return await fs.realpath(commonDirectory);
|
|
218
|
+
}
|
|
219
|
+
async function validateScratchWorktree(cwd, scratch, state, options) {
|
|
220
|
+
let filesPath;
|
|
221
|
+
try {
|
|
222
|
+
filesPath = await fs.realpath(scratch.files);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
throw new RenewBuildError("INVALID_RENEW_SCRATCH", `renew scratch ${state.scratchName} has no linked worktree`, { cause: error });
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
const [topLevel, commonDirectory] = await Promise.all([
|
|
229
|
+
successfulGit(filesPath, ["rev-parse", "--show-toplevel"]),
|
|
230
|
+
scratchCommonDirectory(filesPath),
|
|
231
|
+
]);
|
|
232
|
+
if (path.resolve(topLevel.trim()) !== filesPath || commonDirectory !== await scratchCommonDirectory(cwd)) {
|
|
233
|
+
throw new RenewBuildError("INVALID_RENEW_SCRATCH", `renew scratch ${state.scratchName} is not the recorded linked worktree`);
|
|
200
234
|
}
|
|
201
|
-
|
|
202
|
-
|
|
235
|
+
const symbolicHead = await runGitProcess(filesPath, ["symbolic-ref", "-q", "HEAD"]);
|
|
236
|
+
if (symbolicHead.status === 0 || symbolicHead.status !== 1 || symbolicHead.error) {
|
|
237
|
+
throw new RenewBuildError("INVALID_RENEW_SCRATCH", `renew scratch ${state.scratchName} must retain a detached HEAD`);
|
|
203
238
|
}
|
|
204
|
-
|
|
205
|
-
await
|
|
239
|
+
if (options.requireRecordedHead) {
|
|
240
|
+
const head = (await successfulGit(filesPath, ["rev-parse", "HEAD"])).trim();
|
|
241
|
+
if (head !== state.currentHead) {
|
|
242
|
+
throw new RenewBuildError("INVALID_RENEW_SCRATCH", `renew scratch ${state.scratchName} HEAD no longer matches the recorded replay head`);
|
|
243
|
+
}
|
|
206
244
|
}
|
|
207
245
|
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
if (error instanceof RenewBuildError)
|
|
248
|
+
throw error;
|
|
249
|
+
throw new RenewBuildError("INVALID_RENEW_SCRATCH", `renew scratch ${state.scratchName} has invalid linked worktree metadata`, { cause: error });
|
|
250
|
+
}
|
|
208
251
|
}
|
|
209
252
|
async function writeScratchState(scratch, state) {
|
|
210
253
|
const temporary = `${scratch.state}.next`;
|
|
@@ -212,9 +255,40 @@ async function writeScratchState(scratch, state) {
|
|
|
212
255
|
await fs.writeFile(temporary, `${JSON.stringify(state, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
|
|
213
256
|
await fs.rename(temporary, scratch.state);
|
|
214
257
|
}
|
|
258
|
+
async function hasRegisteredScratchWorktree(cwd, scratch) {
|
|
259
|
+
const worktrees = await successfulGit(cwd, ["worktree", "list", "--porcelain", "-z"]);
|
|
260
|
+
const expected = path.resolve(scratch.files);
|
|
261
|
+
return worktrees.split("\0").some((field) => field.startsWith("worktree ") && path.resolve(field.slice("worktree ".length)) === expected);
|
|
262
|
+
}
|
|
263
|
+
async function scratchRootExists(scratch) {
|
|
264
|
+
try {
|
|
265
|
+
await fs.lstat(scratch.root);
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
if (error.code === "ENOENT")
|
|
270
|
+
return false;
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/** Remove only the deterministic scratch root and its exact Git registration. */
|
|
275
|
+
export async function cleanupRenewScratch(input) {
|
|
276
|
+
const [registered, rootExists] = await Promise.all([
|
|
277
|
+
hasRegisteredScratchWorktree(input.cwd, input.scratch),
|
|
278
|
+
scratchRootExists(input.scratch),
|
|
279
|
+
]);
|
|
280
|
+
if (!registered && !rootExists)
|
|
281
|
+
return { status: "absent" };
|
|
282
|
+
if (registered) {
|
|
283
|
+
await successfulGit(input.cwd, ["worktree", "remove", "--force", input.scratch.files]);
|
|
284
|
+
}
|
|
285
|
+
await fs.rm(input.scratch.root, { recursive: true, force: true });
|
|
286
|
+
return { status: "deleted" };
|
|
287
|
+
}
|
|
215
288
|
export function evidenceResult(state) {
|
|
216
289
|
return {
|
|
217
290
|
observedLedgerHead: state.observedLedgerHead,
|
|
291
|
+
lifecycleHead: state.lifecycleHead,
|
|
218
292
|
deliveryRef: state.deliveryRef,
|
|
219
293
|
targetRef: state.targetRef,
|
|
220
294
|
effectiveScope: [...state.effectiveScope],
|
|
@@ -225,8 +299,9 @@ export function evidenceResult(state) {
|
|
|
225
299
|
evidenceMap: { ...state.evidenceMap },
|
|
226
300
|
};
|
|
227
301
|
}
|
|
228
|
-
async function persistConflict(cwd, scratchBase, state, create) {
|
|
302
|
+
export async function persistConflict(cwd, scratchBase, state, create) {
|
|
229
303
|
const scratch = scratchPaths(scratchBase, state.scratchName);
|
|
304
|
+
let previous;
|
|
230
305
|
if (create) {
|
|
231
306
|
await fs.mkdir(path.dirname(scratch.root), { recursive: true });
|
|
232
307
|
try {
|
|
@@ -239,8 +314,27 @@ async function persistConflict(cwd, scratchBase, state, create) {
|
|
|
239
314
|
throw error;
|
|
240
315
|
}
|
|
241
316
|
}
|
|
242
|
-
|
|
243
|
-
|
|
317
|
+
else {
|
|
318
|
+
previous = await readScratchState(scratch);
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
await materializeConflict(cwd, scratch, state, create);
|
|
322
|
+
await writeScratchState(scratch, state);
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
if (create) {
|
|
326
|
+
await cleanupRenewScratch({ cwd, scratch });
|
|
327
|
+
}
|
|
328
|
+
else if (previous) {
|
|
329
|
+
try {
|
|
330
|
+
await materializeConflict(cwd, scratch, previous, false);
|
|
331
|
+
}
|
|
332
|
+
catch (restoreError) {
|
|
333
|
+
throw new RenewBuildError("INVALID_RENEW_SCRATCH", `renew scratch ${state.scratchName} could not restore its prior conflict state`, { cause: restoreError instanceof Error ? restoreError : undefined });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
throw error;
|
|
337
|
+
}
|
|
244
338
|
return {
|
|
245
339
|
status: "conflict",
|
|
246
340
|
...evidenceResult(state),
|
|
@@ -270,6 +364,7 @@ export async function replayFrom(input) {
|
|
|
270
364
|
conflictCommit: original.objectId,
|
|
271
365
|
conflictTree: merged.tree,
|
|
272
366
|
conflictPaths: merged.paths,
|
|
367
|
+
conflictIndex: merged.index,
|
|
273
368
|
};
|
|
274
369
|
return await persistConflict(input.cwd, input.scratchBase, state, input.createScratch);
|
|
275
370
|
}
|
|
@@ -292,38 +387,54 @@ export async function buildDetachedRenewChain(input) {
|
|
|
292
387
|
const coordinates = await deriveHostGitCoordinates(repositoryRoot);
|
|
293
388
|
const commits = await validateCoordinates({ ...input, cwd: repositoryRoot, coordinates });
|
|
294
389
|
const existingScratch = scratchPaths(scratchBase, input.scratchName);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
390
|
+
const hasExistingScratch = await Promise.all([
|
|
391
|
+
scratchRootExists(existingScratch),
|
|
392
|
+
hasRegisteredScratchWorktree(repositoryRoot, existingScratch),
|
|
393
|
+
]).then(([rootExists, registered]) => rootExists || registered);
|
|
394
|
+
if (hasExistingScratch) {
|
|
395
|
+
let state;
|
|
396
|
+
try {
|
|
397
|
+
state = await readScratchState(existingScratch);
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
if (!(error instanceof RenewBuildError)
|
|
401
|
+
|| (error.code !== "RENEW_SCRATCH_NOT_FOUND" && error.code !== "INVALID_RENEW_SCRATCH")) {
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
await cleanupRenewScratch({ cwd: repositoryRoot, scratch: existingScratch });
|
|
405
|
+
}
|
|
406
|
+
if (state) {
|
|
407
|
+
if (state.observedLedgerHead !== input.observedLedgerHead
|
|
408
|
+
|| state.lifecycleHead !== input.lifecycleHead
|
|
409
|
+
|| state.deliveryRef !== input.deliveryRef
|
|
410
|
+
|| state.targetRef !== input.targetRef
|
|
411
|
+
|| state.oldBase !== input.oldBase
|
|
412
|
+
|| state.oldHead !== input.oldHead
|
|
413
|
+
|| state.newBase !== input.newBase
|
|
414
|
+
|| JSON.stringify(state.effectiveScope) !== JSON.stringify(input.effectiveScope)
|
|
415
|
+
|| JSON.stringify(state.deliveryCommits) !== JSON.stringify(input.deliveryCommits)) {
|
|
416
|
+
await cleanupRenewScratch({ cwd: repositoryRoot, scratch: existingScratch });
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
await validateScratchProgress(repositoryRoot, coordinates, state, commits);
|
|
420
|
+
await validateScratchWorktree(repositoryRoot, existingScratch, state, { requireRecordedHead: true });
|
|
421
|
+
return {
|
|
422
|
+
status: "conflict",
|
|
423
|
+
...evidenceResult(state),
|
|
424
|
+
scratchName: state.scratchName,
|
|
425
|
+
scratchPath: existingScratch.root,
|
|
426
|
+
filesPath: existingScratch.files,
|
|
427
|
+
conflictCommit: state.conflictCommit,
|
|
428
|
+
conflictPaths: [...state.conflictPaths],
|
|
429
|
+
};
|
|
430
|
+
}
|
|
307
431
|
}
|
|
308
|
-
await validateScratchProgress(repositoryRoot, coordinates, state, commits);
|
|
309
|
-
return {
|
|
310
|
-
status: "conflict",
|
|
311
|
-
...evidenceResult(state),
|
|
312
|
-
scratchName: state.scratchName,
|
|
313
|
-
scratchPath: existingScratch.root,
|
|
314
|
-
filesPath: existingScratch.files,
|
|
315
|
-
conflictCommit: state.conflictCommit,
|
|
316
|
-
conflictPaths: [...state.conflictPaths],
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
catch (error) {
|
|
320
|
-
if (error.code !== "ENOENT")
|
|
321
|
-
throw error;
|
|
322
432
|
}
|
|
323
433
|
const state = {
|
|
324
434
|
version: SCRATCH_VERSION,
|
|
325
435
|
scratchName: input.scratchName,
|
|
326
436
|
observedLedgerHead: input.observedLedgerHead,
|
|
437
|
+
lifecycleHead: input.lifecycleHead,
|
|
327
438
|
deliveryRef: input.deliveryRef,
|
|
328
439
|
targetRef: input.targetRef,
|
|
329
440
|
effectiveScope: [...input.effectiveScope],
|
|
@@ -337,6 +448,7 @@ export async function buildDetachedRenewChain(input) {
|
|
|
337
448
|
conflictCommit: "",
|
|
338
449
|
conflictTree: "",
|
|
339
450
|
conflictPaths: [],
|
|
451
|
+
conflictIndex: [],
|
|
340
452
|
};
|
|
341
453
|
return await replayFrom({
|
|
342
454
|
cwd: repositoryRoot,
|
|
@@ -366,6 +478,7 @@ export async function readScratchState(scratch) {
|
|
|
366
478
|
|| parsed.version !== SCRATCH_VERSION
|
|
367
479
|
|| parsed.scratchName !== path.basename(scratch.root)
|
|
368
480
|
|| typeof parsed.observedLedgerHead !== "string"
|
|
481
|
+
|| typeof parsed.lifecycleHead !== "string"
|
|
369
482
|
|| typeof parsed.deliveryRef !== "string"
|
|
370
483
|
|| typeof parsed.targetRef !== "string"
|
|
371
484
|
|| !Array.isArray(parsed.effectiveScope)
|
|
@@ -382,30 +495,47 @@ export async function readScratchState(scratch) {
|
|
|
382
495
|
|| typeof parsed.conflictCommit !== "string"
|
|
383
496
|
|| typeof parsed.conflictTree !== "string"
|
|
384
497
|
|| !Array.isArray(parsed.conflictPaths)
|
|
385
|
-
|| parsed.conflictPaths.some((value) => typeof value !== "string")
|
|
498
|
+
|| parsed.conflictPaths.some((value) => typeof value !== "string")
|
|
499
|
+
|| !Array.isArray(parsed.conflictIndex)
|
|
500
|
+
|| parsed.conflictIndex.some((entry) => !isRecord(entry)
|
|
501
|
+
|| !/^\d{6}$/.test(String(entry.mode))
|
|
502
|
+
|| !/^[0-9a-f]+$/.test(String(entry.objectId))
|
|
503
|
+
|| ![1, 2, 3].includes(entry.stage)
|
|
504
|
+
|| typeof entry.path !== "string")) {
|
|
386
505
|
throw new RenewBuildError("INVALID_RENEW_SCRATCH", `renew scratch ${path.basename(scratch.root)} has invalid state`);
|
|
387
506
|
}
|
|
388
507
|
return parsed;
|
|
389
508
|
}
|
|
390
509
|
export async function treeFromScratchFiles(cwd, coordinates, scratch, state) {
|
|
391
|
-
|
|
392
|
-
await
|
|
393
|
-
|
|
510
|
+
await validateScratchWorktree(cwd, scratch, state, { requireRecordedHead: true });
|
|
511
|
+
const indexPath = (await successfulGit(scratch.files, [
|
|
512
|
+
"rev-parse",
|
|
513
|
+
"--path-format=absolute",
|
|
514
|
+
"--git-path",
|
|
515
|
+
"index",
|
|
516
|
+
])).trim();
|
|
517
|
+
const temporaryDirectory = await fs.mkdtemp(path.join(scratch.root, "tree-index-"));
|
|
518
|
+
const temporaryIndex = path.join(temporaryDirectory, "index");
|
|
394
519
|
try {
|
|
395
|
-
|
|
396
|
-
await
|
|
397
|
-
const
|
|
520
|
+
// Candidate construction must not stage or otherwise alter the retryable scratch.
|
|
521
|
+
await fs.copyFile(indexPath, temporaryIndex);
|
|
522
|
+
const env = { ...process.env, GIT_INDEX_FILE: temporaryIndex };
|
|
523
|
+
await successfulGit(scratch.files, ["add", "--all", "--", "."], { env });
|
|
524
|
+
const tree = (await successfulGit(scratch.files, ["write-tree"], { env })).trim();
|
|
398
525
|
await assertTree(cwd, coordinates, tree);
|
|
399
526
|
return tree;
|
|
400
527
|
}
|
|
401
528
|
finally {
|
|
402
|
-
await fs.rm(
|
|
529
|
+
await fs.rm(temporaryDirectory, { recursive: true, force: true });
|
|
403
530
|
}
|
|
404
531
|
}
|
|
405
532
|
export async function validateScratchProgress(cwd, coordinates, state, commits) {
|
|
406
533
|
if (state.nextIndex < 0 || state.nextIndex >= commits.length || commits[state.nextIndex]?.objectId !== state.conflictCommit) {
|
|
407
534
|
throw new RenewBuildError("INVALID_RENEW_SCRATCH", "renew scratch does not identify the stopped delivery commit");
|
|
408
535
|
}
|
|
536
|
+
if (state.conflictPaths.length === 0 || state.conflictIndex.length === 0) {
|
|
537
|
+
throw new RenewBuildError("INVALID_RENEW_SCRATCH", "renew scratch has no captured conflict index");
|
|
538
|
+
}
|
|
409
539
|
let parent = state.newBase;
|
|
410
540
|
for (let index = 0; index < state.nextIndex; index += 1) {
|
|
411
541
|
const original = commits[index].objectId;
|
package/build/core/renew-plan.js
CHANGED
|
@@ -2,6 +2,7 @@ import { FlowError } from "../flow-error.js";
|
|
|
2
2
|
import { currentLifecycleState } from "./repository-ledger/read-model.js";
|
|
3
3
|
import { buildDetachedRenewChain, continueDetachedRenewChain, renewScratchName } from "./renew-rewrite.js";
|
|
4
4
|
import { hasStrandableProjectionGeneration } from "./projection/index.js";
|
|
5
|
+
import { readCommitCoordinate } from "../git/commits.js";
|
|
5
6
|
import { runGitProcess } from "../git/core.js";
|
|
6
7
|
import { assertCleanTrackedWorkingTree } from "../git/worktree.js";
|
|
7
8
|
import { normalizeScopePattern } from "./scope.js";
|
|
@@ -28,7 +29,7 @@ async function deliveryChain(cwd, oldBase, oldHead) {
|
|
|
28
29
|
export function scopeEntry(input) {
|
|
29
30
|
const patterns = (input.appendScope ?? []).map(normalizeScopePattern);
|
|
30
31
|
return patterns.length === 0 ? null : {
|
|
31
|
-
v:
|
|
32
|
+
v: 2,
|
|
32
33
|
kind: "amend",
|
|
33
34
|
contract: input.contractId,
|
|
34
35
|
at: new Date(input.nowMs).toISOString(),
|
|
@@ -40,8 +41,8 @@ function effectiveScope(model, amendment) {
|
|
|
40
41
|
return [...model.effectiveScope, ...(amendment?.data.scopeDelta?.add ?? [])];
|
|
41
42
|
}
|
|
42
43
|
async function gitTree(cwd, revision) {
|
|
43
|
-
const
|
|
44
|
-
return
|
|
44
|
+
const read = await readCommitCoordinate(cwd, revision);
|
|
45
|
+
return read.status === "ok" ? read.coordinate.tree : null;
|
|
45
46
|
}
|
|
46
47
|
async function acceptedRenewIsReplayable(targetRoot, entry) {
|
|
47
48
|
const [index, oldTree, newTree, worktree] = await Promise.all([
|
|
@@ -103,11 +104,16 @@ export async function admitRenew(session, input, replay, expected) {
|
|
|
103
104
|
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} carrier pins disagree with the lifecycle read model`);
|
|
104
105
|
}
|
|
105
106
|
const amendment = scopeEntry(input);
|
|
107
|
+
const observedLedgerHead = session.repository.observedLedgerHead;
|
|
108
|
+
if (!observedLedgerHead)
|
|
109
|
+
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} has no observed repository ledger head`);
|
|
110
|
+
const lifecycleHead = model.contractHead;
|
|
106
111
|
const acceptedReplay = await acceptedRenewFromReplay(model, session.target.root, oldHead, newBase, replay);
|
|
107
112
|
if (acceptedReplay) {
|
|
108
113
|
return {
|
|
109
114
|
model,
|
|
110
115
|
repository: session.repository,
|
|
116
|
+
observedLedgerHead,
|
|
111
117
|
targetRoot: session.target.root,
|
|
112
118
|
oldBase: acceptedReplay.entry.data.oldBase,
|
|
113
119
|
oldFence: acceptedReplay.seal.data.oldFence,
|
|
@@ -117,6 +123,7 @@ export async function admitRenew(session, input, replay, expected) {
|
|
|
117
123
|
targetRef: model.pins.target.ref,
|
|
118
124
|
effectiveScope: [...model.effectiveScope],
|
|
119
125
|
amendment: null,
|
|
126
|
+
lifecycleHead,
|
|
120
127
|
acceptedReplay,
|
|
121
128
|
acceptedScopeReplay: null,
|
|
122
129
|
};
|
|
@@ -126,6 +133,7 @@ export async function admitRenew(session, input, replay, expected) {
|
|
|
126
133
|
return {
|
|
127
134
|
model,
|
|
128
135
|
repository: session.repository,
|
|
136
|
+
observedLedgerHead,
|
|
129
137
|
targetRoot: session.target.root,
|
|
130
138
|
oldBase: model.pins.base,
|
|
131
139
|
oldFence: model.pins.delivery,
|
|
@@ -135,6 +143,7 @@ export async function admitRenew(session, input, replay, expected) {
|
|
|
135
143
|
targetRef: model.pins.target.ref,
|
|
136
144
|
effectiveScope: [...model.effectiveScope],
|
|
137
145
|
amendment: null,
|
|
146
|
+
lifecycleHead,
|
|
138
147
|
acceptedReplay: null,
|
|
139
148
|
acceptedScopeReplay,
|
|
140
149
|
};
|
|
@@ -147,6 +156,7 @@ export async function admitRenew(session, input, replay, expected) {
|
|
|
147
156
|
const admission = {
|
|
148
157
|
model,
|
|
149
158
|
repository: session.repository,
|
|
159
|
+
observedLedgerHead,
|
|
150
160
|
targetRoot: session.target.root,
|
|
151
161
|
oldBase: model.pins.base,
|
|
152
162
|
oldFence: model.pins.delivery,
|
|
@@ -156,16 +166,17 @@ export async function admitRenew(session, input, replay, expected) {
|
|
|
156
166
|
targetRef: model.pins.target.ref,
|
|
157
167
|
effectiveScope: scope,
|
|
158
168
|
amendment,
|
|
169
|
+
lifecycleHead,
|
|
159
170
|
acceptedReplay: null,
|
|
160
171
|
acceptedScopeReplay: null,
|
|
161
172
|
};
|
|
162
|
-
if (expected && (expected.
|
|
173
|
+
if (expected && (expected.lifecycleHead !== lifecycleHead ||
|
|
163
174
|
expected.deliveryRef !== admission.deliveryRef ||
|
|
164
175
|
expected.targetRef !== admission.targetRef ||
|
|
165
176
|
expected.oldBase !== admission.oldBase ||
|
|
166
177
|
expected.oldHead !== admission.oldHead ||
|
|
167
|
-
expected.
|
|
168
|
-
|
|
178
|
+
(JSON.stringify(expected.effectiveScope) !== JSON.stringify(model.effectiveScope)
|
|
179
|
+
&& JSON.stringify(expected.effectiveScope) !== JSON.stringify(scope)))) {
|
|
169
180
|
throw new FlowError("CONCURRENT_MODIFICATION", `renew scratch coordinates no longer match ${input.contractId}`);
|
|
170
181
|
}
|
|
171
182
|
return admission;
|
|
@@ -177,11 +188,23 @@ export async function prepareRenew(admission, input, continuation) {
|
|
|
177
188
|
return { build: null, capture: null, prefix: admission.amendment ? [admission.amendment] : [], entry: null };
|
|
178
189
|
}
|
|
179
190
|
const build = continuation
|
|
180
|
-
? await continueDetachedRenewChain({
|
|
191
|
+
? await continueDetachedRenewChain({
|
|
192
|
+
cwd: input.cwd,
|
|
193
|
+
scratchName: continuation.scratch.scratchName,
|
|
194
|
+
resolution: continuation.resolution,
|
|
195
|
+
refresh: {
|
|
196
|
+
latestTarget: admission.newBase,
|
|
197
|
+
observedLedgerHead: admission.observedLedgerHead,
|
|
198
|
+
effectiveScope: admission.effectiveScope,
|
|
199
|
+
lifecycleHead: admission.lifecycleHead,
|
|
200
|
+
},
|
|
201
|
+
signal: input.signal,
|
|
202
|
+
})
|
|
181
203
|
: await buildDetachedRenewChain({
|
|
182
204
|
cwd: input.cwd,
|
|
183
205
|
scratchName: renewScratchName(input.contractId),
|
|
184
|
-
observedLedgerHead: admission.
|
|
206
|
+
observedLedgerHead: admission.observedLedgerHead,
|
|
207
|
+
lifecycleHead: admission.lifecycleHead,
|
|
185
208
|
deliveryRef: admission.deliveryRef,
|
|
186
209
|
targetRef: admission.targetRef,
|
|
187
210
|
effectiveScope: admission.effectiveScope,
|
|
@@ -204,6 +227,7 @@ export async function prepareRenew(admission, input, continuation) {
|
|
|
204
227
|
receiptHead: admission.oldHead,
|
|
205
228
|
contentBase: admission.newBase,
|
|
206
229
|
contentHead: build.newHead,
|
|
230
|
+
scopePolicy: "warn",
|
|
207
231
|
});
|
|
208
232
|
if (!capture)
|
|
209
233
|
throw new FlowError("NO_OPEN_ARC", `contract ${input.contractId} has no active folded receipt segment to renew`);
|
|
@@ -228,11 +252,12 @@ export async function prepareRenew(admission, input, continuation) {
|
|
|
228
252
|
return { build, capture, prefix, entry };
|
|
229
253
|
}
|
|
230
254
|
export function sameAdmission(current, pinned) {
|
|
231
|
-
return current.
|
|
255
|
+
return current.observedLedgerHead === pinned.observedLedgerHead &&
|
|
232
256
|
current.oldBase === pinned.oldBase && current.oldFence === pinned.oldFence &&
|
|
233
257
|
current.oldHead === pinned.oldHead && current.newBase === pinned.newBase &&
|
|
234
258
|
current.deliveryRef === pinned.deliveryRef && current.targetRef === pinned.targetRef &&
|
|
235
259
|
current.targetRoot === pinned.targetRoot &&
|
|
260
|
+
current.lifecycleHead === pinned.lifecycleHead &&
|
|
236
261
|
JSON.stringify(current.effectiveScope) === JSON.stringify(pinned.effectiveScope) &&
|
|
237
262
|
current.acceptedReplay?.renewObjectId === pinned.acceptedReplay?.renewObjectId &&
|
|
238
263
|
current.acceptedScopeReplay?.transactionObjectId === pinned.acceptedScopeReplay?.transactionObjectId;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { requireCommitCoordinate } from "../git/commits.js";
|
|
3
3
|
import { deriveHostGitCoordinates, readCommitObject } from "./repository-ledger/git-object-store.js";
|
|
4
4
|
import { stableRepoRoot } from "./worktree-path.js";
|
|
5
|
-
import { assertTree, buildCommit, evidenceResult, interrupted, readScratchState, renewScratchBase, replayFrom, scratchPaths, treeFromScratchFiles, validateCoordinates, validateScratchProgress, RenewBuildError } from "./renew-build.js";
|
|
5
|
+
import { assertTree, buildCommit, cleanupRenewScratch, evidenceResult, interrupted, persistConflict, readScratchState, renewScratchBase, replayFrom, replayResolvedDelivery, scratchPaths, treeFromScratchFiles, validateCoordinates, validateScratchProgress, RenewBuildError } from "./renew-build.js";
|
|
6
6
|
export * from "./renew-build.js";
|
|
7
7
|
export function renewScratchName(contractId) {
|
|
8
8
|
return `renew-${createHash("sha256").update(contractId).digest("hex").slice(0, 24)}`;
|
|
@@ -11,6 +11,8 @@ export async function readDetachedRenewScratch(input) {
|
|
|
11
11
|
const repositoryRoot = await stableRepoRoot(input.cwd);
|
|
12
12
|
const scratch = scratchPaths(await renewScratchBase(repositoryRoot), input.scratchName);
|
|
13
13
|
const state = await readScratchState(scratch);
|
|
14
|
+
const coordinates = await deriveHostGitCoordinates(repositoryRoot);
|
|
15
|
+
await assertTree(repositoryRoot, coordinates, state.conflictTree);
|
|
14
16
|
return {
|
|
15
17
|
status: "conflict",
|
|
16
18
|
...evidenceResult(state),
|
|
@@ -27,14 +29,17 @@ export async function continueDetachedRenewChain(input) {
|
|
|
27
29
|
const scratch = scratchPaths(scratchBase, input.scratchName);
|
|
28
30
|
const state = await readScratchState(scratch);
|
|
29
31
|
const coordinates = await deriveHostGitCoordinates(repositoryRoot);
|
|
32
|
+
await assertTree(repositoryRoot, coordinates, state.conflictTree);
|
|
30
33
|
const commits = await validateCoordinates({ ...state, cwd: repositoryRoot, coordinates });
|
|
31
34
|
await validateScratchProgress(repositoryRoot, coordinates, state, commits);
|
|
32
35
|
interrupted(input.signal);
|
|
33
36
|
const original = commits[state.nextIndex];
|
|
34
37
|
let resolvedHead;
|
|
38
|
+
let resolvedTree;
|
|
35
39
|
if (input.resolution.kind === "tree") {
|
|
36
40
|
await assertTree(repositoryRoot, coordinates, input.resolution.tree);
|
|
37
|
-
|
|
41
|
+
resolvedTree = input.resolution.tree;
|
|
42
|
+
resolvedHead = await buildCommit(repositoryRoot, coordinates, original, resolvedTree, state.currentHead);
|
|
38
43
|
}
|
|
39
44
|
else if (input.resolution.kind === "commit") {
|
|
40
45
|
const resolved = await readCommitObject(repositoryRoot, coordinates, input.resolution.commit);
|
|
@@ -42,10 +47,54 @@ export async function continueDetachedRenewChain(input) {
|
|
|
42
47
|
throw new RenewBuildError("INVALID_RENEW_RESOLUTION", `resolved commit ${input.resolution.commit} must have exactly parent ${state.currentHead}`);
|
|
43
48
|
}
|
|
44
49
|
resolvedHead = input.resolution.commit;
|
|
50
|
+
resolvedTree = (await requireCommitCoordinate(repositoryRoot, resolvedHead)).tree;
|
|
51
|
+
await assertTree(repositoryRoot, coordinates, resolvedTree);
|
|
45
52
|
}
|
|
46
53
|
else {
|
|
47
|
-
|
|
48
|
-
resolvedHead = await buildCommit(repositoryRoot, coordinates, original,
|
|
54
|
+
resolvedTree = await treeFromScratchFiles(repositoryRoot, coordinates, scratch, state);
|
|
55
|
+
resolvedHead = await buildCommit(repositoryRoot, coordinates, original, resolvedTree, state.currentHead);
|
|
56
|
+
}
|
|
57
|
+
if (input.refresh) {
|
|
58
|
+
state.observedLedgerHead = input.refresh.observedLedgerHead;
|
|
59
|
+
state.effectiveScope = [...input.refresh.effectiveScope];
|
|
60
|
+
state.lifecycleHead = input.refresh.lifecycleHead;
|
|
61
|
+
if (input.refresh.latestTarget !== state.newBase) {
|
|
62
|
+
const resolvedPrefix = commits.slice(0, state.nextIndex + 1).map((commit, index) => ({
|
|
63
|
+
original: commit,
|
|
64
|
+
resolvedHead: index === state.nextIndex ? resolvedHead : state.evidenceMap[commit.objectId],
|
|
65
|
+
}));
|
|
66
|
+
state.newBase = input.refresh.latestTarget;
|
|
67
|
+
state.currentHead = input.refresh.latestTarget;
|
|
68
|
+
state.evidenceMap = {};
|
|
69
|
+
for (let index = 0; index < resolvedPrefix.length; index += 1) {
|
|
70
|
+
const resolved = resolvedPrefix[index];
|
|
71
|
+
const commit = await readCommitObject(repositoryRoot, coordinates, resolved.resolvedHead);
|
|
72
|
+
if (commit.parents.length !== 1) {
|
|
73
|
+
throw new RenewBuildError("INVALID_RENEW_SCRATCH", `resolved commit ${resolved.resolvedHead} must have exactly one parent`);
|
|
74
|
+
}
|
|
75
|
+
const tree = (await requireCommitCoordinate(repositoryRoot, resolved.resolvedHead)).tree;
|
|
76
|
+
const replay = await replayResolvedDelivery({
|
|
77
|
+
cwd: repositoryRoot,
|
|
78
|
+
coordinates,
|
|
79
|
+
capturedTarget: commit.parents[0],
|
|
80
|
+
latestTarget: state.currentHead,
|
|
81
|
+
resolvedTree: tree,
|
|
82
|
+
identity: resolved.original,
|
|
83
|
+
});
|
|
84
|
+
if (replay.status === "conflict") {
|
|
85
|
+
state.nextIndex = index;
|
|
86
|
+
state.conflictTree = replay.tree;
|
|
87
|
+
state.conflictPaths = [...replay.paths];
|
|
88
|
+
state.conflictIndex = [...replay.index];
|
|
89
|
+
state.conflictCommit = resolved.original.objectId;
|
|
90
|
+
return await persistConflict(repositoryRoot, scratchBase, state, false);
|
|
91
|
+
}
|
|
92
|
+
const refreshedHead = await buildCommit(repositoryRoot, coordinates, resolved.original, replay.tree, state.currentHead);
|
|
93
|
+
state.evidenceMap[resolved.original.objectId] = refreshedHead;
|
|
94
|
+
state.currentHead = refreshedHead;
|
|
95
|
+
}
|
|
96
|
+
resolvedHead = state.currentHead;
|
|
97
|
+
}
|
|
49
98
|
}
|
|
50
99
|
state.evidenceMap[original.objectId] = resolvedHead;
|
|
51
100
|
state.currentHead = resolvedHead;
|
|
@@ -64,14 +113,6 @@ export async function continueDetachedRenewChain(input) {
|
|
|
64
113
|
export async function abortDetachedRenewChain(input) {
|
|
65
114
|
const repositoryRoot = await stableRepoRoot(input.cwd);
|
|
66
115
|
const scratch = scratchPaths(await renewScratchBase(repositoryRoot), input.scratchName);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
if (error.code === "ENOENT")
|
|
72
|
-
return { status: "absent", scratchName: input.scratchName };
|
|
73
|
-
throw error;
|
|
74
|
-
}
|
|
75
|
-
await fs.rm(scratch.root, { recursive: true, force: true });
|
|
76
|
-
return { status: "deleted", scratchName: input.scratchName };
|
|
116
|
+
const cleanup = await cleanupRenewScratch({ cwd: repositoryRoot, scratch });
|
|
117
|
+
return { ...cleanup, scratchName: input.scratchName };
|
|
77
118
|
}
|