@markjaquith/agency 2.58.2 → 2.60.0
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 +55 -4
- package/cli-main.ts +5 -0
- package/index.ts +1 -0
- package/package.json +4 -1
- package/schemas/agency-kickoff-v1.schema.json +124 -0
- package/src/cli-parser.test.ts +1 -1
- package/src/cli-parser.ts +23 -6
- package/src/commands/task.test.ts +77 -1
- package/src/commands/task.ts +76 -6
- package/src/commands/work.test.ts +62 -3
- package/src/commands/work.ts +106 -3
- package/src/services/ArchiveService.ts +9 -0
- package/src/services/IntegrationService.test.ts +37 -0
- package/src/services/ReviewService.ts +2 -0
- package/src/services/WorktreeService.test.ts +279 -1
- package/src/services/WorktreeService.ts +418 -30
- package/src/workbase/AGENTS.md +55 -4
- package/src/workbase/kickoff-contract.test.ts +145 -0
- package/src/workbase/kickoff-contract.ts +358 -0
|
@@ -82,6 +82,7 @@ interface HarnessOptions {
|
|
|
82
82
|
readonly phaseStatuses?: Readonly<
|
|
83
83
|
Record<string, "open" | "working" | "delegated" | "done" | "dropped">
|
|
84
84
|
>
|
|
85
|
+
readonly taskRepo?: string
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
const createHarness = (options: HarnessOptions = {}) => {
|
|
@@ -138,6 +139,16 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
138
139
|
runners: options.runners,
|
|
139
140
|
},
|
|
140
141
|
}),
|
|
142
|
+
repositoryAliases: () => Effect.succeed(["agency"]),
|
|
143
|
+
validate: () =>
|
|
144
|
+
Effect.succeed({
|
|
145
|
+
root: "/workbase",
|
|
146
|
+
issues: [],
|
|
147
|
+
epicCount: 0,
|
|
148
|
+
taskCount: 1,
|
|
149
|
+
phaseCount: 0,
|
|
150
|
+
valid: true,
|
|
151
|
+
}),
|
|
141
152
|
}
|
|
142
153
|
const epics = {
|
|
143
154
|
show: (id: string) =>
|
|
@@ -154,10 +165,11 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
154
165
|
return Effect.succeed({
|
|
155
166
|
id,
|
|
156
167
|
path: `/workbase/tasks/${id}/TASK.md`,
|
|
168
|
+
revision: "a".repeat(64),
|
|
157
169
|
data: options.multiPhaseTasks?.includes(id)
|
|
158
170
|
? { phases: [] }
|
|
159
171
|
: {
|
|
160
|
-
repo: "agency",
|
|
172
|
+
repo: options.taskRepo ?? "agency",
|
|
161
173
|
branch: `task/${id}`,
|
|
162
174
|
base: "main",
|
|
163
175
|
status: taskStatuses[id] ?? options.taskStatus ?? "open",
|
|
@@ -182,6 +194,7 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
182
194
|
taskId,
|
|
183
195
|
id,
|
|
184
196
|
path: `/workbase/tasks/${taskId}/phases/${id}/PHASE.md`,
|
|
197
|
+
revision: "b".repeat(64),
|
|
185
198
|
data: {
|
|
186
199
|
repo: "agency",
|
|
187
200
|
branch: `task/${id}`,
|
|
@@ -246,7 +259,16 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
246
259
|
}
|
|
247
260
|
const fs = {
|
|
248
261
|
isDirectory: (path: string) =>
|
|
249
|
-
Effect.succeed(
|
|
262
|
+
Effect.succeed(
|
|
263
|
+
path === "/workbase/epics" || path === "/workbase/tasks"
|
|
264
|
+
? false
|
|
265
|
+
: (options.existingDirectories?.includes(path) ?? true),
|
|
266
|
+
),
|
|
267
|
+
readFile: (path: string) =>
|
|
268
|
+
Effect.succeed(path.endsWith("agency.json") ? '{"version":2}\n' : ""),
|
|
269
|
+
readDirectory: () => Effect.succeed([]),
|
|
270
|
+
exists: () => Effect.succeed(false),
|
|
271
|
+
realPath: (path: string) => Effect.succeed(path),
|
|
250
272
|
runCommand: (args: readonly string[]) => {
|
|
251
273
|
const cli = args[1]!
|
|
252
274
|
events.push(`probe:${cli}`)
|
|
@@ -306,6 +328,7 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
306
328
|
Effect.provideService(WorkbaseService, workbase as never),
|
|
307
329
|
Effect.provideService(TaskService, tasks as never),
|
|
308
330
|
Effect.provideService(PhaseService, phases as never),
|
|
331
|
+
Effect.provideService(ReadinessService, readiness as never),
|
|
309
332
|
) as Effect.Effect<void, unknown, never>,
|
|
310
333
|
)
|
|
311
334
|
|
|
@@ -458,7 +481,7 @@ describe("work command", () => {
|
|
|
458
481
|
test("prepares without launching or changing lifecycle status", async () => {
|
|
459
482
|
const harness = createHarness({ existingDirectories: [] })
|
|
460
483
|
|
|
461
|
-
await captureLogs(() =>
|
|
484
|
+
const [output] = await captureLogs(() =>
|
|
462
485
|
harness.runPrepare({
|
|
463
486
|
cwd: "/workbase",
|
|
464
487
|
directory: "example",
|
|
@@ -473,7 +496,43 @@ describe("work command", () => {
|
|
|
473
496
|
expect(harness.materializeOptions[0]).toMatchObject({
|
|
474
497
|
json: true,
|
|
475
498
|
dryRun: true,
|
|
499
|
+
validationAlreadyPerformed: true,
|
|
500
|
+
})
|
|
501
|
+
const result = JSON.parse(output!)
|
|
502
|
+
expect(result.validationEvidence.status).toBe("refreshed")
|
|
503
|
+
expect(result.validationEvidence.reasons).toEqual(["not-supplied"])
|
|
504
|
+
expect(result.kickoff.target).toBe("execution-unit:task/example")
|
|
505
|
+
expect(
|
|
506
|
+
result.kickoff.steps.filter(
|
|
507
|
+
({ id }: { id: string }) => id === "final-context-verification",
|
|
508
|
+
),
|
|
509
|
+
).toHaveLength(1)
|
|
510
|
+
|
|
511
|
+
const [reusedOutput] = await captureLogs(() =>
|
|
512
|
+
harness.runPrepare({
|
|
513
|
+
cwd: "/workbase",
|
|
514
|
+
directory: "example",
|
|
515
|
+
json: true,
|
|
516
|
+
dryRun: true,
|
|
517
|
+
evidence: JSON.stringify(result.validationEvidence.evidence),
|
|
518
|
+
}),
|
|
519
|
+
)
|
|
520
|
+
expect(JSON.parse(reusedOutput!).validationEvidence).toEqual(
|
|
521
|
+
expect.objectContaining({ status: "reused", reasons: [] }),
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
const conflicting = createHarness({
|
|
525
|
+
existingDirectories: [],
|
|
526
|
+
taskRepo: "other",
|
|
476
527
|
})
|
|
528
|
+
await expect(
|
|
529
|
+
conflicting.runPrepare({
|
|
530
|
+
cwd: "/workbase",
|
|
531
|
+
directory: "example",
|
|
532
|
+
dryRun: true,
|
|
533
|
+
evidence: JSON.stringify(result.validationEvidence.evidence),
|
|
534
|
+
}),
|
|
535
|
+
).rejects.toThrow("Recalled repository conflicts")
|
|
477
536
|
})
|
|
478
537
|
|
|
479
538
|
test("launches an epic agent from an epic directory", async () => {
|
package/src/commands/work.ts
CHANGED
|
@@ -29,6 +29,13 @@ import {
|
|
|
29
29
|
resolveRunnerCommand,
|
|
30
30
|
runnerEnvironment,
|
|
31
31
|
} from "../workbase/runner-command"
|
|
32
|
+
import {
|
|
33
|
+
assessValidationEvidence,
|
|
34
|
+
buildKickoffPlan,
|
|
35
|
+
buildValidationEvidence,
|
|
36
|
+
normalizeRecalledContext,
|
|
37
|
+
readValidationEvidence,
|
|
38
|
+
} from "../workbase/kickoff-contract"
|
|
32
39
|
|
|
33
40
|
export interface WorkOptions extends BaseCommandOptions {
|
|
34
41
|
readonly directory?: string
|
|
@@ -41,6 +48,7 @@ export interface WorkOptions extends BaseCommandOptions {
|
|
|
41
48
|
readonly printCommand?: boolean
|
|
42
49
|
readonly auto?: boolean
|
|
43
50
|
readonly force?: boolean
|
|
51
|
+
readonly evidence?: string
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
export type StartWork = (options: WorkOptions) => ReturnType<typeof work>
|
|
@@ -422,6 +430,7 @@ export const workPrepare = (options: WorkOptions = {}) =>
|
|
|
422
430
|
const tasks = yield* TaskService
|
|
423
431
|
const phases = yield* PhaseService
|
|
424
432
|
const worktrees = yield* WorktreeService
|
|
433
|
+
const readiness = yield* ReadinessService
|
|
425
434
|
const { log } = createLoggers(options)
|
|
426
435
|
const cwd = options.cwd ?? process.cwd()
|
|
427
436
|
const targetPath = options.directory ? resolve(cwd, options.directory) : cwd
|
|
@@ -461,22 +470,111 @@ export const workPrepare = (options: WorkOptions = {}) =>
|
|
|
461
470
|
)
|
|
462
471
|
}
|
|
463
472
|
|
|
473
|
+
const task = yield* tasks.show(taskId, root)
|
|
474
|
+
const phase = phaseId
|
|
475
|
+
? yield* phases.show(taskId, phaseId, root)
|
|
476
|
+
: undefined
|
|
477
|
+
if ("phases" in task.data && !phase) {
|
|
478
|
+
return yield* Effect.fail(
|
|
479
|
+
new Error(`Task '${taskId}' has multiple phases; phase ID is required`),
|
|
480
|
+
)
|
|
481
|
+
}
|
|
482
|
+
if (!("phases" in task.data) && phaseId) {
|
|
483
|
+
return yield* Effect.fail(
|
|
484
|
+
new Error(
|
|
485
|
+
`Task '${taskId}' is single-phase and does not accept a phase ID`,
|
|
486
|
+
),
|
|
487
|
+
)
|
|
488
|
+
}
|
|
489
|
+
const target = phase
|
|
490
|
+
? `execution-unit:phase/${taskId}/${phase.id}`
|
|
491
|
+
: `execution-unit:task/${taskId}`
|
|
492
|
+
const document = phase ?? task
|
|
493
|
+
const suppliedEvidence = options.evidence
|
|
494
|
+
? yield* readValidationEvidence(options.evidence, cwd)
|
|
495
|
+
: undefined
|
|
496
|
+
if (
|
|
497
|
+
suppliedEvidence?.recalledContext.repo &&
|
|
498
|
+
(!("repo" in document.data) ||
|
|
499
|
+
suppliedEvidence.recalledContext.repo !== document.data.repo)
|
|
500
|
+
) {
|
|
501
|
+
return yield* Effect.fail(
|
|
502
|
+
new Error(
|
|
503
|
+
"Recalled repository conflicts with the current execution unit",
|
|
504
|
+
),
|
|
505
|
+
)
|
|
506
|
+
}
|
|
507
|
+
if (
|
|
508
|
+
suppliedEvidence?.recalledContext.base &&
|
|
509
|
+
(!("base" in document.data) ||
|
|
510
|
+
suppliedEvidence.recalledContext.base !== document.data.base)
|
|
511
|
+
) {
|
|
512
|
+
return yield* Effect.fail(
|
|
513
|
+
new Error("Recalled base conflicts with the current execution unit"),
|
|
514
|
+
)
|
|
515
|
+
}
|
|
516
|
+
const assessment = yield* assessValidationEvidence({
|
|
517
|
+
evidence: suppliedEvidence,
|
|
518
|
+
startPath: root,
|
|
519
|
+
target,
|
|
520
|
+
documentPath: document.path,
|
|
521
|
+
documentRevision: document.revision,
|
|
522
|
+
})
|
|
523
|
+
let validation: unknown = { valid: true, source: "evidence" }
|
|
524
|
+
if (assessment.disposition.status === "refreshed") {
|
|
525
|
+
validation = yield* workbase.validate(root)
|
|
526
|
+
if (!(validation as { valid: boolean }).valid && !options.force) {
|
|
527
|
+
return yield* Effect.fail(new Error("Workbase validation failed"))
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
yield* readiness.guardWorkTarget(target, root, options.force)
|
|
464
531
|
const workspace = yield* worktrees.materialize(taskId, phaseId, root, {
|
|
465
532
|
...options,
|
|
466
533
|
dryRun: options.dryRun,
|
|
534
|
+
validationAlreadyPerformed: true,
|
|
535
|
+
})
|
|
536
|
+
const recalledContext =
|
|
537
|
+
suppliedEvidence?.recalledContext ??
|
|
538
|
+
normalizeRecalledContext({
|
|
539
|
+
id: taskId,
|
|
540
|
+
repo: "repo" in document.data ? document.data.repo : undefined,
|
|
541
|
+
base: "base" in document.data ? document.data.base : undefined,
|
|
542
|
+
})
|
|
543
|
+
const evidence = yield* buildValidationEvidence({
|
|
544
|
+
startPath: root,
|
|
545
|
+
target,
|
|
546
|
+
documentPath: document.path,
|
|
547
|
+
documentRevision: document.revision,
|
|
548
|
+
recalledContext,
|
|
467
549
|
})
|
|
550
|
+
const result = {
|
|
551
|
+
...workspace,
|
|
552
|
+
workspace,
|
|
553
|
+
validation,
|
|
554
|
+
validationEvidence: { ...assessment.disposition, evidence },
|
|
555
|
+
kickoff: buildKickoffPlan({
|
|
556
|
+
workbaseRoot: root,
|
|
557
|
+
target,
|
|
558
|
+
taskId,
|
|
559
|
+
phaseId: phase?.id,
|
|
560
|
+
taskPath: task.path,
|
|
561
|
+
phasePath: phase?.path,
|
|
562
|
+
checkoutPath: workspace.writablePath ?? workspace.reviewPath,
|
|
563
|
+
documentRevision: document.revision,
|
|
564
|
+
}),
|
|
565
|
+
}
|
|
468
566
|
if (options.json) {
|
|
469
|
-
log(JSON.stringify(
|
|
567
|
+
log(JSON.stringify(result, null, 2))
|
|
470
568
|
} else {
|
|
471
569
|
log(
|
|
472
|
-
`${workspace.dryRun ? "
|
|
570
|
+
`${workspace.dryRun ? "Kickoff plan" : "Workspace ready"}: ${workspace.writablePath ?? workspace.reviewPath}`,
|
|
473
571
|
)
|
|
474
572
|
}
|
|
475
573
|
})
|
|
476
574
|
|
|
477
575
|
export const help = `
|
|
478
576
|
Usage: agency work [<directory-or-task-id> | --epic <epic-id>] [--runner <name>] [--auto]
|
|
479
|
-
agency work prepare [target] [--dry-run] [--json]
|
|
577
|
+
agency work prepare [target] [--evidence <json-or-path>] [--dry-run] [--json]
|
|
480
578
|
|
|
481
579
|
Launch an agent for an epic, task, or phase. With no directory, select one
|
|
482
580
|
interactively. A positional argument resolves as a directory first, then as a task
|
|
@@ -487,6 +585,10 @@ Agency's project plugin; Agency context remains authoritative for writes.
|
|
|
487
585
|
The prepare subcommand resolves and materializes an execution workspace without
|
|
488
586
|
launching an agent or changing lifecycle status. --dry-run reports planned Git
|
|
489
587
|
changes without fetching, creating branches, or creating worktrees.
|
|
588
|
+
It emits revision-bound validation evidence and an idempotent external-orchestrator
|
|
589
|
+
contract. Evidence is reused only while the target, workbase, configuration, and
|
|
590
|
+
repository mapping remain unchanged. Dynamic readiness and workspace safety checks
|
|
591
|
+
always run.
|
|
490
592
|
|
|
491
593
|
Options:
|
|
492
594
|
--epic <id> Work on an epic
|
|
@@ -500,6 +602,7 @@ Options:
|
|
|
500
602
|
--opencode Require the OpenCode preset
|
|
501
603
|
--claude Require the Claude Code preset
|
|
502
604
|
--force Override readiness; reopen terminal execution units
|
|
605
|
+
--evidence <value> Validation evidence JSON or a path to JSON (prepare only)
|
|
503
606
|
--no-input Never open an interactive selector
|
|
504
607
|
|
|
505
608
|
Without interactive input, provide an explicit workbase or cwd and an entity
|
|
@@ -413,6 +413,7 @@ const restoreWorktreeSnapshots = async (
|
|
|
413
413
|
snapshot.head,
|
|
414
414
|
snapshot.path,
|
|
415
415
|
])
|
|
416
|
+
await runGit(["jj", "-R", snapshot.path, "edit", snapshot.head])
|
|
416
417
|
continue
|
|
417
418
|
}
|
|
418
419
|
await runGit(
|
|
@@ -723,6 +724,7 @@ export class ArchiveService extends Effect.Service<ArchiveService>()(
|
|
|
723
724
|
removedWorktrees.push(
|
|
724
725
|
...(yield* worktrees.remove(unit.taskId, unit.phaseId, root, {
|
|
725
726
|
dryRun: true,
|
|
727
|
+
persistResume: false,
|
|
726
728
|
})),
|
|
727
729
|
)
|
|
728
730
|
}
|
|
@@ -781,6 +783,7 @@ export class ArchiveService extends Effect.Service<ArchiveService>()(
|
|
|
781
783
|
worktrees.remove(unit.taskId, unit.phaseId, root, {
|
|
782
784
|
snapshots,
|
|
783
785
|
lockHeld: true,
|
|
786
|
+
persistResume: false,
|
|
784
787
|
}),
|
|
785
788
|
)
|
|
786
789
|
} catch (cause) {
|
|
@@ -891,6 +894,7 @@ export class ArchiveService extends Effect.Service<ArchiveService>()(
|
|
|
891
894
|
const result = yield* worktrees
|
|
892
895
|
.remove(unit.taskId, executionPhaseId(unit), root, {
|
|
893
896
|
dryRun: true,
|
|
897
|
+
persistResume: false,
|
|
894
898
|
})
|
|
895
899
|
.pipe(Effect.either)
|
|
896
900
|
if (Either.isLeft(result)) {
|
|
@@ -1064,6 +1068,7 @@ export class ArchiveService extends Effect.Service<ArchiveService>()(
|
|
|
1064
1068
|
worktrees.remove(unit.taskId, unit.phaseId, root, {
|
|
1065
1069
|
snapshots,
|
|
1066
1070
|
lockHeld: true,
|
|
1071
|
+
persistResume: false,
|
|
1067
1072
|
}),
|
|
1068
1073
|
)
|
|
1069
1074
|
}
|
|
@@ -1212,6 +1217,7 @@ export class ArchiveService extends Effect.Service<ArchiveService>()(
|
|
|
1212
1217
|
root,
|
|
1213
1218
|
{
|
|
1214
1219
|
dryRun: true,
|
|
1220
|
+
persistResume: false,
|
|
1215
1221
|
},
|
|
1216
1222
|
)),
|
|
1217
1223
|
)
|
|
@@ -1267,6 +1273,7 @@ export class ArchiveService extends Effect.Service<ArchiveService>()(
|
|
|
1267
1273
|
{
|
|
1268
1274
|
snapshots,
|
|
1269
1275
|
lockHeld: true,
|
|
1276
|
+
persistResume: false,
|
|
1270
1277
|
},
|
|
1271
1278
|
),
|
|
1272
1279
|
)
|
|
@@ -1359,6 +1366,7 @@ export class ArchiveService extends Effect.Service<ArchiveService>()(
|
|
|
1359
1366
|
yield* rejectExistingDestination(destination, "Archive")
|
|
1360
1367
|
const removedWorktrees = yield* worktrees.remove(taskId, id, root, {
|
|
1361
1368
|
dryRun: true,
|
|
1369
|
+
persistResume: false,
|
|
1362
1370
|
})
|
|
1363
1371
|
const at = new Date().toISOString()
|
|
1364
1372
|
const content = yield* declarationContent(task, {
|
|
@@ -1407,6 +1415,7 @@ export class ArchiveService extends Effect.Service<ArchiveService>()(
|
|
|
1407
1415
|
worktrees.remove(taskId, id, root, {
|
|
1408
1416
|
snapshots,
|
|
1409
1417
|
lockHeld: true,
|
|
1418
|
+
persistResume: false,
|
|
1410
1419
|
}),
|
|
1411
1420
|
)
|
|
1412
1421
|
},
|
|
@@ -107,6 +107,21 @@ describe("IntegrationService", () => {
|
|
|
107
107
|
])
|
|
108
108
|
})
|
|
109
109
|
|
|
110
|
+
test("generates the canonical Agency kickoff recipe with precedence", () => {
|
|
111
|
+
expect(managedWorkbaseAgents).toContain(
|
|
112
|
+
"takes precedence over generic Herdr defaults",
|
|
113
|
+
)
|
|
114
|
+
expect(managedWorkbaseAgents).toContain(
|
|
115
|
+
"agency work prepare <slug> --evidence",
|
|
116
|
+
)
|
|
117
|
+
expect(managedWorkbaseAgents).toContain("agency-kickoff-v1")
|
|
118
|
+
expect(managedWorkbaseAgents).toContain(
|
|
119
|
+
"call Herdr help, skill, or CLI discovery",
|
|
120
|
+
)
|
|
121
|
+
expect(managedWorkbaseAgents).toContain("exactly one final")
|
|
122
|
+
expect(managedWorkbaseAgents).toContain("leave the runner in the")
|
|
123
|
+
})
|
|
124
|
+
|
|
110
125
|
test("generates a dynamic workbase plugin", () => {
|
|
111
126
|
expect(managedWorkbaseOpencodePlugin).toContain(
|
|
112
127
|
"process.env.AGENCY_WRITABLE_CHECKOUT",
|
|
@@ -445,6 +460,28 @@ describe("IntegrationService", () => {
|
|
|
445
460
|
expect(body).not.toContain(".opencode/command/agency.md")
|
|
446
461
|
})
|
|
447
462
|
|
|
463
|
+
test("generates repository add and setup guidance", () => {
|
|
464
|
+
const body = managedBody(managedWorkbaseAgents)
|
|
465
|
+
|
|
466
|
+
expect(body).toContain("## Adding a Repository")
|
|
467
|
+
expect(body).toContain("agency repo add <alias> <remote> --json")
|
|
468
|
+
expect(body).toContain(
|
|
469
|
+
"`agency repo add` mutates immediately and does not accept `--apply`",
|
|
470
|
+
)
|
|
471
|
+
expect(body).toContain("agency repo setup --dry-run")
|
|
472
|
+
expect(body).toContain("agency repo setup --apply")
|
|
473
|
+
expect(body).toMatch(
|
|
474
|
+
/repositories that are already declared\s+but locally missing/,
|
|
475
|
+
)
|
|
476
|
+
expect(body).toContain("Do not edit\n`agency.json` or `repos/` manually")
|
|
477
|
+
expect(body).toMatch(
|
|
478
|
+
/agency repo verify <alias> --json\s+agency validate --json/,
|
|
479
|
+
)
|
|
480
|
+
expect(body).toMatch(
|
|
481
|
+
/run only these checks, in order, unless\s+`agency context` reports a relevant problem/,
|
|
482
|
+
)
|
|
483
|
+
})
|
|
484
|
+
|
|
448
485
|
test("configures Agency agents with complete workbase access", () => {
|
|
449
486
|
const config = JSON.parse(managedBody(managedWorkbaseOpencode))
|
|
450
487
|
|
|
@@ -101,6 +101,7 @@ const restoreSnapshots = async (
|
|
|
101
101
|
snapshot.head,
|
|
102
102
|
snapshot.path,
|
|
103
103
|
])
|
|
104
|
+
await runGit(["jj", "-R", snapshot.path, "edit", snapshot.head])
|
|
104
105
|
continue
|
|
105
106
|
}
|
|
106
107
|
await runGit(
|
|
@@ -392,6 +393,7 @@ export class ReviewService extends Effect.Service<ReviewService>()(
|
|
|
392
393
|
worktrees.remove(taskId, undefined, root, {
|
|
393
394
|
snapshots,
|
|
394
395
|
lockHeld: true,
|
|
396
|
+
persistResume: false,
|
|
395
397
|
}),
|
|
396
398
|
).then(() => undefined),
|
|
397
399
|
rollback: () => restoreSnapshots(snapshots),
|