@markjaquith/agency 2.71.6 → 2.71.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markjaquith/agency",
3
- "version": "2.71.6",
3
+ "version": "2.71.7",
4
4
  "description": "Manage agentic work across repositories with durable workbases",
5
5
  "keywords": [
6
6
  "agents",
@@ -68,6 +68,7 @@
68
68
  "benchmark:workbase": "bun scripts/benchmark-workbase.ts",
69
69
  "benchmark:doctor": "bun scripts/benchmark-doctor.ts",
70
70
  "benchmark:context": "bun scripts/benchmark-context.ts",
71
+ "benchmark:finish": "bun scripts/benchmark-finish.ts",
71
72
  "benchmark:sync": "bun scripts/benchmark-sync.ts",
72
73
  "benchmark:task": "bun scripts/benchmark-task.ts",
73
74
  "test": "find src \\( -name '*.test.ts' -o -name '*.test.tsx' \\) -print0 | xargs -0 -n 1 -P 4 bun test",
@@ -234,6 +234,38 @@ describe("claim service", () => {
234
234
  )
235
235
  })
236
236
 
237
+ test("inspects a task without parsing unrelated task documents", async () => {
238
+ await mkdir(join(root, "tasks/broken"), { recursive: true })
239
+ await Bun.write(join(root, "tasks/broken/TASK.md"), "not frontmatter\n")
240
+
241
+ const inspected = await inspect()
242
+ expect(inspected.target.path).toBe(join(root, "tasks/single/TASK.md"))
243
+ expect(inspected.data).toMatchObject({ branch: "task/single" })
244
+ })
245
+
246
+ test("inspects a phase without parsing unrelated phase documents", async () => {
247
+ await mkdir(join(root, "tasks/phased/phases/target"), { recursive: true })
248
+ await mkdir(join(root, "tasks/phased/phases/broken"), { recursive: true })
249
+ await Bun.write(
250
+ join(root, "tasks/phased/TASK.md"),
251
+ "---\nticketUrl: null\nphases:\n - id: target\n - id: broken\nstatus: working\n---\n",
252
+ )
253
+ await Bun.write(
254
+ join(root, "tasks/phased/phases/target/PHASE.md"),
255
+ "---\nrepo: agency\nbranch: phase/target\nbase: main\npr: null\nstatus: open\n---\n",
256
+ )
257
+ await Bun.write(
258
+ join(root, "tasks/phased/phases/broken/PHASE.md"),
259
+ "not frontmatter\n",
260
+ )
261
+
262
+ const inspected = await inspect("phased", "target")
263
+ expect(inspected.target.path).toBe(
264
+ join(root, "tasks/phased/phases/target/PHASE.md"),
265
+ )
266
+ expect(inspected.data).toMatchObject({ branch: "phase/target" })
267
+ })
268
+
237
269
  test("serializes concurrent claims and allows expired ownership replacement", async () => {
238
270
  const initial = await inspect()
239
271
  const attempts = await Promise.allSettled([
@@ -10,10 +10,7 @@ import {
10
10
  writeFile,
11
11
  } from "node:fs/promises"
12
12
  import { basename, dirname, join, relative } from "node:path"
13
- import { PhaseService } from "./PhaseService"
14
- import { TaskService } from "./TaskService"
15
13
  import { WorkbaseService } from "./WorkbaseService"
16
- import { FileSystemService } from "./FileSystemService"
17
14
  import {
18
15
  documentRevision,
19
16
  isDocumentRevision,
@@ -66,6 +63,47 @@ interface ClaimTarget {
66
63
  readonly label: string
67
64
  }
68
65
 
66
+ const resolveTarget = async (
67
+ root: string,
68
+ taskId: string,
69
+ phaseId?: string,
70
+ ): Promise<ClaimTarget> => {
71
+ const taskPath = join(root, "tasks", taskId, "TASK.md")
72
+ const path = phaseId
73
+ ? join(root, "tasks", taskId, "phases", phaseId, "PHASE.md")
74
+ : taskPath
75
+ try {
76
+ await stat(taskPath)
77
+ } catch {
78
+ throw new ClaimError({ message: `Task '${taskId}' does not exist` })
79
+ }
80
+ if (phaseId) {
81
+ try {
82
+ await stat(path)
83
+ } catch {
84
+ throw new ClaimError({
85
+ message: `Phase '${phaseId}' does not exist on task '${taskId}'`,
86
+ })
87
+ }
88
+ }
89
+ return phaseId
90
+ ? {
91
+ kind: "phase",
92
+ root,
93
+ taskId,
94
+ phaseId,
95
+ path,
96
+ label: `phase '${taskId}/${phaseId}'`,
97
+ }
98
+ : {
99
+ kind: "task",
100
+ root,
101
+ taskId,
102
+ path,
103
+ label: `task '${taskId}'`,
104
+ }
105
+ }
106
+
69
107
  interface ClaimInput {
70
108
  readonly taskId: string
71
109
  readonly phaseId?: string
@@ -276,43 +314,24 @@ export class ClaimService extends Effect.Service<ClaimService>()(
276
314
  startPath: string = process.cwd(),
277
315
  ) =>
278
316
  Effect.gen(function* () {
279
- const fs = yield* FileSystemService
280
317
  const workbase = yield* WorkbaseService
281
- const tasks = yield* TaskService
282
- const phases = yield* PhaseService
283
318
  const root = yield* workbase.discover(startPath)
284
- const task = yield* tasks.show(taskId, root)
285
- const phase = phaseId
286
- ? yield* phases.show(task.id, phaseId, root)
287
- : undefined
288
- const target: ClaimTarget = phaseId
289
- ? {
290
- kind: "phase",
291
- root,
292
- taskId: task.id,
293
- phaseId,
294
- path: phase!.path,
295
- label: `phase '${task.id}/${phaseId}'`,
296
- }
297
- : {
298
- kind: "task",
299
- root,
300
- taskId: task.id,
301
- path: task.path,
302
- label: `task '${task.id}'`,
303
- }
304
- if (!phaseId && "phases" in task.data) {
319
+ const target = yield* operation(() =>
320
+ resolveTarget(root, taskId, phaseId),
321
+ )
322
+ const content = yield* operation(() => readFile(target.path, "utf8"))
323
+ const parsed = parseFrontmatterSync(content, target.path)
324
+ const data = decodeExecution(target, parsed.data)
325
+ if (!phaseId && "phases" in data) {
305
326
  return yield* new ClaimError({
306
327
  target: target.label,
307
- message: `Task '${task.id}' has multiple phases; claim a phase instead`,
328
+ message: `Task '${taskId}' has multiple phases; claim a phase instead`,
308
329
  })
309
330
  }
310
- const content = yield* fs.readFile(target.path)
311
- const parsed = parseFrontmatterSync(content, target.path)
312
331
  return {
313
332
  target,
314
333
  revision: documentRevision(content),
315
- data: decodeExecution(target, parsed.data),
334
+ data,
316
335
  }
317
336
  }),
318
337