@markjaquith/agency 3.2.13 → 3.2.15
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/package.json +7 -2
- package/src/workbase/schemas.ts +12 -0
- package/fixtures/protocol/orchestration-recipes.json +0 -53
- package/pi-extensions/agency.test.ts +0 -117
- package/src/check-commits.test.ts +0 -58
- package/src/cli-parser.test.ts +0 -945
- package/src/cli.test.ts +0 -1889
- package/src/commands/act.test.ts +0 -418
- package/src/commands/archive.test.ts +0 -236
- package/src/commands/context.test.ts +0 -665
- package/src/commands/description.test.ts +0 -103
- package/src/commands/doctor.test.ts +0 -185
- package/src/commands/epic.test.ts +0 -196
- package/src/commands/init.test.ts +0 -117
- package/src/commands/integration.test.ts +0 -165
- package/src/commands/pr.test.ts +0 -248
- package/src/commands/push.test.ts +0 -56
- package/src/commands/read-only.test.ts +0 -161
- package/src/commands/repo.test.ts +0 -199
- package/src/commands/restore.test.ts +0 -106
- package/src/commands/status.test.ts +0 -113
- package/src/commands/sync.test.ts +0 -200
- package/src/commands/task-phase.test.ts +0 -410
- package/src/commands/task.test.ts +0 -281
- package/src/commands/validate.test.ts +0 -186
- package/src/commands/work.test.ts +0 -1375
- package/src/commands/workbase.test.ts +0 -170
- package/src/graph-schema.test.ts +0 -124
- package/src/protocol.test.ts +0 -163
- package/src/readiness.test.ts +0 -105
- package/src/services/ArchiveBulkService.test.ts +0 -384
- package/src/services/ArchiveService.test.ts +0 -1092
- package/src/services/EpicService.test.ts +0 -74
- package/src/services/FileSystemService.test.ts +0 -81
- package/src/services/GraphMutationService.test.ts +0 -486
- package/src/services/GraphService.test.ts +0 -494
- package/src/services/IntegrationService.test.ts +0 -1091
- package/src/services/LifecycleTransaction.test.ts +0 -145
- package/src/services/PullRequestService.test.ts +0 -716
- package/src/services/PushService.test.ts +0 -408
- package/src/services/ReadinessService.test.ts +0 -290
- package/src/services/RepositoryService.test.ts +0 -789
- package/src/services/ReviewService.test.ts +0 -408
- package/src/services/SyncService.test.ts +0 -1377
- package/src/services/TaskPhaseService.test.ts +0 -852
- package/src/services/VersionControlService.test.ts +0 -62
- package/src/services/WorkbaseService.test.ts +0 -910
- package/src/services/WorktreeLock.test.ts +0 -205
- package/src/services/WorktreePerformance.test.ts +0 -71
- package/src/services/WorktreeService.test.ts +0 -2292
- package/src/test-utils.ts +0 -110
- package/src/usage-log.test.ts +0 -188
- package/src/utils/chooser.test.ts +0 -192
- package/src/utils/effect.test.ts +0 -30
- package/src/utils/interactive.pty.test.ts +0 -128
- package/src/utils/interactive.test.tsx +0 -860
- package/src/utils/process.test.ts +0 -132
- package/src/utils/progress.test.ts +0 -37
- package/src/work-view.test.ts +0 -151
- package/src/workbase/agent-command.test.ts +0 -128
- package/src/workbase/checkout-command.test.ts +0 -62
- package/src/workbase/delivery-command.test.ts +0 -180
- package/src/workbase/dependency-graph.test.ts +0 -50
- package/src/workbase/execution-contract.test.ts +0 -161
- package/src/workbase/frontmatter.test.ts +0 -67
- package/src/workbase/repository-reference.test.ts +0 -25
- package/src/workbase/schemas.test.ts +0 -543
- package/src/workbase/work-target.test.ts +0 -108
- package/src/workbase/worktree-command.test.ts +0 -61
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test"
|
|
2
|
-
import { Schema } from "@effect/schema"
|
|
3
|
-
import { Effect } from "effect"
|
|
4
|
-
import { mkdir } from "node:fs/promises"
|
|
5
|
-
import { dirname, join } from "node:path"
|
|
6
|
-
import { AgencyGraph } from "../graph-schema"
|
|
7
|
-
import {
|
|
8
|
-
cleanupTempDir,
|
|
9
|
-
createTempDir,
|
|
10
|
-
runTestEffect,
|
|
11
|
-
trackDocumentReadConcurrency,
|
|
12
|
-
} from "../test-utils"
|
|
13
|
-
import { documentLoadConcurrency } from "../workbase/document-loading"
|
|
14
|
-
import { FileSystemService } from "./FileSystemService"
|
|
15
|
-
import { GraphService } from "./GraphService"
|
|
16
|
-
import {
|
|
17
|
-
VersionControlService,
|
|
18
|
-
type VersionControlBackend,
|
|
19
|
-
} from "./VersionControlService"
|
|
20
|
-
|
|
21
|
-
const write = async (root: string, path: string, content: string) => {
|
|
22
|
-
const fullPath = join(root, path)
|
|
23
|
-
await mkdir(dirname(fullPath), { recursive: true })
|
|
24
|
-
await Bun.write(fullPath, content)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const createWorkbase = async () => {
|
|
28
|
-
const root = await createTempDir()
|
|
29
|
-
await write(root, "agency.json", '{"version":2}\n')
|
|
30
|
-
await mkdir(join(root, "repos/agency"), { recursive: true })
|
|
31
|
-
await write(
|
|
32
|
-
root,
|
|
33
|
-
"epics/delivery/EPIC.md",
|
|
34
|
-
`---
|
|
35
|
-
ticketUrl: https://example.com/delivery
|
|
36
|
-
repos:
|
|
37
|
-
- repo: agency
|
|
38
|
-
ref: main
|
|
39
|
-
tasks:
|
|
40
|
-
- id: prepare
|
|
41
|
-
- id: ship
|
|
42
|
-
dependsOn: [prepare]
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
# Delivery
|
|
46
|
-
Deliver the graph.
|
|
47
|
-
`,
|
|
48
|
-
)
|
|
49
|
-
await write(
|
|
50
|
-
root,
|
|
51
|
-
"tasks/prepare/TASK.md",
|
|
52
|
-
`---
|
|
53
|
-
ticketUrl: null
|
|
54
|
-
epic: delivery
|
|
55
|
-
repo: agency
|
|
56
|
-
branch: feat/prepare
|
|
57
|
-
base: main
|
|
58
|
-
pr: null
|
|
59
|
-
status: done
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
# Prepare
|
|
63
|
-
`,
|
|
64
|
-
)
|
|
65
|
-
await write(
|
|
66
|
-
root,
|
|
67
|
-
"tasks/ship/TASK.md",
|
|
68
|
-
`---
|
|
69
|
-
ticketUrl: null
|
|
70
|
-
epic: delivery
|
|
71
|
-
phases:
|
|
72
|
-
- id: implement
|
|
73
|
-
- id: verify
|
|
74
|
-
dependsOn: [implement]
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
# Ship
|
|
78
|
-
`,
|
|
79
|
-
)
|
|
80
|
-
await write(
|
|
81
|
-
root,
|
|
82
|
-
"tasks/ship/phases/implement/PHASE.md",
|
|
83
|
-
`---
|
|
84
|
-
repo: agency
|
|
85
|
-
branch: feat/implement
|
|
86
|
-
base: main
|
|
87
|
-
pr: null
|
|
88
|
-
status: open
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
# Implement
|
|
92
|
-
`,
|
|
93
|
-
)
|
|
94
|
-
await write(
|
|
95
|
-
root,
|
|
96
|
-
"tasks/ship/phases/verify/PHASE.md",
|
|
97
|
-
`---
|
|
98
|
-
repo: agency
|
|
99
|
-
branch: feat/verify
|
|
100
|
-
base: main
|
|
101
|
-
pr: null
|
|
102
|
-
status: open
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
# Verify
|
|
106
|
-
`,
|
|
107
|
-
)
|
|
108
|
-
return root
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const getGraph = (root: string, options: Record<string, unknown> = {}) =>
|
|
112
|
-
runTestEffect(
|
|
113
|
-
GraphService.pipe(
|
|
114
|
-
Effect.flatMap((service) => service.get({ cwd: root, ...options })),
|
|
115
|
-
),
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
describe("GraphService", () => {
|
|
119
|
-
const roots: string[] = []
|
|
120
|
-
|
|
121
|
-
afterEach(async () => {
|
|
122
|
-
await Promise.all(roots.splice(0).map(cleanupTempDir))
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
test("exports deterministic nodes, typed edges, and computed state", async () => {
|
|
126
|
-
const root = await createWorkbase()
|
|
127
|
-
roots.push(root)
|
|
128
|
-
const graph = await getGraph(root)
|
|
129
|
-
|
|
130
|
-
expect(Schema.decodeUnknownSync(AgencyGraph)(graph)).toEqual(graph)
|
|
131
|
-
expect(graph.workbase.vcs).toBe("git")
|
|
132
|
-
expect(graph.nodes.map((node) => node.id)).toEqual([
|
|
133
|
-
"epic:delivery",
|
|
134
|
-
"execution-unit:phase/ship/implement",
|
|
135
|
-
"execution-unit:phase/ship/verify",
|
|
136
|
-
"execution-unit:task/prepare",
|
|
137
|
-
"phase:ship/implement",
|
|
138
|
-
"phase:ship/verify",
|
|
139
|
-
"repository:agency",
|
|
140
|
-
"task:prepare",
|
|
141
|
-
"task:ship",
|
|
142
|
-
])
|
|
143
|
-
expect(graph.edges).toContainEqual({
|
|
144
|
-
id: "depends_on:task:ship->task:prepare",
|
|
145
|
-
kind: "depends_on",
|
|
146
|
-
from: "task:ship",
|
|
147
|
-
to: "task:prepare",
|
|
148
|
-
})
|
|
149
|
-
expect(graph.edges).toContainEqual({
|
|
150
|
-
id: "owns:phase:ship/verify->execution-unit:phase/ship/verify",
|
|
151
|
-
kind: "owns",
|
|
152
|
-
from: "phase:ship/verify",
|
|
153
|
-
to: "execution-unit:phase/ship/verify",
|
|
154
|
-
})
|
|
155
|
-
expect(graph.edges).toContainEqual({
|
|
156
|
-
id: "writes:execution-unit:task/prepare->repository:agency",
|
|
157
|
-
kind: "writes",
|
|
158
|
-
from: "execution-unit:task/prepare",
|
|
159
|
-
to: "repository:agency",
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
const prepare = graph.nodes.find((node) => node.id === "task:prepare")!
|
|
163
|
-
const ship = graph.nodes.find((node) => node.id === "task:ship")!
|
|
164
|
-
const implement = graph.nodes.find(
|
|
165
|
-
(node) => node.id === "phase:ship/implement",
|
|
166
|
-
)!
|
|
167
|
-
const verify = graph.nodes.find((node) => node.id === "phase:ship/verify")!
|
|
168
|
-
expect(prepare.dependents).toEqual(["task:ship"])
|
|
169
|
-
expect(prepare.readiness).toMatchObject({
|
|
170
|
-
ready: false,
|
|
171
|
-
terminal: true,
|
|
172
|
-
})
|
|
173
|
-
expect(ship.readiness).toMatchObject({
|
|
174
|
-
ready: true,
|
|
175
|
-
blocked: false,
|
|
176
|
-
terminal: false,
|
|
177
|
-
})
|
|
178
|
-
expect(ship.aggregate).toMatchObject({ total: 2, open: 2, terminal: 0 })
|
|
179
|
-
expect(implement.dependents).toEqual(["phase:ship/verify"])
|
|
180
|
-
expect(implement.readiness).toMatchObject({ ready: true, blocked: false })
|
|
181
|
-
expect(
|
|
182
|
-
graph.nodes.find(
|
|
183
|
-
(node) => node.id === "execution-unit:phase/ship/implement",
|
|
184
|
-
)?.readiness,
|
|
185
|
-
).toMatchObject({
|
|
186
|
-
ready: true,
|
|
187
|
-
blocked: false,
|
|
188
|
-
blockedBy: [],
|
|
189
|
-
terminal: false,
|
|
190
|
-
})
|
|
191
|
-
expect(verify.readiness).toMatchObject({
|
|
192
|
-
ready: false,
|
|
193
|
-
blocked: true,
|
|
194
|
-
blockedBy: ["phase:ship/implement"],
|
|
195
|
-
terminal: false,
|
|
196
|
-
blockers: [
|
|
197
|
-
{
|
|
198
|
-
kind: "dependency",
|
|
199
|
-
id: "phase:ship/implement",
|
|
200
|
-
status: "open",
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
})
|
|
204
|
-
const epic = graph.nodes.find((node) => node.id === "epic:delivery")!
|
|
205
|
-
expect(epic.aggregate).toMatchObject({ total: 3, done: 1, open: 2 })
|
|
206
|
-
expect(graph.summary).toEqual({
|
|
207
|
-
status: "open",
|
|
208
|
-
total: 3,
|
|
209
|
-
open: 2,
|
|
210
|
-
working: 0,
|
|
211
|
-
delegated: 0,
|
|
212
|
-
done: 1,
|
|
213
|
-
dropped: 0,
|
|
214
|
-
terminal: 1,
|
|
215
|
-
})
|
|
216
|
-
expect(await getGraph(root)).toEqual(graph)
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
test("bounds document reads across nested task and phase traversal", async () => {
|
|
220
|
-
const root = await createWorkbase()
|
|
221
|
-
roots.push(root)
|
|
222
|
-
await Promise.all(
|
|
223
|
-
Array.from({ length: documentLoadConcurrency + 8 }, (_, index) =>
|
|
224
|
-
write(
|
|
225
|
-
root,
|
|
226
|
-
`tasks/ship/phases/extra-${index}/PHASE.md`,
|
|
227
|
-
`---\nrepo: agency\nbranch: extra-${index}\nbase: main\npr: null\nstatus: open\n---\n`,
|
|
228
|
-
),
|
|
229
|
-
),
|
|
230
|
-
)
|
|
231
|
-
const fs = await Effect.runPromise(
|
|
232
|
-
FileSystemService.pipe(Effect.provide(FileSystemService.Default)),
|
|
233
|
-
)
|
|
234
|
-
const tracked = trackDocumentReadConcurrency(fs)
|
|
235
|
-
|
|
236
|
-
await runTestEffect(
|
|
237
|
-
GraphService.pipe(
|
|
238
|
-
Effect.flatMap((service) => service.get({ cwd: root })),
|
|
239
|
-
Effect.provideService(FileSystemService, tracked.fs),
|
|
240
|
-
),
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
expect(tracked.maximum()).toBe(documentLoadConcurrency)
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
test("does not resolve a VCS backend when git details are not requested", async () => {
|
|
247
|
-
const root = await createWorkbase()
|
|
248
|
-
roots.push(root)
|
|
249
|
-
let calls = 0
|
|
250
|
-
const graph = await runTestEffect(
|
|
251
|
-
GraphService.pipe(
|
|
252
|
-
Effect.flatMap((service) => service.get({ cwd: root })),
|
|
253
|
-
Effect.provideService(VersionControlService, {
|
|
254
|
-
_tag: "VersionControlService",
|
|
255
|
-
forWorkbase: () => {
|
|
256
|
-
calls += 1
|
|
257
|
-
throw new Error("unexpected VCS lookup")
|
|
258
|
-
},
|
|
259
|
-
}),
|
|
260
|
-
),
|
|
261
|
-
)
|
|
262
|
-
|
|
263
|
-
expect(graph.nodes.length).toBeGreaterThan(0)
|
|
264
|
-
expect(calls).toBe(0)
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
test("propagates directory discovery failures", async () => {
|
|
268
|
-
const root = await createTempDir()
|
|
269
|
-
roots.push(root)
|
|
270
|
-
await write(root, "agency.json", '{"version":2}\n')
|
|
271
|
-
await write(root, "tasks", "not a directory")
|
|
272
|
-
|
|
273
|
-
await expect(getGraph(root)).rejects.toThrow(
|
|
274
|
-
`Failed to read directory: ${join(root, "tasks")}`,
|
|
275
|
-
)
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
test("never reports active or terminal execution units as ready", async () => {
|
|
279
|
-
const root = await createWorkbase()
|
|
280
|
-
roots.push(root)
|
|
281
|
-
const path = "tasks/ship/phases/implement/PHASE.md"
|
|
282
|
-
|
|
283
|
-
for (const status of ["working", "delegated", "done", "dropped"]) {
|
|
284
|
-
await write(
|
|
285
|
-
root,
|
|
286
|
-
path,
|
|
287
|
-
`---
|
|
288
|
-
repo: agency
|
|
289
|
-
branch: feat/implement
|
|
290
|
-
base: main
|
|
291
|
-
pr: null
|
|
292
|
-
status: ${status}
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
# Implement
|
|
296
|
-
`,
|
|
297
|
-
)
|
|
298
|
-
const graph = await getGraph(root, {
|
|
299
|
-
kinds: ["execution-unit"],
|
|
300
|
-
ready: true,
|
|
301
|
-
})
|
|
302
|
-
expect(graph.nodes.map((node) => node.key)).not.toContain(
|
|
303
|
-
"phase/ship/implement",
|
|
304
|
-
)
|
|
305
|
-
}
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
test("applies filters after computing graph state", async () => {
|
|
309
|
-
const root = await createWorkbase()
|
|
310
|
-
roots.push(root)
|
|
311
|
-
|
|
312
|
-
const ready = await getGraph(root, {
|
|
313
|
-
ready: true,
|
|
314
|
-
kinds: ["execution-unit"],
|
|
315
|
-
repositories: ["agency"],
|
|
316
|
-
statuses: ["open"],
|
|
317
|
-
})
|
|
318
|
-
expect(ready.nodes.map((node) => node.id)).toEqual([
|
|
319
|
-
"execution-unit:phase/ship/implement",
|
|
320
|
-
])
|
|
321
|
-
expect(ready.edges).toEqual([])
|
|
322
|
-
|
|
323
|
-
const blocked = await getGraph(root, { blocked: true, kinds: ["phase"] })
|
|
324
|
-
expect(blocked.nodes.map((node) => node.id)).toEqual(["phase:ship/verify"])
|
|
325
|
-
})
|
|
326
|
-
|
|
327
|
-
test("does not report structurally invalid phases as ready", async () => {
|
|
328
|
-
const root = await createWorkbase()
|
|
329
|
-
roots.push(root)
|
|
330
|
-
await write(
|
|
331
|
-
root,
|
|
332
|
-
"tasks/ship/phases/orphan/PHASE.md",
|
|
333
|
-
`---
|
|
334
|
-
repo: agency
|
|
335
|
-
branch: feat/orphan
|
|
336
|
-
base: main
|
|
337
|
-
pr: null
|
|
338
|
-
status: open
|
|
339
|
-
---
|
|
340
|
-
|
|
341
|
-
# Orphan
|
|
342
|
-
`,
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
const graph = await getGraph(root, { kinds: ["phase"], ready: true })
|
|
346
|
-
expect(graph.nodes.map((node) => node.id)).not.toContain(
|
|
347
|
-
"phase:ship/orphan",
|
|
348
|
-
)
|
|
349
|
-
const complete = await getGraph(root)
|
|
350
|
-
expect(
|
|
351
|
-
complete.nodes.find((node) => node.id === "phase:ship/orphan")?.readiness,
|
|
352
|
-
).toMatchObject({
|
|
353
|
-
ready: false,
|
|
354
|
-
blocked: true,
|
|
355
|
-
blockers: [
|
|
356
|
-
{
|
|
357
|
-
kind: "validation",
|
|
358
|
-
id: "tasks/ship/TASK.md",
|
|
359
|
-
reason: "Unlisted phase 'orphan'",
|
|
360
|
-
},
|
|
361
|
-
],
|
|
362
|
-
blockedBy: ["tasks/ship/TASK.md"],
|
|
363
|
-
})
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
test("inherits parent epic validation blockers", async () => {
|
|
367
|
-
const root = await createWorkbase()
|
|
368
|
-
roots.push(root)
|
|
369
|
-
await write(
|
|
370
|
-
root,
|
|
371
|
-
"tasks/unlisted/TASK.md",
|
|
372
|
-
`---
|
|
373
|
-
ticketUrl: null
|
|
374
|
-
epic: delivery
|
|
375
|
-
repo: agency
|
|
376
|
-
branch: feat/unlisted
|
|
377
|
-
base: main
|
|
378
|
-
pr: null
|
|
379
|
-
status: open
|
|
380
|
-
---
|
|
381
|
-
|
|
382
|
-
# Unlisted
|
|
383
|
-
`,
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
const graph = await getGraph(root)
|
|
387
|
-
const execution = graph.nodes.find(
|
|
388
|
-
(node) => node.id === "execution-unit:task/unlisted",
|
|
389
|
-
)
|
|
390
|
-
|
|
391
|
-
expect(execution?.readiness).toMatchObject({
|
|
392
|
-
ready: false,
|
|
393
|
-
blockers: [
|
|
394
|
-
{
|
|
395
|
-
kind: "validation",
|
|
396
|
-
reason: "Epic does not list child task 'unlisted'",
|
|
397
|
-
},
|
|
398
|
-
],
|
|
399
|
-
})
|
|
400
|
-
})
|
|
401
|
-
|
|
402
|
-
test("adds body, workspace, git, and PR details only when requested", async () => {
|
|
403
|
-
const root = await createWorkbase()
|
|
404
|
-
roots.push(root)
|
|
405
|
-
const baseline = await getGraph(root)
|
|
406
|
-
for (const node of baseline.nodes) {
|
|
407
|
-
expect("body" in node).toBe(false)
|
|
408
|
-
expect("workspace" in node).toBe(false)
|
|
409
|
-
expect("git" in node).toBe(false)
|
|
410
|
-
expect("pr" in node).toBe(false)
|
|
411
|
-
}
|
|
412
|
-
expect(baseline.workbase.root).toBeUndefined()
|
|
413
|
-
|
|
414
|
-
const detailed = await getGraph(root, {
|
|
415
|
-
include: ["bodies", "workspace", "git", "pr"],
|
|
416
|
-
backend: {
|
|
417
|
-
kind: "git",
|
|
418
|
-
inspectRepository: () => Effect.succeed(null),
|
|
419
|
-
listWorkspaces: () => Effect.succeed([]),
|
|
420
|
-
resolveRevision: () => Effect.succeed(null),
|
|
421
|
-
workspaceHead: () => Effect.succeed(null),
|
|
422
|
-
workspaceDirty: () => Effect.succeed(null),
|
|
423
|
-
remoteUrl: () => Effect.succeed(null),
|
|
424
|
-
} as unknown as VersionControlBackend,
|
|
425
|
-
})
|
|
426
|
-
expect(detailed.includes).toEqual(["bodies", "git", "pr", "workspace"])
|
|
427
|
-
expect(detailed.workbase.root).toBe(root)
|
|
428
|
-
const epic = detailed.nodes.find((node) => node.id === "epic:delivery")
|
|
429
|
-
const repository = detailed.nodes.find(
|
|
430
|
-
(node) => node.id === "repository:agency",
|
|
431
|
-
)
|
|
432
|
-
const execution = detailed.nodes.find(
|
|
433
|
-
(node) => node.id === "execution-unit:phase/ship/implement",
|
|
434
|
-
)
|
|
435
|
-
if (epic?.kind !== "epic") throw new Error("Missing epic node")
|
|
436
|
-
if (repository?.kind !== "repository") {
|
|
437
|
-
throw new Error("Missing repository node")
|
|
438
|
-
}
|
|
439
|
-
if (execution?.kind !== "execution-unit") {
|
|
440
|
-
throw new Error("Missing execution-unit node")
|
|
441
|
-
}
|
|
442
|
-
expect(epic?.body).toContain("Deliver the graph")
|
|
443
|
-
expect(repository?.git).toMatchObject({ kind: null })
|
|
444
|
-
expect(execution?.pr).toEqual({ url: null, state: "none" })
|
|
445
|
-
expect(Schema.decodeUnknownSync(AgencyGraph)(detailed)).toEqual(detailed)
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
test("reuses repository metadata and revision lookups for git details", async () => {
|
|
449
|
-
const root = await createWorkbase()
|
|
450
|
-
roots.push(root)
|
|
451
|
-
const calls = {
|
|
452
|
-
listWorkspaces: 0,
|
|
453
|
-
remoteUrl: 0,
|
|
454
|
-
resolveRevision: new Map<string, number>(),
|
|
455
|
-
}
|
|
456
|
-
const backend = {
|
|
457
|
-
kind: "git",
|
|
458
|
-
inspectRepository: () => Effect.succeed(null),
|
|
459
|
-
listWorkspaces: () =>
|
|
460
|
-
Effect.sync(() => {
|
|
461
|
-
calls.listWorkspaces += 1
|
|
462
|
-
return []
|
|
463
|
-
}),
|
|
464
|
-
remoteUrl: () =>
|
|
465
|
-
Effect.sync(() => {
|
|
466
|
-
calls.remoteUrl += 1
|
|
467
|
-
return null
|
|
468
|
-
}),
|
|
469
|
-
resolveRevision: (_path: string, revision: string) =>
|
|
470
|
-
Effect.sync(() => {
|
|
471
|
-
calls.resolveRevision.set(
|
|
472
|
-
revision,
|
|
473
|
-
(calls.resolveRevision.get(revision) ?? 0) + 1,
|
|
474
|
-
)
|
|
475
|
-
return revision
|
|
476
|
-
}),
|
|
477
|
-
workspaceHead: () => Effect.succeed(null),
|
|
478
|
-
workspaceDirty: () => Effect.succeed(null),
|
|
479
|
-
} as unknown as VersionControlBackend
|
|
480
|
-
|
|
481
|
-
await getGraph(root, { include: ["git"], backend })
|
|
482
|
-
|
|
483
|
-
expect(calls.listWorkspaces).toBe(0)
|
|
484
|
-
expect(calls.remoteUrl).toBe(1)
|
|
485
|
-
expect(calls.resolveRevision).toEqual(
|
|
486
|
-
new Map([
|
|
487
|
-
["feat/prepare", 1],
|
|
488
|
-
["feat/implement", 1],
|
|
489
|
-
["feat/verify", 1],
|
|
490
|
-
["main", 1],
|
|
491
|
-
]),
|
|
492
|
-
)
|
|
493
|
-
})
|
|
494
|
-
})
|