@markjaquith/agency 2.49.0 → 2.51.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 +56 -9
- package/cli.ts +56 -1
- package/package.json +1 -1
- package/schemas/agency-graph-v1.schema.json +142 -38
- package/src/cli-parser.test.ts +81 -0
- package/src/cli-parser.ts +78 -1
- package/src/cli.test.ts +4 -4
- package/src/commands/init.test.ts +2 -0
- package/src/commands/review.ts +38 -0
- package/src/commands/task.ts +27 -5
- package/src/commands/vcs.test.ts +75 -0
- package/src/commands/vcs.ts +72 -0
- package/src/commands/work.test.ts +2 -0
- package/src/commands/work.ts +2 -2
- package/src/commands/worktree.ts +1 -1
- package/src/graph-schema.test.ts +1 -1
- package/src/graph-schema.ts +36 -13
- package/src/protocol.ts +1 -0
- package/src/services/ArchiveService.test.ts +2 -2
- package/src/services/ArchiveService.ts +24 -0
- package/src/services/ClaimService.ts +3 -2
- package/src/services/ContextService.ts +118 -17
- package/src/services/DoctorService.ts +58 -7
- package/src/services/GraphMutationService.ts +5 -0
- package/src/services/GraphService.ts +119 -54
- package/src/services/PhaseService.ts +196 -70
- package/src/services/PullRequestService.test.ts +2 -2
- package/src/services/PullRequestService.ts +37 -33
- package/src/services/ReadinessService.ts +7 -3
- package/src/services/RepositoryService.test.ts +31 -1
- package/src/services/RepositoryService.ts +63 -31
- package/src/services/ReviewService.test.ts +472 -0
- package/src/services/ReviewService.ts +427 -0
- package/src/services/SyncService.test.ts +69 -2
- package/src/services/SyncService.ts +161 -90
- package/src/services/TaskPhaseService.test.ts +80 -0
- package/src/services/TaskService.ts +82 -9
- package/src/services/VcsMigrationService.test.ts +211 -0
- package/src/services/VcsMigrationService.ts +816 -0
- package/src/services/VersionControlService.test.ts +100 -0
- package/src/services/VersionControlService.ts +479 -0
- package/src/services/WorkbaseService.ts +7 -2
- package/src/services/WorktreeService.test.ts +88 -17
- package/src/services/WorktreeService.ts +865 -329
- package/src/test-utils.ts +12 -0
- package/src/work-view.ts +14 -6
- package/src/workbase/AGENTS.md +2 -2
- package/src/workbase/schemas.test.ts +57 -0
- package/src/workbase/schemas.ts +57 -0
- package/src/workbase/version-control.ts +5 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
|
|
2
|
+
import { Effect } from "effect"
|
|
3
|
+
import { mkdir } from "node:fs/promises"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { cleanupTempDir, createTempDir, runTestEffect } from "../test-utils"
|
|
6
|
+
import { TaskService } from "./TaskService"
|
|
7
|
+
import { ReviewService } from "./ReviewService"
|
|
8
|
+
import { WorktreeService } from "./WorktreeService"
|
|
9
|
+
import { ContextService } from "./ContextService"
|
|
10
|
+
import { GraphService } from "./GraphService"
|
|
11
|
+
import { PullRequestService } from "./PullRequestService"
|
|
12
|
+
import { PhaseService } from "./PhaseService"
|
|
13
|
+
import { ArchiveService } from "./ArchiveService"
|
|
14
|
+
import { ClaimService } from "./ClaimService"
|
|
15
|
+
import { SyncService } from "./SyncService"
|
|
16
|
+
import { task as taskCommand } from "../commands/task"
|
|
17
|
+
|
|
18
|
+
const git = async (args: string[], cwd?: string) => {
|
|
19
|
+
const child = Bun.spawn(["git", ...args], {
|
|
20
|
+
cwd,
|
|
21
|
+
stdout: "pipe",
|
|
22
|
+
stderr: "pipe",
|
|
23
|
+
})
|
|
24
|
+
await child.exited
|
|
25
|
+
if (child.exitCode !== 0)
|
|
26
|
+
throw new Error(await new Response(child.stderr).text())
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("ReviewService", () => {
|
|
30
|
+
let root: string
|
|
31
|
+
let source: string
|
|
32
|
+
|
|
33
|
+
beforeEach(async () => {
|
|
34
|
+
root = await createTempDir()
|
|
35
|
+
source = join(root, "source")
|
|
36
|
+
await Bun.write(join(root, "agency.json"), '{"version":2}\n')
|
|
37
|
+
await mkdir(source, { recursive: true })
|
|
38
|
+
await git(["init", "--initial-branch=main"], source)
|
|
39
|
+
await git(["config", "user.email", "test@example.com"], source)
|
|
40
|
+
await git(["config", "user.name", "Test"], source)
|
|
41
|
+
await Bun.write(join(source, "README.md"), "initial\n")
|
|
42
|
+
await git(["add", "README.md"], source)
|
|
43
|
+
await git(["-c", "commit.gpgsign=false", "commit", "-m", "initial"], source)
|
|
44
|
+
await git(["switch", "-c", "review-me"], source)
|
|
45
|
+
await Bun.write(join(source, "README.md"), "review one\n")
|
|
46
|
+
await git(["commit", "-am", "review one"], source)
|
|
47
|
+
await mkdir(join(root, "repos"), { recursive: true })
|
|
48
|
+
await git(["clone", "--bare", source, join(root, "repos/agency")])
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
afterEach(async () => cleanupTempDir(root))
|
|
52
|
+
|
|
53
|
+
const createReview = async () => {
|
|
54
|
+
const review = await runTestEffect(
|
|
55
|
+
ReviewService.pipe(
|
|
56
|
+
Effect.flatMap((service) =>
|
|
57
|
+
service.resolve("agency", { ref: "review-me" }, root),
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
return runTestEffect(
|
|
62
|
+
TaskService.pipe(
|
|
63
|
+
Effect.flatMap((service) =>
|
|
64
|
+
service.create({ id: "review", ticketUrl: null, review }, root),
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
test("pins, materializes, reports, and explicitly refreshes a branch review", async () => {
|
|
71
|
+
const created = await createReview()
|
|
72
|
+
const original = "review" in created.data ? created.data.review.commit : ""
|
|
73
|
+
expect("review" in created.data && created.data.review.commit).toMatch(
|
|
74
|
+
/^[a-f0-9]{40}$/,
|
|
75
|
+
)
|
|
76
|
+
const workspace = await runTestEffect(
|
|
77
|
+
WorktreeService.pipe(
|
|
78
|
+
Effect.flatMap((service) =>
|
|
79
|
+
service.materialize("review", undefined, root),
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
expect(workspace.writablePath).toBeNull()
|
|
84
|
+
expect(workspace.reviewPath).toBe(join(workspace.codePath, "agency"))
|
|
85
|
+
expect(
|
|
86
|
+
await Bun.file(join(workspace.codePath, "agency/README.md")).text(),
|
|
87
|
+
).toBe("review one\n")
|
|
88
|
+
const branch = Bun.spawnSync([
|
|
89
|
+
"git",
|
|
90
|
+
"-C",
|
|
91
|
+
join(workspace.codePath, "agency"),
|
|
92
|
+
"branch",
|
|
93
|
+
"--show-current",
|
|
94
|
+
])
|
|
95
|
+
expect(new TextDecoder().decode(branch.stdout).trim()).toBe("")
|
|
96
|
+
|
|
97
|
+
const context = await runTestEffect(
|
|
98
|
+
ContextService.pipe(
|
|
99
|
+
Effect.flatMap((service) =>
|
|
100
|
+
service.get({ target: "review", cwd: root }),
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
expect(context.authority.mode).toBe("review")
|
|
105
|
+
expect(context.authority.writable).toBeNull()
|
|
106
|
+
expect(context.authority.references).toHaveLength(1)
|
|
107
|
+
expect(context.authority.documents?.writable).toEqual([created.path])
|
|
108
|
+
expect(context.pr).toEqual({ url: null, state: "none" })
|
|
109
|
+
|
|
110
|
+
const graph = await runTestEffect(
|
|
111
|
+
GraphService.pipe(
|
|
112
|
+
Effect.flatMap((service) =>
|
|
113
|
+
service.get({ cwd: root, include: ["workspace", "git", "pr"] }),
|
|
114
|
+
),
|
|
115
|
+
),
|
|
116
|
+
)
|
|
117
|
+
expect(
|
|
118
|
+
graph.edges.some(
|
|
119
|
+
(edge) => edge.kind === "writes" && edge.from.includes("review"),
|
|
120
|
+
),
|
|
121
|
+
).toBe(false)
|
|
122
|
+
expect(
|
|
123
|
+
graph.edges.some(
|
|
124
|
+
(edge) => edge.kind === "references" && edge.from.includes("review"),
|
|
125
|
+
),
|
|
126
|
+
).toBe(true)
|
|
127
|
+
|
|
128
|
+
await Bun.write(join(source, "README.md"), "review two\n")
|
|
129
|
+
await git(["commit", "-am", "review two"], source)
|
|
130
|
+
const refreshed = await runTestEffect(
|
|
131
|
+
ReviewService.pipe(
|
|
132
|
+
Effect.flatMap((service) =>
|
|
133
|
+
service.refresh("review", root, created.revision),
|
|
134
|
+
),
|
|
135
|
+
),
|
|
136
|
+
)
|
|
137
|
+
expect(refreshed.changed).toBe(true)
|
|
138
|
+
expect(
|
|
139
|
+
await Bun.file(join(workspace.codePath, "agency/README.md")).text(),
|
|
140
|
+
).toBe("review two\n")
|
|
141
|
+
const oldPins = Bun.spawnSync([
|
|
142
|
+
"git",
|
|
143
|
+
"-C",
|
|
144
|
+
join(root, "repos/agency"),
|
|
145
|
+
"for-each-ref",
|
|
146
|
+
"--format=%(refname)",
|
|
147
|
+
"--points-at",
|
|
148
|
+
original,
|
|
149
|
+
"refs/agency/reviews",
|
|
150
|
+
])
|
|
151
|
+
expect(new TextDecoder().decode(oldPins.stdout).trim()).toBe("")
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test("rejects delivery and phase operations even when forced", async () => {
|
|
155
|
+
await createReview()
|
|
156
|
+
await expect(
|
|
157
|
+
runTestEffect(
|
|
158
|
+
PullRequestService.pipe(
|
|
159
|
+
Effect.flatMap((service) =>
|
|
160
|
+
service.create("review", undefined, false, root, { force: true }),
|
|
161
|
+
),
|
|
162
|
+
),
|
|
163
|
+
),
|
|
164
|
+
).rejects.toThrow("cannot create a delivery pull request")
|
|
165
|
+
await expect(
|
|
166
|
+
runTestEffect(
|
|
167
|
+
PhaseService.pipe(
|
|
168
|
+
Effect.flatMap((service) =>
|
|
169
|
+
service.create(
|
|
170
|
+
{
|
|
171
|
+
taskId: "review",
|
|
172
|
+
id: "phase",
|
|
173
|
+
firstPhase: "original",
|
|
174
|
+
repo: "agency",
|
|
175
|
+
branch: "phase",
|
|
176
|
+
base: "main",
|
|
177
|
+
},
|
|
178
|
+
root,
|
|
179
|
+
),
|
|
180
|
+
),
|
|
181
|
+
),
|
|
182
|
+
),
|
|
183
|
+
).rejects.toThrow("cannot be converted to phases")
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test("participates in claim lifecycle", async () => {
|
|
187
|
+
const task = await createReview()
|
|
188
|
+
const claimed = await runTestEffect(
|
|
189
|
+
ClaimService.pipe(
|
|
190
|
+
Effect.flatMap((service) =>
|
|
191
|
+
service.claim(
|
|
192
|
+
{
|
|
193
|
+
taskId: "review",
|
|
194
|
+
claimant: "reviewer",
|
|
195
|
+
runner: "opencode",
|
|
196
|
+
sessionId: "session",
|
|
197
|
+
revision: task.revision,
|
|
198
|
+
},
|
|
199
|
+
root,
|
|
200
|
+
),
|
|
201
|
+
),
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
expect(claimed.data.status).toBe("working")
|
|
205
|
+
const released = await runTestEffect(
|
|
206
|
+
ClaimService.pipe(
|
|
207
|
+
Effect.flatMap((service) =>
|
|
208
|
+
service.release(
|
|
209
|
+
{
|
|
210
|
+
taskId: "review",
|
|
211
|
+
sessionId: "session",
|
|
212
|
+
revision: claimed.revision,
|
|
213
|
+
},
|
|
214
|
+
root,
|
|
215
|
+
),
|
|
216
|
+
),
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
expect(released.data.status).toBe("open")
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
test("rejects refresh while actively claimed", async () => {
|
|
223
|
+
const task = await createReview()
|
|
224
|
+
await runTestEffect(
|
|
225
|
+
ClaimService.pipe(
|
|
226
|
+
Effect.flatMap((service) =>
|
|
227
|
+
service.claim(
|
|
228
|
+
{
|
|
229
|
+
taskId: "review",
|
|
230
|
+
claimant: "reviewer",
|
|
231
|
+
runner: "opencode",
|
|
232
|
+
sessionId: "active",
|
|
233
|
+
revision: task.revision,
|
|
234
|
+
},
|
|
235
|
+
root,
|
|
236
|
+
),
|
|
237
|
+
),
|
|
238
|
+
),
|
|
239
|
+
)
|
|
240
|
+
await expect(
|
|
241
|
+
runTestEffect(
|
|
242
|
+
ReviewService.pipe(
|
|
243
|
+
Effect.flatMap((service) => service.refresh("review", root)),
|
|
244
|
+
),
|
|
245
|
+
),
|
|
246
|
+
).rejects.toThrow("active claim")
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
test("rolls back document and checkout when pin advancement fails", async () => {
|
|
250
|
+
const created = await createReview()
|
|
251
|
+
const workspace = await runTestEffect(
|
|
252
|
+
WorktreeService.pipe(
|
|
253
|
+
Effect.flatMap((service) =>
|
|
254
|
+
service.materialize("review", undefined, root),
|
|
255
|
+
),
|
|
256
|
+
),
|
|
257
|
+
)
|
|
258
|
+
const original = "review" in created.data ? created.data.review.commit : ""
|
|
259
|
+
await Bun.write(join(source, "README.md"), "new source\n")
|
|
260
|
+
await git(["commit", "-am", "new source"], source)
|
|
261
|
+
await git(
|
|
262
|
+
["update-ref", "refs/agency/reviews/726576696577", "main"],
|
|
263
|
+
join(root, "repos/agency"),
|
|
264
|
+
)
|
|
265
|
+
await expect(
|
|
266
|
+
runTestEffect(
|
|
267
|
+
ReviewService.pipe(
|
|
268
|
+
Effect.flatMap((service) => service.refresh("review", root)),
|
|
269
|
+
),
|
|
270
|
+
),
|
|
271
|
+
).rejects.toThrow("rolled back")
|
|
272
|
+
const current = await runTestEffect(
|
|
273
|
+
TaskService.pipe(
|
|
274
|
+
Effect.flatMap((service) => service.show("review", root)),
|
|
275
|
+
),
|
|
276
|
+
)
|
|
277
|
+
expect("review" in current.data && current.data.review.commit).toBe(
|
|
278
|
+
original,
|
|
279
|
+
)
|
|
280
|
+
expect(
|
|
281
|
+
await Bun.file(join(workspace.reviewPath!, "README.md")).text(),
|
|
282
|
+
).toBe("review one\n")
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
test("blocks refresh of a dirty detached checkout", async () => {
|
|
286
|
+
await createReview()
|
|
287
|
+
const workspace = await runTestEffect(
|
|
288
|
+
WorktreeService.pipe(
|
|
289
|
+
Effect.flatMap((service) =>
|
|
290
|
+
service.materialize("review", undefined, root),
|
|
291
|
+
),
|
|
292
|
+
),
|
|
293
|
+
)
|
|
294
|
+
await Bun.write(join(workspace.codePath, "agency/dirty.txt"), "dirty\n")
|
|
295
|
+
await expect(
|
|
296
|
+
runTestEffect(
|
|
297
|
+
ReviewService.pipe(
|
|
298
|
+
Effect.flatMap((service) => service.refresh("review", root)),
|
|
299
|
+
),
|
|
300
|
+
),
|
|
301
|
+
).rejects.toThrow("dirty or structurally unexpected")
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
test("rejects unsafe branch sources and mixed create inputs", async () => {
|
|
305
|
+
for (const ref of [
|
|
306
|
+
"HEAD",
|
|
307
|
+
"refs/tags/v1",
|
|
308
|
+
"--upload-pack=evil",
|
|
309
|
+
"feature:*",
|
|
310
|
+
"feature/*",
|
|
311
|
+
"feature..bad",
|
|
312
|
+
]) {
|
|
313
|
+
await expect(
|
|
314
|
+
runTestEffect(
|
|
315
|
+
ReviewService.pipe(
|
|
316
|
+
Effect.flatMap((service) =>
|
|
317
|
+
service.resolve("agency", { ref }, root),
|
|
318
|
+
),
|
|
319
|
+
),
|
|
320
|
+
),
|
|
321
|
+
).rejects.toThrow("Invalid review branch")
|
|
322
|
+
}
|
|
323
|
+
const resolved = await runTestEffect(
|
|
324
|
+
ReviewService.pipe(
|
|
325
|
+
Effect.flatMap((service) =>
|
|
326
|
+
service.resolve("agency", { ref: "origin/review-me" }, root),
|
|
327
|
+
),
|
|
328
|
+
),
|
|
329
|
+
)
|
|
330
|
+
expect(resolved.source).toEqual({
|
|
331
|
+
kind: "branch",
|
|
332
|
+
ref: "refs/heads/review-me",
|
|
333
|
+
})
|
|
334
|
+
await expect(
|
|
335
|
+
runTestEffect(
|
|
336
|
+
TaskService.pipe(
|
|
337
|
+
Effect.flatMap((service) =>
|
|
338
|
+
service.create(
|
|
339
|
+
{
|
|
340
|
+
id: "mixed",
|
|
341
|
+
ticketUrl: null,
|
|
342
|
+
review: resolved,
|
|
343
|
+
repo: "agency",
|
|
344
|
+
branch: "mixed",
|
|
345
|
+
base: "main",
|
|
346
|
+
},
|
|
347
|
+
root,
|
|
348
|
+
),
|
|
349
|
+
),
|
|
350
|
+
),
|
|
351
|
+
),
|
|
352
|
+
).rejects.toThrow("cannot include writable")
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
test("removes a task-scoped pin when task creation fails", async () => {
|
|
356
|
+
await runTestEffect(
|
|
357
|
+
TaskService.pipe(
|
|
358
|
+
Effect.flatMap((service) =>
|
|
359
|
+
service.create(
|
|
360
|
+
{
|
|
361
|
+
id: "review",
|
|
362
|
+
ticketUrl: null,
|
|
363
|
+
repo: "agency",
|
|
364
|
+
branch: "task/review",
|
|
365
|
+
base: "main",
|
|
366
|
+
},
|
|
367
|
+
root,
|
|
368
|
+
),
|
|
369
|
+
),
|
|
370
|
+
),
|
|
371
|
+
)
|
|
372
|
+
await expect(
|
|
373
|
+
runTestEffect(
|
|
374
|
+
taskCommand({
|
|
375
|
+
subcommand: "create",
|
|
376
|
+
args: ["review"],
|
|
377
|
+
review: "agency",
|
|
378
|
+
ref: "review-me",
|
|
379
|
+
cwd: root,
|
|
380
|
+
silent: true,
|
|
381
|
+
}),
|
|
382
|
+
),
|
|
383
|
+
).rejects.toThrow("already exists")
|
|
384
|
+
const pin = Bun.spawnSync([
|
|
385
|
+
"git",
|
|
386
|
+
"-C",
|
|
387
|
+
join(root, "repos/agency"),
|
|
388
|
+
"show-ref",
|
|
389
|
+
"--verify",
|
|
390
|
+
"refs/agency/reviews/726576696577",
|
|
391
|
+
])
|
|
392
|
+
expect(pin.exitCode).not.toBe(0)
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
test("CLI creation stores no writable execution defaults", async () => {
|
|
396
|
+
await runTestEffect(
|
|
397
|
+
taskCommand({
|
|
398
|
+
subcommand: "create",
|
|
399
|
+
args: ["command-review"],
|
|
400
|
+
review: "agency",
|
|
401
|
+
ref: "origin/review-me",
|
|
402
|
+
cwd: root,
|
|
403
|
+
silent: true,
|
|
404
|
+
}),
|
|
405
|
+
)
|
|
406
|
+
const created = await runTestEffect(
|
|
407
|
+
TaskService.pipe(
|
|
408
|
+
Effect.flatMap((service) => service.show("command-review", root)),
|
|
409
|
+
),
|
|
410
|
+
)
|
|
411
|
+
expect("review" in created.data).toBe(true)
|
|
412
|
+
expect("repo" in created.data).toBe(false)
|
|
413
|
+
expect("branch" in created.data).toBe(false)
|
|
414
|
+
expect("base" in created.data).toBe(false)
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
test("sync observes source movement without changing the pin", async () => {
|
|
418
|
+
const created = await createReview()
|
|
419
|
+
const original = "review" in created.data ? created.data.review.commit : ""
|
|
420
|
+
await Bun.write(join(source, "README.md"), "moved\n")
|
|
421
|
+
await git(["commit", "-am", "move source"], source)
|
|
422
|
+
const synced = await runTestEffect(
|
|
423
|
+
SyncService.pipe(
|
|
424
|
+
Effect.flatMap((service) => service.reconcile({ cwd: root })),
|
|
425
|
+
),
|
|
426
|
+
)
|
|
427
|
+
const state = synced.executions.find(
|
|
428
|
+
(execution) => execution.target === "task:review",
|
|
429
|
+
)
|
|
430
|
+
expect(state?.review?.pinnedCommit).toBe(original)
|
|
431
|
+
expect(state?.review?.sourceCommit).not.toBe(original)
|
|
432
|
+
const current = await runTestEffect(
|
|
433
|
+
TaskService.pipe(
|
|
434
|
+
Effect.flatMap((service) => service.show("review", root)),
|
|
435
|
+
),
|
|
436
|
+
)
|
|
437
|
+
expect("review" in current.data && current.data.review.commit).toBe(
|
|
438
|
+
original,
|
|
439
|
+
)
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
test("archives the pinned checkout after its source branch is deleted", async () => {
|
|
443
|
+
await createReview()
|
|
444
|
+
const workspace = await runTestEffect(
|
|
445
|
+
WorktreeService.pipe(
|
|
446
|
+
Effect.flatMap((service) =>
|
|
447
|
+
service.materialize("review", undefined, root),
|
|
448
|
+
),
|
|
449
|
+
),
|
|
450
|
+
)
|
|
451
|
+
await git(["switch", "main"], source)
|
|
452
|
+
await git(["branch", "-D", "review-me"], source)
|
|
453
|
+
await runTestEffect(
|
|
454
|
+
TaskService.pipe(
|
|
455
|
+
Effect.flatMap((service) =>
|
|
456
|
+
service.setStatus("review", "done", root, {
|
|
457
|
+
summary: "Review completed.",
|
|
458
|
+
}),
|
|
459
|
+
),
|
|
460
|
+
),
|
|
461
|
+
)
|
|
462
|
+
const archived = await runTestEffect(
|
|
463
|
+
ArchiveService.pipe(
|
|
464
|
+
Effect.flatMap((service) => service.archiveTask("review", root)),
|
|
465
|
+
),
|
|
466
|
+
)
|
|
467
|
+
expect(archived.removedWorktrees).toContain(
|
|
468
|
+
join(workspace.codePath, "agency"),
|
|
469
|
+
)
|
|
470
|
+
expect(await Bun.file(join(archived.path, "TASK.md")).exists()).toBe(true)
|
|
471
|
+
})
|
|
472
|
+
})
|