@markjaquith/agency 3.2.2 → 3.2.3

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 (45) hide show
  1. package/README.md +20 -58
  2. package/cli-main.ts +0 -72
  3. package/fixtures/protocol/orchestration-recipes.json +9 -34
  4. package/package.json +1 -4
  5. package/schemas/agency-graph-v1.schema.json +2 -31
  6. package/src/cli-parser.test.ts +13 -132
  7. package/src/cli-parser.ts +10 -101
  8. package/src/cli.test.ts +12 -111
  9. package/src/commands/act.ts +2 -6
  10. package/src/commands/sync.ts +3 -3
  11. package/src/commands/validate.ts +3 -1
  12. package/src/commands/work.test.ts +70 -8
  13. package/src/commands/work.ts +15 -8
  14. package/src/graph-schema.ts +0 -2
  15. package/src/protocol.test.ts +0 -25
  16. package/src/protocol.ts +0 -11
  17. package/src/readiness.test.ts +2 -2
  18. package/src/services/ArchiveBulkService.test.ts +0 -88
  19. package/src/services/ArchiveService.ts +0 -32
  20. package/src/services/GraphMutationService.ts +0 -26
  21. package/src/services/GraphService.test.ts +1 -1
  22. package/src/services/IntegrationService.test.ts +4 -4
  23. package/src/services/LifecycleTransaction.ts +5 -1
  24. package/src/services/PhaseService.ts +1 -8
  25. package/src/services/ReadinessService.test.ts +0 -34
  26. package/src/services/ReadinessService.ts +1 -20
  27. package/src/services/ReviewService.test.ts +0 -64
  28. package/src/services/ReviewService.ts +0 -5
  29. package/src/services/SyncService.test.ts +75 -88
  30. package/src/services/SyncService.ts +52 -123
  31. package/src/services/TaskPhaseService.test.ts +13 -1
  32. package/src/services/TaskService.ts +1 -7
  33. package/src/services/WorkbaseService.ts +0 -3
  34. package/src/services/WorktreeService.test.ts +192 -1
  35. package/src/services/WorktreeService.ts +169 -34
  36. package/src/test-utils.ts +0 -2
  37. package/src/workbase/AGENTS.md +9 -15
  38. package/src/workbase/agent-command.test.ts +0 -3
  39. package/src/workbase/agent-command.ts +0 -6
  40. package/src/workbase/document-revision.ts +0 -4
  41. package/src/workbase/schemas.test.ts +12 -25
  42. package/src/workbase/schemas.ts +0 -16
  43. package/src/commands/claim.ts +0 -122
  44. package/src/services/ClaimService.test.ts +0 -415
  45. package/src/services/ClaimService.ts +0 -608
package/src/cli-parser.ts CHANGED
@@ -114,21 +114,6 @@ const mutationOptions = {
114
114
  "no-epic": { type: "boolean" },
115
115
  } satisfies OptionConfig
116
116
 
117
- const claimOptions = {
118
- ...outputOptions,
119
- claimant: { type: "string" },
120
- agent: { type: "string" },
121
- "session-id": { type: "string" },
122
- revision: { type: "string" },
123
- "expires-at": { type: "string" },
124
- } satisfies OptionConfig
125
-
126
- const ownedClaimOptions = {
127
- ...outputOptions,
128
- "session-id": { type: "string" },
129
- revision: { type: "string" },
130
- } satisfies OptionConfig
131
-
132
117
  const nonPrCompletionOptions = {
133
118
  "no-pull-request": { type: "boolean" },
134
119
  summary: { type: "string" },
@@ -679,75 +664,6 @@ const commands = {
679
664
  },
680
665
  },
681
666
  },
682
- claim: {
683
- usage: "agency claim <task-id> [phase-id] [options]",
684
- options: {
685
- ...claimOptions,
686
- task: { type: "string" },
687
- phase: { type: "string" },
688
- },
689
- command: {
690
- usage:
691
- "agency claim <task-id> [phase-id] --claimant <id> --agent <id> --session-id <id> --revision <sha256> [--expires-at <timestamp>] [--json]",
692
- minArgs: 1,
693
- maxArgs: 2,
694
- options: [
695
- "claimant",
696
- "agent",
697
- "session-id",
698
- "revision",
699
- "expires-at",
700
- "json",
701
- "task",
702
- "phase",
703
- ],
704
- required: ["claimant", "agent", "session-id", "revision"],
705
- },
706
- },
707
- release: {
708
- usage: "agency release <task-id> [phase-id] [options]",
709
- options: {
710
- ...ownedClaimOptions,
711
- task: { type: "string" },
712
- phase: { type: "string" },
713
- },
714
- command: {
715
- usage:
716
- "agency release <task-id> [phase-id] --session-id <id> --revision <sha256> [--json]",
717
- minArgs: 1,
718
- maxArgs: 2,
719
- options: ["session-id", "revision", "json", "task", "phase"],
720
- required: ["session-id", "revision"],
721
- },
722
- },
723
- finish: {
724
- usage: "agency finish <task-id> [phase-id] [options]",
725
- options: {
726
- ...ownedClaimOptions,
727
- ...nonPrCompletionOptions,
728
- outcome: { type: "string" },
729
- task: { type: "string" },
730
- phase: { type: "string" },
731
- },
732
- command: {
733
- usage:
734
- "agency finish <task-id> [phase-id] --session-id <id> --revision <sha256> --outcome <done|dropped> [options] [--json]",
735
- minArgs: 1,
736
- maxArgs: 2,
737
- options: [
738
- "session-id",
739
- "revision",
740
- "outcome",
741
- "no-pull-request",
742
- "summary",
743
- "evidence-url",
744
- "json",
745
- "task",
746
- "phase",
747
- ],
748
- required: ["session-id", "revision", "outcome"],
749
- },
750
- },
751
667
  sync: {
752
668
  usage: "agency sync [<task-id> [phase-id]] [--dry-run] [--json]",
753
669
  options: {
@@ -1177,8 +1093,6 @@ const targetSlots = (
1177
1093
  return ["task"]
1178
1094
  if (commandName === "phase")
1179
1095
  return subcommand === "list" ? ["task"] : ["task", "phase"]
1180
- if (["claim", "release", "finish"].includes(commandName))
1181
- return ["task", "phase"]
1182
1096
  if (["archive", "restore"].includes(commandName))
1183
1097
  return subcommand === "epic"
1184
1098
  ? ["epic"]
@@ -1637,16 +1551,6 @@ export function parseCli(args: readonly string[]): ParsedCli {
1637
1551
  ) {
1638
1552
  validateViewOptions(parsed.values, spec)
1639
1553
  }
1640
- if (
1641
- commandName === "finish" &&
1642
- parsed.values.outcome !== "done" &&
1643
- parsed.values.outcome !== "dropped"
1644
- ) {
1645
- throw usageError(
1646
- "Option '--outcome' must be 'done' or 'dropped'.",
1647
- spec.usage,
1648
- )
1649
- }
1650
1554
  const nonPrCompletion = parsed.values["no-pull-request"] === true
1651
1555
  const completionSummary = parsed.values.summary
1652
1556
  const completionEvidenceUrl = parsed.values["evidence-url"]
@@ -1670,19 +1574,24 @@ export function parseCli(args: readonly string[]): ParsedCli {
1670
1574
  )
1671
1575
  }
1672
1576
  const completesDone =
1673
- (commandName === "finish" && parsed.values.outcome === "done") ||
1674
- (["task", "phase"].includes(commandName) &&
1675
- subcommand === "status" &&
1676
- commandPositionals.at(-1) === "done")
1577
+ ["task", "phase"].includes(commandName) &&
1578
+ subcommand === "status" &&
1579
+ commandPositionals.at(-1) === "done"
1677
1580
  if (!completesDone) {
1678
1581
  throw usageError(
1679
- "Option '--no-pull-request' is valid only with a done status or outcome.",
1582
+ "Option '--no-pull-request' is valid only with a done status.",
1680
1583
  spec.usage,
1681
1584
  )
1682
1585
  }
1683
1586
  }
1684
1587
  if (commandName === "work") {
1685
1588
  const preparing = commandPositionals[0] === "prepare"
1589
+ if (preparing && parsed.values.phase && !parsed.values.task) {
1590
+ throw usageError(
1591
+ "Option '--phase' requires '--task' with work preparation.",
1592
+ spec.usage,
1593
+ )
1594
+ }
1686
1595
  if (
1687
1596
  (!preparing && commandPositionals.length > 1) ||
1688
1597
  (preparing &&
package/src/cli.test.ts CHANGED
@@ -210,131 +210,23 @@ describe("CLI", () => {
210
210
  expect(exported.stdout).not.toContain("/private/value")
211
211
  })
212
212
 
213
- test("coordinates claims through revision-guarded machine commands", async () => {
213
+ test("records status-based non-PR completion", async () => {
214
214
  const root = await createTempDir()
215
215
  tempDirs.push(root)
216
216
  await Bun.write(join(root, "agency.json"), '{"version":2}\n')
217
217
  await mkdir(join(root, "repos", "agency"), { recursive: true })
218
- parseJson(
219
- await runCli(
220
- ["task", "create", "claimed", "--repo", "agency", "--json"],
221
- root,
222
- ),
223
- )
224
- const context = parseJson(
225
- await runCli(["context", "tasks/claimed", "--json"], root),
226
- )
227
- const revision = context.documents.task.sha256
228
- const claimed = parseJson(
229
- await runCli(
230
- [
231
- "claim",
232
- "claimed",
233
- "--claimant",
234
- "orchestrator",
235
- "--agent",
236
- "agent",
237
- "--session-id",
238
- "job-1",
239
- "--revision",
240
- revision,
241
- "--json",
242
- ],
243
- root,
244
- ),
245
- )
246
- expect(claimed.claim).toMatchObject({
247
- claimant: "orchestrator",
248
- agent: "agent",
249
- sessionId: "job-1",
250
- state: "active",
251
- })
252
-
253
- const conflict = await runCli(
254
- [
255
- "claim",
256
- "claimed",
257
- "--claimant",
258
- "other",
259
- "--agent",
260
- "other-agent",
261
- "--session-id",
262
- "job-2",
263
- "--revision",
264
- claimed.revision,
265
- "--json",
266
- ],
267
- root,
268
- )
269
- expect(conflict.exitCode).toBe(1)
270
- expect(JSON.parse(conflict.stdout)).toMatchObject({
271
- ok: false,
272
- error: {
273
- code: "CLAIM_CONFLICT",
274
- retryable: true,
275
- fields: { claim: { agent: "agent", sessionId: "job-1" } },
276
- },
277
- })
278
-
279
- const finished = parseJson(
280
- await runCli(
281
- [
282
- "finish",
283
- "claimed",
284
- "--session-id",
285
- "job-1",
286
- "--revision",
287
- claimed.revision,
288
- "--outcome",
289
- "done",
290
- "--json",
291
- ],
292
- root,
293
- ),
294
- )
295
- expect(finished.claim).toMatchObject({ state: "finished", outcome: "done" })
296
- expect(
297
- parseJson(await runCli(["task", "show", "claimed", "--json"], root)).data
298
- .status,
299
- ).toBe("working")
300
-
301
218
  parseJson(
302
219
  await runCli(
303
220
  ["task", "create", "non-pr", "--repo", "agency", "--json"],
304
221
  root,
305
222
  ),
306
223
  )
307
- const nonPrContext = parseJson(
308
- await runCli(["context", "tasks/non-pr", "--json"], root),
309
- )
310
- const nonPrClaim = parseJson(
311
- await runCli(
312
- [
313
- "claim",
314
- "non-pr",
315
- "--claimant",
316
- "orchestrator",
317
- "--agent",
318
- "agent",
319
- "--session-id",
320
- "job-2",
321
- "--revision",
322
- nonPrContext.documents.task.sha256,
323
- "--json",
324
- ],
325
- root,
326
- ),
327
- )
328
224
  parseJson(
329
225
  await runCli(
330
226
  [
331
- "finish",
227
+ "task",
228
+ "status",
332
229
  "non-pr",
333
- "--session-id",
334
- "job-2",
335
- "--revision",
336
- nonPrClaim.revision,
337
- "--outcome",
338
230
  "done",
339
231
  "--no-pull-request",
340
232
  "--summary",
@@ -1073,6 +965,15 @@ status: open
1073
965
  ],
1074
966
  }),
1075
967
  ])
968
+ const preparedAgain = parseJson(
969
+ await runCli(
970
+ ["work", "prepare", join(root, "tasks/example/TASK.md"), "--json"],
971
+ parent,
972
+ ),
973
+ )
974
+ expect(preparedAgain.checkouts).toEqual([
975
+ expect.objectContaining({ action: "reused", kind: "writable" }),
976
+ ])
1076
977
  const rebuild = parseJson(
1077
978
  await runCli(
1078
979
  ["worktree", "rebuild", "example", "--dry-run", "--json"],
@@ -106,9 +106,6 @@ const entityChoices = (
106
106
  value: entityKey(node),
107
107
  }))
108
108
 
109
- const activeClaim = (node: EntityNode) =>
110
- "claim" in node.data && node.data.claim?.state === "active"
111
-
112
109
  const executionNode = (
113
110
  node: EntityNode,
114
111
  executions: ReadonlyMap<
@@ -131,7 +128,6 @@ const canWork = (
131
128
  const execution = executionNode(node, executions)
132
129
  return Boolean(
133
130
  execution &&
134
- !activeClaim(node) &&
135
131
  (execution.readiness.ready ||
136
132
  (execution.status === "working" &&
137
133
  execution.readiness.blockers.every(
@@ -168,10 +164,10 @@ const actionChoices = (
168
164
  if (canCreatePr(node, executions)) {
169
165
  choices.push({ key: "pr", label: "Create pull request", value: "pr" })
170
166
  }
171
- if (execution && isTerminalStatus(node.status) && !activeClaim(node)) {
167
+ if (execution && isTerminalStatus(node.status)) {
172
168
  choices.push({ key: "reopen", label: "Reopen", value: "reopen" })
173
169
  }
174
- if (execution && !isTerminalStatus(node.status) && !activeClaim(node)) {
170
+ if (execution && !isTerminalStatus(node.status)) {
175
171
  choices.push({ key: "drop", label: "Drop", value: "drop" })
176
172
  }
177
173
  if (node.readiness.terminal) {
@@ -125,7 +125,7 @@ export const help = `
125
125
  Usage: agency sync [<task-id> [phase-id]] [--dry-run] [--json]
126
126
 
127
127
  Compare portable repository declarations and execution state with local Git
128
- repositories, worktrees, branches, references, claims, and pull requests.
128
+ repositories, worktrees, branches, references, and pull requests.
129
129
  Safe reconciliation transitions are applied by default.
130
130
  When a task or phase is provided, only that target and its repositories are
131
131
  queried or reconciled. Task scope includes all phases of a multi-phase task.
@@ -135,7 +135,7 @@ Options:
135
135
  --json Output one versioned machine result
136
136
 
137
137
  Sync may materialize declared repositories and unambiguous missing checkouts,
138
- adopt legacy repositories with portable origins, release expired claims,
139
- record a uniquely matched PR, and mark merged work done. Dirty, stale, or
138
+ adopt legacy repositories with portable origins, record a uniquely matched PR,
139
+ and mark merged work done. Dirty, stale, or
140
140
  conflicting checkouts are always left unresolved.
141
141
  `
@@ -13,7 +13,9 @@ interface ValidateOptions extends BaseCommandOptions {
13
13
  readonly json?: boolean
14
14
  }
15
15
 
16
- class ValidationFailedError extends Data.TaggedError("ValidationFailedError")<{
16
+ export class ValidationFailedError extends Data.TaggedError(
17
+ "ValidationFailedError",
18
+ )<{
17
19
  readonly message: string
18
20
  readonly root: string
19
21
  readonly issues: readonly {
@@ -84,6 +84,8 @@ interface HarnessOptions {
84
84
  Record<string, "open" | "working" | "delegated" | "done" | "dropped">
85
85
  >
86
86
  readonly taskRepo?: string
87
+ readonly existingPaths?: readonly string[]
88
+ readonly validationIssues?: readonly { path: string; message: string }[]
87
89
  }
88
90
 
89
91
  const createHarness = (options: HarnessOptions = {}) => {
@@ -142,15 +144,17 @@ const createHarness = (options: HarnessOptions = {}) => {
142
144
  },
143
145
  }),
144
146
  repositoryAliases: () => Effect.succeed(["agency"]),
145
- validate: () =>
146
- Effect.succeed({
147
+ validate: () => {
148
+ const issues = options.validationIssues ?? []
149
+ return Effect.succeed({
147
150
  root: "/workbase",
148
- issues: [],
151
+ issues,
149
152
  epicCount: 0,
150
153
  taskCount: 1,
151
154
  phaseCount: 0,
152
- valid: true,
153
- }),
155
+ valid: issues.length === 0,
156
+ })
157
+ },
154
158
  }
155
159
  const epics = {
156
160
  show: (id: string) =>
@@ -281,7 +285,8 @@ const createHarness = (options: HarnessOptions = {}) => {
281
285
  readFile: (path: string) =>
282
286
  Effect.succeed(path.endsWith("agency.json") ? '{"version":2}\n' : ""),
283
287
  readDirectory: () => Effect.succeed([]),
284
- exists: () => Effect.succeed(false),
288
+ exists: (path: string) =>
289
+ Effect.succeed(options.existingPaths?.includes(path) ?? false),
285
290
  realPath: (path: string) => Effect.succeed(path),
286
291
  runCommand: (args: readonly string[]) => {
287
292
  const cli = args[1]!
@@ -569,6 +574,65 @@ describe("work command", () => {
569
574
  ).rejects.toThrow("Recalled repository conflicts")
570
575
  })
571
576
 
577
+ test("resolves task and phase document paths", async () => {
578
+ const taskDocument = "/workbase/tasks/example/TASK.md"
579
+ const taskHarness = createHarness({
580
+ existingDirectories: [],
581
+ existingPaths: [taskDocument],
582
+ })
583
+
584
+ await taskHarness.runPrepare({
585
+ directory: taskDocument,
586
+ dryRun: true,
587
+ silent: true,
588
+ })
589
+ expect(taskHarness.guards[0]?.target).toBe("execution-unit:task/example")
590
+ expect(taskHarness.shownTasks).toEqual(["example", "example"])
591
+
592
+ const phaseDocument =
593
+ "/workbase/tasks/example/phases/implementation/PHASE.md"
594
+ const phaseHarness = createHarness({
595
+ existingDirectories: [],
596
+ existingPaths: [phaseDocument],
597
+ multiPhaseTasks: ["example"],
598
+ workspace: multiPhaseWorkspace,
599
+ })
600
+
601
+ await phaseHarness.runPrepare({
602
+ directory: phaseDocument,
603
+ dryRun: true,
604
+ silent: true,
605
+ })
606
+ expect(phaseHarness.guards[0]?.target).toBe(
607
+ "execution-unit:phase/example/implementation",
608
+ )
609
+ expect(phaseHarness.materializeOptions).toHaveLength(1)
610
+ })
611
+
612
+ test("returns structured validation issues before readiness or materialization", async () => {
613
+ const harness = createHarness({
614
+ existingDirectories: [],
615
+ validationIssues: [
616
+ {
617
+ path: "tasks/example/TASK.md",
618
+ message: "Repository alias 'missing' is not configured",
619
+ },
620
+ ],
621
+ })
622
+
623
+ await expect(
624
+ harness.runPrepare({
625
+ cwd: "/workbase",
626
+ directory: "example",
627
+ dryRun: true,
628
+ }),
629
+ ).rejects.toThrow(
630
+ "Workbase validation failed with 1 issue:\n- tasks/example/TASK.md: Repository alias 'missing' is not configured",
631
+ )
632
+ expect(harness.guards).toEqual([])
633
+ expect(harness.events).toEqual([])
634
+ })
635
+
572
636
  test("force prepares a phase blocked by an active dependency without launching or changing status", async () => {
573
637
  const blocked = createHarness({
574
638
  workspace: multiPhaseWorkspace,
@@ -1031,12 +1095,10 @@ describe("work command", () => {
1031
1095
  })
1032
1096
  expect(harness.launchEnvironments[0]).toMatchObject({
1033
1097
  AGENCY_AGENT: "custom",
1034
- AGENCY_CLAIMANT: process.env.USER ?? "agency",
1035
1098
  AGENCY_WORKBASE: "/workbase",
1036
1099
  AGENCY_TARGET: "execution-unit:task/example",
1037
1100
  AGENCY_TASK_ID: "example",
1038
1101
  AGENCY_PHASE_ID: "",
1039
- AGENCY_CLAIM_REVISION: "",
1040
1102
  CUSTOM_TARGET: "execution-unit:task/example",
1041
1103
  })
1042
1104
  })
@@ -36,6 +36,7 @@ import {
36
36
  normalizeRecalledContext,
37
37
  readValidationEvidence,
38
38
  } from "../workbase/execution-contract"
39
+ import { ValidationFailedError } from "./validate"
39
40
 
40
41
  export interface WorkOptions extends BaseCommandOptions {
41
42
  readonly directory?: string
@@ -288,7 +289,6 @@ export const work = (
288
289
  const defaultAgents = ["opencode2", "opencode", "pi", "claude"] as const
289
290
  let defaultAgentIndex = 0
290
291
  let agent: string = selectedAgent ?? defaultAgents[defaultAgentIndex]!
291
- const claimant = process.env.AGENCY_CLAIMANT ?? process.env.USER ?? "agency"
292
292
  const sessionId =
293
293
  process.env.AGENCY_SESSION_ID ?? `${process.pid}-${Date.now()}`
294
294
  const resume =
@@ -300,9 +300,7 @@ export const work = (
300
300
  target: targetNodeId(target),
301
301
  task: target.kind === "epic" ? "" : target.taskId,
302
302
  phase: target.kind === "phase" ? target.phaseId : "",
303
- claimant,
304
303
  sessionId,
305
- claimRevision: "",
306
304
  }
307
305
  let resolved = resolveAgentCommand(
308
306
  agent,
@@ -439,7 +437,8 @@ export const workPrepare = (options: WorkOptions = {}) =>
439
437
  const cwd = options.cwd ?? process.cwd()
440
438
  const targetPath = options.directory ? resolve(cwd, options.directory) : cwd
441
439
  const isDirectory = yield* fs.isDirectory(targetPath)
442
- const root = yield* workbase.discover(isDirectory ? targetPath : cwd)
440
+ const targetExists = isDirectory || (yield* fs.exists(targetPath))
441
+ const root = yield* workbase.discover(targetExists ? targetPath : cwd)
443
442
 
444
443
  let taskId = options.taskId
445
444
  let phaseId = options.phaseId
@@ -447,7 +446,7 @@ export const workPrepare = (options: WorkOptions = {}) =>
447
446
  const task = yield* tasks.show(taskId, root)
448
447
  taskId = task.id
449
448
  if (phaseId) phaseId = (yield* phases.show(task.id, phaseId, root)).id
450
- } else if (options.directory && !isDirectory) {
449
+ } else if (options.directory && !targetExists) {
451
450
  const task = yield* tasks.show(options.directory, root)
452
451
  taskId = task.id
453
452
  } else {
@@ -526,9 +525,17 @@ export const workPrepare = (options: WorkOptions = {}) =>
526
525
  })
527
526
  let validation: unknown = { valid: true, source: "evidence" }
528
527
  if (assessment.disposition.status === "refreshed") {
529
- validation = yield* workbase.validate(root)
530
- if (!(validation as { valid: boolean }).valid && !options.force) {
531
- return yield* Effect.fail(new Error("Workbase validation failed"))
528
+ const report = yield* workbase.validate(root)
529
+ validation = report
530
+ if (!report.valid && !options.force) {
531
+ const details = report.issues
532
+ .map((issue) => `- ${issue.path}: ${issue.message}`)
533
+ .join("\n")
534
+ return yield* new ValidationFailedError({
535
+ message: `Workbase validation failed with ${report.issues.length} issue${report.issues.length === 1 ? "" : "s"}:\n${details}`,
536
+ root: report.root,
537
+ issues: report.issues,
538
+ })
532
539
  }
533
540
  }
534
541
  yield* readiness.guardWorkTarget(target, root, options.force)
@@ -3,7 +3,6 @@ import {
3
3
  EpicFrontmatter,
4
4
  PhaseFrontmatter,
5
5
  PullRequestRecord,
6
- ClaimRecord,
7
6
  ReviewRecord,
8
7
  TaskHandoff,
9
8
  TaskFrontmatter,
@@ -96,7 +95,6 @@ export const GraphExecutionData = Schema.Union(
96
95
  handoff: Schema.optional(TaskHandoff),
97
96
  review: ReviewRecord,
98
97
  status: WorkStatus,
99
- claim: Schema.optional(ClaimRecord),
100
98
  }),
101
99
  )
102
100
 
@@ -116,31 +116,6 @@ describe("machine protocol", () => {
116
116
  "issues",
117
117
  "root",
118
118
  ])
119
- expect(
120
- errorEnvelope({
121
- _tag: "ClaimConflictError",
122
- message: "already claimed",
123
- target: "task 'example'",
124
- currentRevision: "a".repeat(64),
125
- claim: {
126
- claimant: "orchestrator",
127
- agent: "agent",
128
- sessionId: "job-1",
129
- startedAt: "2026-07-17T12:00:00.000Z",
130
- targetRevision: "0".repeat(64),
131
- state: "active",
132
- },
133
- }),
134
- ).toMatchObject({
135
- error: {
136
- code: "CLAIM_CONFLICT",
137
- retryable: true,
138
- fields: {
139
- target: "task 'example'",
140
- claim: { agent: "agent", sessionId: "job-1" },
141
- },
142
- },
143
- })
144
119
  expect(
145
120
  errorEnvelope({
146
121
  _tag: "RevisionConflictError",
package/src/protocol.ts CHANGED
@@ -81,22 +81,11 @@ const errorMetadata: Readonly<Record<string, ErrorMetadata>> = {
81
81
  EpicError: { code: "EPIC_ERROR", retryable: false },
82
82
  TaskError: { code: "TASK_ERROR", retryable: false },
83
83
  PhaseError: { code: "PHASE_ERROR", retryable: false },
84
- ClaimError: { code: "CLAIM_ERROR", retryable: false },
85
- ClaimConflictError: {
86
- code: "CLAIM_CONFLICT",
87
- retryable: true,
88
- remediation: "Inspect the current ownership details before retrying.",
89
- },
90
84
  RevisionConflictError: {
91
85
  code: "REVISION_CONFLICT",
92
86
  retryable: true,
93
87
  remediation: "Read the current document revision and retry intentionally.",
94
88
  },
95
- ClaimOwnershipError: {
96
- code: "CLAIM_OWNERSHIP",
97
- retryable: false,
98
- remediation: "Use the session that owns the claim.",
99
- },
100
89
  ArchiveError: { code: "ARCHIVE_ERROR", retryable: false },
101
90
  WorktreeError: { code: "WORKTREE_ERROR", retryable: false },
102
91
  PushError: { code: "PUSH_ERROR", retryable: false },
@@ -27,10 +27,10 @@ describe("readiness model", () => {
27
27
  blockedBy: ["task:one"],
28
28
  terminal: true,
29
29
  })
30
- expect(readinessState("working", [{ id: "claim:self" }])).toEqual({
30
+ expect(readinessState("working", [{ id: "dependency:self" }])).toEqual({
31
31
  ready: false,
32
32
  blocked: true,
33
- blockedBy: ["claim:self"],
33
+ blockedBy: ["dependency:self"],
34
34
  terminal: false,
35
35
  })
36
36
  })