@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.
Files changed (69) hide show
  1. package/package.json +7 -2
  2. package/src/workbase/schemas.ts +12 -0
  3. package/fixtures/protocol/orchestration-recipes.json +0 -53
  4. package/pi-extensions/agency.test.ts +0 -117
  5. package/src/check-commits.test.ts +0 -58
  6. package/src/cli-parser.test.ts +0 -945
  7. package/src/cli.test.ts +0 -1889
  8. package/src/commands/act.test.ts +0 -418
  9. package/src/commands/archive.test.ts +0 -236
  10. package/src/commands/context.test.ts +0 -665
  11. package/src/commands/description.test.ts +0 -103
  12. package/src/commands/doctor.test.ts +0 -185
  13. package/src/commands/epic.test.ts +0 -196
  14. package/src/commands/init.test.ts +0 -117
  15. package/src/commands/integration.test.ts +0 -165
  16. package/src/commands/pr.test.ts +0 -248
  17. package/src/commands/push.test.ts +0 -56
  18. package/src/commands/read-only.test.ts +0 -161
  19. package/src/commands/repo.test.ts +0 -199
  20. package/src/commands/restore.test.ts +0 -106
  21. package/src/commands/status.test.ts +0 -113
  22. package/src/commands/sync.test.ts +0 -200
  23. package/src/commands/task-phase.test.ts +0 -410
  24. package/src/commands/task.test.ts +0 -281
  25. package/src/commands/validate.test.ts +0 -186
  26. package/src/commands/work.test.ts +0 -1375
  27. package/src/commands/workbase.test.ts +0 -170
  28. package/src/graph-schema.test.ts +0 -124
  29. package/src/protocol.test.ts +0 -163
  30. package/src/readiness.test.ts +0 -105
  31. package/src/services/ArchiveBulkService.test.ts +0 -384
  32. package/src/services/ArchiveService.test.ts +0 -1092
  33. package/src/services/EpicService.test.ts +0 -74
  34. package/src/services/FileSystemService.test.ts +0 -81
  35. package/src/services/GraphMutationService.test.ts +0 -486
  36. package/src/services/GraphService.test.ts +0 -494
  37. package/src/services/IntegrationService.test.ts +0 -1091
  38. package/src/services/LifecycleTransaction.test.ts +0 -145
  39. package/src/services/PullRequestService.test.ts +0 -716
  40. package/src/services/PushService.test.ts +0 -408
  41. package/src/services/ReadinessService.test.ts +0 -290
  42. package/src/services/RepositoryService.test.ts +0 -789
  43. package/src/services/ReviewService.test.ts +0 -408
  44. package/src/services/SyncService.test.ts +0 -1377
  45. package/src/services/TaskPhaseService.test.ts +0 -852
  46. package/src/services/VersionControlService.test.ts +0 -62
  47. package/src/services/WorkbaseService.test.ts +0 -910
  48. package/src/services/WorktreeLock.test.ts +0 -205
  49. package/src/services/WorktreePerformance.test.ts +0 -71
  50. package/src/services/WorktreeService.test.ts +0 -2292
  51. package/src/test-utils.ts +0 -110
  52. package/src/usage-log.test.ts +0 -188
  53. package/src/utils/chooser.test.ts +0 -192
  54. package/src/utils/effect.test.ts +0 -30
  55. package/src/utils/interactive.pty.test.ts +0 -128
  56. package/src/utils/interactive.test.tsx +0 -860
  57. package/src/utils/process.test.ts +0 -132
  58. package/src/utils/progress.test.ts +0 -37
  59. package/src/work-view.test.ts +0 -151
  60. package/src/workbase/agent-command.test.ts +0 -128
  61. package/src/workbase/checkout-command.test.ts +0 -62
  62. package/src/workbase/delivery-command.test.ts +0 -180
  63. package/src/workbase/dependency-graph.test.ts +0 -50
  64. package/src/workbase/execution-contract.test.ts +0 -161
  65. package/src/workbase/frontmatter.test.ts +0 -67
  66. package/src/workbase/repository-reference.test.ts +0 -25
  67. package/src/workbase/schemas.test.ts +0 -543
  68. package/src/workbase/work-target.test.ts +0 -108
  69. package/src/workbase/worktree-command.test.ts +0 -61
@@ -1,665 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"
2
- import { mkdir, readFile as nodeReadFile, rename, rm } from "node:fs/promises"
3
- import { dirname, join } from "node:path"
4
- import { Effect } from "effect"
5
- import {
6
- captureLogs,
7
- cleanupTempDir,
8
- createTempDir,
9
- runTestEffect,
10
- trackDocumentReadConcurrency,
11
- } from "../test-utils"
12
- import { documentLoadConcurrency } from "../workbase/document-loading"
13
- import { ContextService } from "../services/ContextService"
14
- import { FileSystemService } from "../services/FileSystemService"
15
- import { context } from "./context"
16
-
17
- const write = async (root: string, path: string, content: string) => {
18
- const fullPath = join(root, path)
19
- await mkdir(dirname(fullPath), { recursive: true })
20
- await Bun.write(fullPath, content)
21
- }
22
-
23
- const run = async (cwd: string, args: string[]) => {
24
- const process = Bun.spawn(args, { cwd, stdout: "pipe", stderr: "pipe" })
25
- const [exitCode, stderr] = await Promise.all([
26
- process.exited,
27
- new Response(process.stderr).text(),
28
- ])
29
- if (exitCode !== 0) throw new Error(`${args.join(" ")}: ${stderr}`)
30
- }
31
-
32
- const readContext = async (
33
- root: string,
34
- target: string | undefined,
35
- compact = false,
36
- full = false,
37
- ) => {
38
- const logs = await captureLogs(() =>
39
- runTestEffect(context({ cwd: root, target, compact, full, json: true })),
40
- )
41
- expect(logs).toHaveLength(1)
42
- return JSON.parse(logs[0]!)
43
- }
44
-
45
- describe("context", () => {
46
- let root: string
47
-
48
- beforeEach(async () => {
49
- root = await createTempDir()
50
- await write(root, "agency.json", '{"version":2}\n')
51
- for (const repo of ["agency", "docs"]) {
52
- const path = join(root, "repos", repo)
53
- await mkdir(path, { recursive: true })
54
- await run(root, ["git", "init", "--initial-branch=main", path])
55
- await run(path, ["git", "config", "user.email", "test@example.com"])
56
- await run(path, ["git", "config", "user.name", "Test"])
57
- await Bun.write(join(path, "README.md"), `${repo}\n`)
58
- await run(path, ["git", "add", "README.md"])
59
- await run(path, ["git", "commit", "-m", "initial"])
60
- }
61
-
62
- await write(
63
- root,
64
- "epics/contract/EPIC.md",
65
- `---
66
- ticketUrl: https://example.com/contract
67
- repos:
68
- - repo: agency
69
- ref: main
70
- tasks:
71
- - id: foundations
72
- - id: agent-contract
73
- dependsOn: [foundations]
74
- ---
75
-
76
- # Contract
77
- `,
78
- )
79
- await write(
80
- root,
81
- "tasks/foundations/TASK.md",
82
- `---
83
- ticketUrl: null
84
- epic: contract
85
- repo: agency
86
- branch: foundations
87
- base: main
88
- pr: https://github.com/example/agency/pull/1
89
- status: done
90
- ---
91
-
92
- # Foundations
93
- `,
94
- )
95
- await write(
96
- root,
97
- "tasks/agent-contract/TASK.md",
98
- `---
99
- ticketUrl: null
100
- epic: contract
101
- phases:
102
- - id: schema
103
- - id: context-command
104
- dependsOn: [schema]
105
- ---
106
-
107
- # Agent contract
108
- Task prose.
109
- `,
110
- )
111
- await write(
112
- root,
113
- "tasks/agent-contract/phases/schema/PHASE.md",
114
- `---
115
- repo: agency
116
- branch: schema
117
- base: main
118
- pr: https://github.com/example/agency/pull/2
119
- status: done
120
- ---
121
-
122
- # Schema
123
- `,
124
- )
125
- await write(
126
- root,
127
- "tasks/agent-contract/phases/context-command/PHASE.md",
128
- `---
129
- repo: agency
130
- repos:
131
- - repo: docs
132
- ref: main
133
- branch: feat/context
134
- base: main
135
- pr: null
136
- status: open
137
- ---
138
-
139
- # Context command
140
- Phase prose.
141
- `,
142
- )
143
-
144
- const code = join(root, "tasks/agent-contract/phases/context-command/code")
145
- await mkdir(code, { recursive: true })
146
- await run(join(root, "repos/agency"), [
147
- "git",
148
- "worktree",
149
- "add",
150
- "-b",
151
- "feat/context",
152
- join(code, "agency"),
153
- "main",
154
- ])
155
- await run(join(root, "repos/docs"), [
156
- "git",
157
- "worktree",
158
- "add",
159
- "--detach",
160
- join(code, "docs"),
161
- "main",
162
- ])
163
- })
164
-
165
- test("bounds document reads across nested task and phase traversal", async () => {
166
- await Promise.all(
167
- Array.from({ length: documentLoadConcurrency + 8 }, (_, index) =>
168
- write(
169
- root,
170
- `tasks/agent-contract/phases/extra-${index}/PHASE.md`,
171
- `---\nrepo: agency\nbranch: extra-${index}\nbase: main\npr: null\nstatus: open\n---\n`,
172
- ),
173
- ),
174
- )
175
- const fs = await Effect.runPromise(
176
- FileSystemService.pipe(Effect.provide(FileSystemService.Default)),
177
- )
178
- const tracked = trackDocumentReadConcurrency(fs)
179
-
180
- await runTestEffect(
181
- ContextService.pipe(
182
- Effect.flatMap((service) =>
183
- service.get({ cwd: root, target: "foundations" }),
184
- ),
185
- Effect.provideService(FileSystemService, tracked.fs),
186
- ),
187
- )
188
-
189
- expect(tracked.maximum()).toBe(documentLoadConcurrency)
190
- })
191
-
192
- afterEach(async () => {
193
- await cleanupTempDir(root)
194
- })
195
-
196
- test("returns full phase context with graph, authority, Git, and validation state", async () => {
197
- const target = "tasks/agent-contract/phases/context-command"
198
- const result = await readContext(root, target, false, true)
199
-
200
- expect(result).toMatchObject({
201
- projection: "complete",
202
- workbase: { root, version: 2 },
203
- target: {
204
- kind: "phase",
205
- taskId: "agent-contract",
206
- phaseId: "context-command",
207
- },
208
- graph: {
209
- parent: { kind: "task", id: "agent-contract" },
210
- dependencies: ["schema"],
211
- readiness: { ready: true, blocked: false, blockers: [] },
212
- aggregate: { status: "open", total: 1, open: 1 },
213
- },
214
- authority: {
215
- mode: "execution",
216
- writable: { repo: "agency", branch: "feat/context", base: "main" },
217
- references: [{ repo: "docs", ref: "main" }],
218
- },
219
- workspace: {
220
- materialization: "complete",
221
- writable: {
222
- materialized: true,
223
- registered: true,
224
- checkoutBranch: "feat/context",
225
- detached: false,
226
- },
227
- references: [
228
- {
229
- repo: "docs",
230
- materialized: true,
231
- registered: true,
232
- detached: true,
233
- },
234
- ],
235
- },
236
- pr: { url: null, state: "none" },
237
- validation: { valid: true, warnings: [] },
238
- })
239
- expect(result.documents.epic.body).toContain("# Contract")
240
- expect(result.documents.task.body).toContain("Task prose.")
241
- expect(result.documents.phase.body).toContain("Phase prose.")
242
- expect(result.documents.phase.sha256).toMatch(/^[a-f0-9]{64}$/)
243
- expect(result.authority.documents.writable).toEqual([
244
- join(root, "tasks/agent-contract/TASK.md"),
245
- join(root, "tasks/agent-contract/phases/context-command/PHASE.md"),
246
- ])
247
- expect(result.workspace.writable.branchCommit).toMatch(/^[a-f0-9]{40}$/)
248
- expect(result.workspace.writable.baseCommit).toMatch(/^[a-f0-9]{40}$/)
249
- expect(result.workspace.references[0].resolvedCommit).toMatch(
250
- /^[a-f0-9]{40}$/,
251
- )
252
- })
253
-
254
- test("defaults to compact while preserving explicit compact compatibility", async () => {
255
- const result = await readContext(
256
- root,
257
- "tasks/agent-contract/phases/context-command/code/agency",
258
- )
259
- const explicit = await readContext(
260
- root,
261
- "tasks/agent-contract/phases/context-command/code/agency",
262
- true,
263
- )
264
-
265
- expect(result.projection).toBe("compact")
266
- expect(explicit).toEqual(result)
267
- expect(result.target.kind).toBe("phase")
268
- expect(result.documents.phase.body).toBeUndefined()
269
- expect(result.documents.phase.data.branch).toBe("feat/context")
270
- expect(result.documents.phase.sha256).toMatch(/^[a-f0-9]{64}$/)
271
- expect(result.workspace.writable).toEqual({
272
- materialized: true,
273
- registered: true,
274
- })
275
- expect(result.authority.writable.checkoutPath).toContain("code/agency")
276
- expect(result.authority.documents.writable).toEqual([
277
- join(root, "tasks/agent-contract/TASK.md"),
278
- join(root, "tasks/agent-contract/phases/context-command/PHASE.md"),
279
- ])
280
- })
281
-
282
- test("reports dependency and validation blockers deterministically", async () => {
283
- await write(
284
- root,
285
- "tasks/agent-contract/phases/schema/PHASE.md",
286
- `---
287
- repo: missing
288
- branch: schema
289
- base: main
290
- pr: null
291
- status: dropped
292
- ---
293
-
294
- # Schema
295
- `,
296
- )
297
- const result = await readContext(
298
- root,
299
- "tasks/agent-contract/phases/context-command",
300
- )
301
-
302
- expect(result.graph.readiness.ready).toBe(false)
303
- expect(result.graph.readiness.blockers).toContainEqual({
304
- kind: "dependency",
305
- id: "schema",
306
- status: "dropped",
307
- reason: "Phase dependency is dropped",
308
- })
309
- expect(result.validation.valid).toBe(false)
310
- expect(result.validation.warnings).toContainEqual({
311
- path: "tasks/agent-contract/phases/schema/PHASE.md",
312
- message: "Unknown repository alias 'missing'",
313
- })
314
- })
315
-
316
- test("reads each workbase document at most once per invocation", async () => {
317
- const unrelatedArchive = join(root, "archive/tasks/unrelated/TASK.md")
318
- await write(
319
- root,
320
- "archive/tasks/unrelated/TASK.md",
321
- `---
322
- ticketUrl: null
323
- repo: agency
324
- branch: unrelated
325
- base: main
326
- pr: null
327
- status: done
328
- ---
329
- `,
330
- )
331
- const reads = new Map<string, number>()
332
- const originalFile = Bun.file
333
- Bun.file = mock((path: string) => {
334
- const file = originalFile(path)
335
- return Object.assign(file, {
336
- text: async () => {
337
- reads.set(path, (reads.get(path) ?? 0) + 1)
338
- return nodeReadFile(path, "utf8")
339
- },
340
- })
341
- }) as unknown as typeof Bun.file
342
- try {
343
- await readContext(root, "tasks/agent-contract/phases/context-command")
344
- } finally {
345
- Bun.file = originalFile
346
- }
347
-
348
- const documents = [...reads].filter(([path]) =>
349
- /(?:EPIC|TASK|PHASE)\.md$/.test(path),
350
- )
351
- expect(documents.length).toBeGreaterThan(0)
352
- expect(documents.every(([, count]) => count === 1)).toBe(true)
353
- expect(reads.has(unrelatedArchive)).toBe(false)
354
- })
355
-
356
- test("limits repository inspection and overlaps independent Git observations", async () => {
357
- const commands: string[][] = []
358
- let activeGitCommands = 0
359
- let maxActiveGitCommands = 0
360
- const originalSpawn = Bun.spawn
361
- ;(Bun as { spawn: unknown }).spawn = mock(((
362
- args: string[],
363
- options: Parameters<typeof Bun.spawn>[1],
364
- ) => {
365
- const command = [...args]
366
- const child = originalSpawn(command, options)
367
- if (command[0] !== "git") return child
368
- commands.push(command)
369
- activeGitCommands += 1
370
- maxActiveGitCommands = Math.max(maxActiveGitCommands, activeGitCommands)
371
- const exited = child.exited.finally(() => {
372
- activeGitCommands -= 1
373
- })
374
- return new Proxy(child, {
375
- get(target, property) {
376
- return property === "exited"
377
- ? exited
378
- : Reflect.get(target, property, target)
379
- },
380
- })
381
- }) as unknown as typeof Bun.spawn)
382
- try {
383
- await readContext(root, "tasks/agent-contract/phases/context-command")
384
- } finally {
385
- ;(Bun as { spawn: unknown }).spawn = originalSpawn
386
- }
387
-
388
- expect(
389
- commands.some(
390
- (command) =>
391
- command.includes("config") && command.includes("--show-origin"),
392
- ),
393
- ).toBe(false)
394
- expect(maxActiveGitCommands).toBeGreaterThanOrEqual(3)
395
- })
396
-
397
- test("resolves a bare task ID and returns root discovery context", async () => {
398
- const task = await readContext(root, "agent-contract")
399
- expect(task.target).toMatchObject({
400
- kind: "task",
401
- taskId: "agent-contract",
402
- })
403
- expect(task.graph.parent).toEqual({ kind: "epic", id: "contract" })
404
- expect(task.authority.documents.writable).toEqual([])
405
-
406
- const executionTask = await readContext(root, "foundations")
407
- expect(executionTask.authority.documents.writable).toEqual([
408
- join(root, "tasks/foundations/TASK.md"),
409
- ])
410
-
411
- const workbase = await readContext(root, ".")
412
- expect(workbase).toMatchObject({
413
- projection: "compact",
414
- workbase: { root, version: 2 },
415
- target: { kind: "workbase", path: root },
416
- hint: "Run agency context . --full --json to include document prose.",
417
- authority: {
418
- mode: "orchestration",
419
- writable: null,
420
- references: [],
421
- },
422
- validation: { valid: true, warnings: [] },
423
- })
424
- expect(
425
- workbase.discovery.epics.map((item: { id: string }) => item.id),
426
- ).toEqual(["contract"])
427
- expect(
428
- workbase.discovery.tasks.map((item: { id: string }) => item.id),
429
- ).toEqual(["agent-contract", "foundations"])
430
- expect(
431
- workbase.discovery.phases.map(
432
- (item: { taskId: string; id: string }) => `${item.taskId}/${item.id}`,
433
- ),
434
- ).toEqual(["agent-contract/context-command", "agent-contract/schema"])
435
- expect(workbase.discovery.tasks[0].body).toBeUndefined()
436
- expect(workbase.discovery.phases[0].data.status).toBe("open")
437
- expect(workbase.discovery.phases[0].sha256).toMatch(/^[a-f0-9]{64}$/)
438
- })
439
-
440
- test("includes discovery prose only in full root context", async () => {
441
- const workbase = await readContext(root, ".", false, true)
442
- expect(workbase.projection).toBe("complete")
443
- expect(workbase.hint).toBeNull()
444
- expect(workbase.discovery.epics[0].body).toContain("# Contract")
445
- expect(workbase.discovery.tasks[0].body).toContain("Task prose.")
446
- expect(workbase.discovery.tasks[0].data.phases).toBeDefined()
447
- expect(workbase.discovery.phases[0].taskId).toBe("agent-contract")
448
- })
449
-
450
- test("resolves bare task IDs from inside another target", async () => {
451
- const cwd = join(
452
- root,
453
- "tasks/agent-contract/phases/context-command/code/agency",
454
- )
455
- const logs = await captureLogs(() =>
456
- runTestEffect(context({ cwd, target: "foundations", json: true })),
457
- )
458
- const result = JSON.parse(logs[0]!)
459
- expect(result.target).toMatchObject({ kind: "task", taskId: "foundations" })
460
- expect(result.authority.writable.branch).toBe("foundations")
461
- })
462
-
463
- test("resolves archived tasks without granting mutation or execution authority", async () => {
464
- await mkdir(join(root, "archive/tasks"), { recursive: true })
465
- await rename(
466
- join(root, "tasks/foundations"),
467
- join(root, "archive/tasks/foundations"),
468
- )
469
-
470
- const result = await readContext(root, "foundations", false, true)
471
-
472
- expect(result.target).toMatchObject({
473
- kind: "task",
474
- taskId: "foundations",
475
- archived: true,
476
- path: join(root, "archive/tasks/foundations/TASK.md"),
477
- })
478
- expect(result.documents.task.body).toContain("# Foundations")
479
- expect(result.authority).toMatchObject({
480
- mode: "orchestration",
481
- writable: null,
482
- references: [],
483
- documents: { writable: [] },
484
- })
485
- expect(result.workspace).toMatchObject({
486
- materialization: "absent",
487
- writable: null,
488
- references: [],
489
- })
490
- expect(result.graph.readiness).toMatchObject({
491
- ready: false,
492
- blocked: true,
493
- })
494
- expect(result.graph.readiness.blockers).toContainEqual({
495
- kind: "status",
496
- id: "foundations",
497
- reason: "Target is archived; restore it before mutation or execution",
498
- })
499
- expect(result.pr).toMatchObject({
500
- url: "https://github.com/example/agency/pull/1",
501
- state: "open",
502
- })
503
- })
504
-
505
- test("resolves archived phases with their active parent context", async () => {
506
- await mkdir(join(root, "archive/tasks/agent-contract/phases"), {
507
- recursive: true,
508
- })
509
- await rename(
510
- join(root, "tasks/agent-contract/phases/context-command"),
511
- join(root, "archive/tasks/agent-contract/phases/context-command"),
512
- )
513
-
514
- const result = await readContext(
515
- root,
516
- "archive/tasks/agent-contract/phases/context-command",
517
- false,
518
- true,
519
- )
520
-
521
- expect(result.target).toMatchObject({
522
- kind: "phase",
523
- taskId: "agent-contract",
524
- phaseId: "context-command",
525
- archived: true,
526
- path: join(
527
- root,
528
- "archive/tasks/agent-contract/phases/context-command/PHASE.md",
529
- ),
530
- })
531
- expect(result.documents.task.body).toContain("Task prose.")
532
- expect(result.documents.phase.body).toContain("Phase prose.")
533
- expect(result.graph.parent).toEqual({ kind: "task", id: "agent-contract" })
534
- expect(result.graph.readiness).toMatchObject({
535
- ready: false,
536
- blocked: true,
537
- })
538
- expect(result.authority).toMatchObject({
539
- mode: "orchestration",
540
- writable: null,
541
- references: [],
542
- documents: { writable: [] },
543
- })
544
- })
545
-
546
- test("resolves archived epics with archived descendant graph context", async () => {
547
- await mkdir(join(root, "archive/epics"), { recursive: true })
548
- await mkdir(join(root, "archive/tasks"), { recursive: true })
549
- await rename(
550
- join(root, "epics/contract"),
551
- join(root, "archive/epics/contract"),
552
- )
553
- for (const taskId of ["agent-contract", "foundations"]) {
554
- await rename(
555
- join(root, "tasks", taskId),
556
- join(root, "archive/tasks", taskId),
557
- )
558
- }
559
-
560
- const result = await readContext(root, "epics/contract", false, true)
561
-
562
- expect(result.target).toMatchObject({
563
- kind: "epic",
564
- epicId: "contract",
565
- archived: true,
566
- path: join(root, "archive/epics/contract/EPIC.md"),
567
- })
568
- expect(result.documents.epic.body).toContain("# Contract")
569
- expect(result.graph.aggregate).toMatchObject({ total: 3, done: 2, open: 1 })
570
- expect(result.graph.readiness).toMatchObject({
571
- ready: false,
572
- blocked: true,
573
- })
574
- expect(result.authority).toMatchObject({
575
- mode: "orchestration",
576
- writable: null,
577
- references: [],
578
- documents: { writable: [] },
579
- })
580
- })
581
-
582
- test("computes orchestration readiness from runnable descendants", async () => {
583
- await write(
584
- root,
585
- "tasks/agent-contract/TASK.md",
586
- `---
587
- ticketUrl: null
588
- epic: contract
589
- phases:
590
- - id: schema
591
- - id: context-command
592
- dependsOn: [schema]
593
- - id: parallel
594
- ---
595
-
596
- # Agent contract
597
- `,
598
- )
599
- await write(
600
- root,
601
- "tasks/agent-contract/phases/schema/PHASE.md",
602
- `---
603
- repo: agency
604
- branch: schema
605
- base: main
606
- pr: null
607
- status: working
608
- ---
609
-
610
- # Schema
611
- `,
612
- )
613
- await write(
614
- root,
615
- "tasks/agent-contract/phases/parallel/PHASE.md",
616
- `---
617
- repo: docs
618
- branch: parallel
619
- base: main
620
- pr: null
621
- status: open
622
- ---
623
-
624
- # Parallel
625
- `,
626
- )
627
-
628
- const task = await readContext(root, "agent-contract")
629
- expect(task.graph.readiness).toMatchObject({ ready: true, blocked: false })
630
- expect(task.graph.aggregate).toMatchObject({
631
- status: "working",
632
- total: 3,
633
- open: 2,
634
- working: 1,
635
- })
636
-
637
- const epic = await readContext(root, "epics/contract")
638
- expect(epic.graph.readiness).toMatchObject({ ready: true, blocked: false })
639
- expect(epic.graph.aggregate).toMatchObject({
640
- status: "working",
641
- total: 4,
642
- done: 1,
643
- open: 2,
644
- working: 1,
645
- })
646
- })
647
-
648
- test("reports stale worktree registration independently of materialization", async () => {
649
- const checkout = join(
650
- root,
651
- "tasks/agent-contract/phases/context-command/code/agency",
652
- )
653
- await rm(checkout, { recursive: true, force: true })
654
-
655
- const result = await readContext(
656
- root,
657
- "tasks/agent-contract/phases/context-command",
658
- )
659
- expect(result.workspace.writable).toMatchObject({
660
- materialized: false,
661
- registered: true,
662
- })
663
- expect(result.workspace.materialization).toBe("partial")
664
- })
665
- })