@markjaquith/agency 2.72.0 → 2.73.1

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 CHANGED
@@ -441,7 +441,7 @@ variables. A worker must verify either signal against `agency context . --json`
441
441
  before acting; a matching worker performs the task directly and must not invoke
442
442
  `agency work` for the same target. Managed guidance also fails safe for older
443
443
  generated prompts when their document paths, current directory, and active valid
444
- context all agree. Herdr is not part of this identity contract.
444
+ context all agree. External session state is not part of this identity contract.
445
445
  The managed OpenCode plugin validates the marker against Agency context, binds it
446
446
  to the receiving OpenCode session, injects an explicit active-worker system
447
447
  instruction, and restores Agency identity for that session's shell environment.
@@ -860,13 +860,13 @@ includes `selector`, absolute `documentPath`, the document `revision`, full
860
860
  `validation`, normalized `recalledContext`, and `evidence`. Evidence version 1 is
861
861
  an auditable local payload containing the canonical workbase root, target and
862
862
  document identities, aggregate workbase revision, configuration revision,
863
- repository-mapping revision, kickoff-contract version, validity result, recalled
863
+ repository-mapping revision, execution-contract version, validity result, recalled
864
864
  context, and a digest over those fields. It is not a signature or an authority
865
865
  grant. A different workbase, target, document revision, document set,
866
866
  configuration, repository mapping, contract version, or payload digest
867
867
  invalidates reuse. Older creation output without evidence remains compatible;
868
868
  preflight simply validates again. The published machine schema is
869
- `schemas/agency-kickoff-v1.schema.json`.
869
+ `schemas/agency-execution-v1.schema.json`.
870
870
 
871
871
  Create investigation-only work with the existing task architecture:
872
872
 
@@ -1128,12 +1128,18 @@ promptless by default; use `--auto` to send Agency's generated context prompt.
1128
1128
  `agency work prepare` resolves an execution unit and creates or reuses its
1129
1129
  writable and reference worktrees, or its single pinned review checkout, without
1130
1130
  launching an agent or changing status.
1131
+ Managed workbase guidance provides exact fast paths for 15 common intents:
1132
+ single-phase creation; create-and-start; materialization; remote PR sync; phase
1133
+ conversion; archive; review creation and start; status inspection; drop;
1134
+ continuation; publication and PR creation; non-PR completion; initial multi-phase
1135
+ setup; investigation handoff; and review refresh.
1131
1136
  Its JSON result includes the workspace, validation result, whether supplied
1132
1137
  evidence was `reused` or `refreshed` with stable reason strings, refreshed
1133
- evidence, and a versioned `agency-kickoff-v1` orchestration plan. The plan has a
1134
- deterministic idempotency key and ordered, retry-safe actions for worktree
1135
- preflight/preparation, a background tab, side-by-side task document,
1136
- `agency work . --auto`, and exactly one final `agency context <document> --json`.
1138
+ evidence, and a versioned `agency-execution-v1` contract. The contract reports
1139
+ whether preparation was previewed or applied, a stable target identity, prepared
1140
+ workspace and document paths, and canonical Agency-native commands for starting
1141
+ work and reading context. It does not prescribe how callers present or execute
1142
+ prepared work.
1137
1143
  The evidence argument may be an evidence object, task-creation JSON, or a path to
1138
1144
  either. Use `--dry-run` to report planned fetch, branch, and worktree changes
1139
1145
  without applying them. Validation reuse never skips readiness, active-claim,
@@ -1142,7 +1148,7 @@ checks.
1142
1148
 
1143
1149
  The authoritative implementation locations for this contract are
1144
1150
  `src/commands/task.ts` (creation output),
1145
- `src/workbase/kickoff-contract.ts` (evidence and orchestration schemas),
1151
+ `src/workbase/execution-contract.ts` (evidence and execution schemas),
1146
1152
  `src/commands/work.ts` (launch preflight),
1147
1153
  `src/services/WorktreeService.ts` (workspace safety), and
1148
1154
  `src/workbase/AGENTS.md` (generated OpenCode guidance). These paths are the
package/index.ts CHANGED
@@ -2,4 +2,4 @@ export * from "./src/workbase/schemas"
2
2
  export * from "./src/protocol"
3
3
  export * from "./src/graph-schema"
4
4
  export * from "./src/readiness"
5
- export * from "./src/workbase/kickoff-contract"
5
+ export * from "./src/workbase/execution-contract"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markjaquith/agency",
3
- "version": "2.72.0",
3
+ "version": "2.73.1",
4
4
  "description": "Manage agentic work across repositories with durable workbases",
5
5
  "keywords": [
6
6
  "agents",
@@ -46,8 +46,8 @@
46
46
  "./schemas/agency-graph-v1.json": {
47
47
  "default": "./schemas/agency-graph-v1.schema.json"
48
48
  },
49
- "./schemas/agency-kickoff-v1.json": {
50
- "default": "./schemas/agency-kickoff-v1.schema.json"
49
+ "./schemas/agency-execution-v1.json": {
50
+ "default": "./schemas/agency-execution-v1.schema.json"
51
51
  },
52
52
  "./fixtures/protocol/success.json": {
53
53
  "default": "./fixtures/protocol/success.json"
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/markjaquith/agency/schemas/agency-kickoff-v1.schema.json",
4
- "title": "Agency kickoff contract v1",
3
+ "$id": "https://github.com/markjaquith/agency/schemas/agency-execution-v1.schema.json",
4
+ "title": "Agency execution contract v1",
5
5
  "oneOf": [
6
6
  { "$ref": "#/$defs/validationEvidence" },
7
- { "$ref": "#/$defs/kickoffPlan" }
7
+ { "$ref": "#/$defs/executionContract" }
8
8
  ],
9
9
  "$defs": {
10
10
  "recalledContext": {
@@ -54,69 +54,93 @@
54
54
  },
55
55
  "additionalProperties": false
56
56
  },
57
- "kickoffPlan": {
57
+ "command": {
58
+ "type": "object",
59
+ "required": ["cwd", "argv"],
60
+ "properties": {
61
+ "cwd": { "type": "string" },
62
+ "argv": {
63
+ "type": "array",
64
+ "minItems": 1,
65
+ "items": { "type": "string" }
66
+ }
67
+ },
68
+ "additionalProperties": false
69
+ },
70
+ "executionContract": {
58
71
  "type": "object",
59
72
  "required": [
60
73
  "version",
61
- "idempotencyKey",
74
+ "capability",
75
+ "mode",
76
+ "executionIdentity",
62
77
  "workbaseRoot",
63
- "target",
64
- "documentRevision",
65
78
  "sourceLocations",
66
- "taskDirectory",
67
- "taskDocument",
68
- "phaseDocument",
69
- "preparedCheckout",
70
- "orchestrator",
71
- "steps",
72
- "successFields"
79
+ "workspace",
80
+ "commands"
73
81
  ],
74
82
  "properties": {
75
83
  "version": { "const": 1 },
76
- "idempotencyKey": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
84
+ "capability": { "const": "agency-execution-v1" },
85
+ "mode": { "enum": ["preview", "applied"] },
86
+ "executionIdentity": {
87
+ "type": "object",
88
+ "required": ["key", "target", "documentRevision"],
89
+ "properties": {
90
+ "key": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
91
+ "target": { "type": "string" },
92
+ "documentRevision": {
93
+ "type": "string",
94
+ "pattern": "^[a-f0-9]{64}$"
95
+ }
96
+ },
97
+ "additionalProperties": false
98
+ },
77
99
  "workbaseRoot": { "type": "string" },
78
- "target": { "type": "string" },
79
- "documentRevision": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
80
- "sourceLocations": { "type": "array", "items": { "type": "string" } },
81
- "taskDirectory": { "type": "string" },
82
- "taskDocument": { "type": "string" },
83
- "phaseDocument": { "type": ["string", "null"] },
84
- "preparedCheckout": { "type": ["string", "null"] },
85
- "orchestrator": {
100
+ "sourceLocations": {
101
+ "type": "array",
102
+ "items": { "type": "string" }
103
+ },
104
+ "workspace": {
86
105
  "type": "object",
87
106
  "required": [
88
- "capability",
89
- "knownCurrentCommandsBypassDiscovery",
90
- "fallback"
107
+ "state",
108
+ "executionDirectory",
109
+ "taskDocument",
110
+ "phaseDocument",
111
+ "checkoutPath"
91
112
  ],
92
113
  "properties": {
93
- "capability": { "const": "agency-kickoff-v1" },
94
- "knownCurrentCommandsBypassDiscovery": { "const": true },
95
- "fallback": { "type": "string" }
114
+ "state": { "enum": ["planned", "materialized"] },
115
+ "executionDirectory": { "type": "string" },
116
+ "taskDocument": { "type": "string" },
117
+ "phaseDocument": { "type": ["string", "null"] },
118
+ "checkoutPath": { "type": ["string", "null"] }
96
119
  },
97
120
  "additionalProperties": false
98
121
  },
99
- "steps": {
122
+ "plannedActions": {
100
123
  "type": "array",
101
- "minItems": 6,
102
124
  "items": {
103
125
  "type": "object",
104
- "required": ["id"],
126
+ "required": ["kind", "target", "checkoutPath"],
105
127
  "properties": {
106
- "id": { "type": "string" },
107
- "argv": { "type": "array", "items": { "type": "string" } },
108
- "cwd": { "type": "string" },
109
- "action": { "type": "string" },
110
- "path": { "type": "string" },
111
- "retry": { "type": "string" },
112
- "recovery": { "type": "string" },
113
- "idempotencyKey": { "type": "string" },
114
- "exactlyOnce": { "type": "boolean" }
128
+ "kind": { "const": "workspace-materialization" },
129
+ "target": { "type": "string" },
130
+ "checkoutPath": { "type": ["string", "null"] }
115
131
  },
116
132
  "additionalProperties": false
117
133
  }
118
134
  },
119
- "successFields": { "type": "array", "items": { "type": "string" } }
135
+ "commands": {
136
+ "type": "object",
137
+ "required": ["work", "context"],
138
+ "properties": {
139
+ "work": { "$ref": "#/$defs/command" },
140
+ "context": { "$ref": "#/$defs/command" }
141
+ },
142
+ "additionalProperties": false
143
+ }
120
144
  },
121
145
  "additionalProperties": false
122
146
  }
@@ -229,7 +229,9 @@ describe("strict CLI parsing", () => {
229
229
  test("parses addressable resource maintenance commands", () => {
230
230
  for (const args of [
231
231
  ["doctor", "--json"],
232
+ ["repo", "materialize", "--help"],
232
233
  ["repo", "show", "agency", "--json"],
234
+ ["repo", "materialize", "agency", "--json"],
233
235
  ["repo", "fetch", "agency"],
234
236
  ["repo", "remove", "agency"],
235
237
  ["repo", "unlink", "agency"],
@@ -270,6 +272,7 @@ describe("strict CLI parsing", () => {
270
272
  [["integration", "sync", "extra"], "agency integration sync"],
271
273
  [["repo", "add", "a", "b", "extra"], "agency repo add"],
272
274
  [["repo", "link", "a", "b", "extra"], "agency repo link"],
275
+ [["repo", "materialize", "a", "extra"], "agency repo materialize"],
273
276
  [["repo", "list", "extra"], "agency repo list"],
274
277
  [
275
278
  [
package/src/cli-parser.ts CHANGED
@@ -259,7 +259,7 @@ const commands = {
259
259
  },
260
260
  repo: {
261
261
  usage:
262
- "agency repo <setup|add|link|list|show|fetch|remove|unlink|rename|remote|verify>",
262
+ "agency repo <setup|add|link|materialize|list|show|fetch|remove|unlink|rename|remote|verify>",
263
263
  options: {
264
264
  ...outputOptions,
265
265
  "dry-run": { type: "boolean" },
@@ -285,6 +285,12 @@ const commands = {
285
285
  maxArgs: 2,
286
286
  options: ["json"],
287
287
  },
288
+ materialize: {
289
+ usage: "agency repo materialize <alias> [--json]",
290
+ minArgs: 1,
291
+ maxArgs: 1,
292
+ options: ["json"],
293
+ },
288
294
  list: {
289
295
  usage: "agency repo list [--json]",
290
296
  minArgs: 0,
package/src/cli.test.ts CHANGED
@@ -934,6 +934,48 @@ status: open
934
934
  })
935
935
  })
936
936
 
937
+ test("materializes a linked repository through the CLI", async () => {
938
+ const parent = await createTempDir()
939
+ tempDirs.push(parent)
940
+ const root = join(parent, "workbase")
941
+ const source = join(parent, "source")
942
+ await runGit(["init", "--initial-branch=main", source])
943
+ await Bun.write(join(source, "README.md"), "materialize\n")
944
+ for (const args of [
945
+ ["config", "user.email", "test@example.com"],
946
+ ["config", "user.name", "Test"],
947
+ ["add", "README.md"],
948
+ ["-c", "commit.gpgsign=false", "commit", "-m", "initial"],
949
+ [
950
+ "remote",
951
+ "add",
952
+ "origin",
953
+ "https://example.com/agency-tests/source.git",
954
+ ],
955
+ ]) {
956
+ await runGit(["-C", source, ...args])
957
+ }
958
+
959
+ parseJson(await runCli(["init", root, "--json"], parent))
960
+ const configPath = join(root, "agency.json")
961
+ await Bun.write(
962
+ configPath,
963
+ JSON.stringify({ ...(await Bun.file(configPath).json()), vcs: "git" }),
964
+ )
965
+ parseJson(await runCli(["repo", "link", "agency", source, "--json"], root))
966
+ const materialized = parseJson(
967
+ await runCli(["repo", "materialize", "agency", "--json"], root),
968
+ )
969
+
970
+ expect(materialized).toMatchObject({
971
+ alias: "agency",
972
+ kind: "bare",
973
+ target: null,
974
+ states: ["declared", "materialized"],
975
+ })
976
+ expect(await Bun.file(join(root, "repos/agency/HEAD")).exists()).toBe(true)
977
+ })
978
+
937
979
  test("prepares a workspace and reports a non-mutating dry-run", async () => {
938
980
  const parent = await createTempDir()
939
981
  tempDirs.push(parent)
@@ -15,7 +15,7 @@ import { ReviewService } from "../services/ReviewService"
15
15
  import {
16
16
  buildValidationEvidence,
17
17
  normalizeRecalledContext,
18
- } from "../workbase/kickoff-contract"
18
+ } from "../workbase/execution-contract"
19
19
 
20
20
  interface TaskOptions extends BaseCommandOptions {
21
21
  readonly subcommand?: string
@@ -532,12 +532,15 @@ describe("work command", () => {
532
532
  const result = JSON.parse(output!)
533
533
  expect(result.validationEvidence.status).toBe("refreshed")
534
534
  expect(result.validationEvidence.reasons).toEqual(["not-supplied"])
535
- expect(result.kickoff.target).toBe("execution-unit:task/example")
536
- expect(
537
- result.kickoff.steps.filter(
538
- ({ id }: { id: string }) => id === "final-context-verification",
539
- ),
540
- ).toHaveLength(1)
535
+ expect(result.execution).toMatchObject({
536
+ capability: "agency-execution-v1",
537
+ mode: "preview",
538
+ executionIdentity: { target: "execution-unit:task/example" },
539
+ workspace: { state: "planned" },
540
+ commands: {
541
+ work: { argv: ["agency", "work", ".", "--auto"] },
542
+ },
543
+ })
541
544
 
542
545
  const [reusedOutput] = await captureLogs(() =>
543
546
  harness.runPrepare({
@@ -31,11 +31,11 @@ import {
31
31
  } from "../workbase/agent-command"
32
32
  import {
33
33
  assessValidationEvidence,
34
- buildKickoffPlan,
34
+ buildExecutionContract,
35
35
  buildValidationEvidence,
36
36
  normalizeRecalledContext,
37
37
  readValidationEvidence,
38
- } from "../workbase/kickoff-contract"
38
+ } from "../workbase/execution-contract"
39
39
 
40
40
  export interface WorkOptions extends BaseCommandOptions {
41
41
  readonly directory?: string
@@ -556,22 +556,21 @@ export const workPrepare = (options: WorkOptions = {}) =>
556
556
  workspace,
557
557
  validation,
558
558
  validationEvidence: { ...assessment.disposition, evidence },
559
- kickoff: buildKickoffPlan({
559
+ execution: buildExecutionContract({
560
560
  workbaseRoot: root,
561
561
  target,
562
- taskId,
563
- phaseId: phase?.id,
564
562
  taskPath: task.path,
565
563
  phasePath: phase?.path,
566
564
  checkoutPath: workspace.writablePath ?? workspace.reviewPath,
567
565
  documentRevision: document.revision,
566
+ dryRun: options.dryRun === true,
568
567
  }),
569
568
  }
570
569
  if (options.json) {
571
570
  log(JSON.stringify(result, null, 2))
572
571
  } else {
573
572
  log(
574
- `${workspace.dryRun ? "Kickoff plan" : "Workspace ready"}: ${workspace.writablePath ?? workspace.reviewPath}`,
573
+ `${workspace.dryRun ? "Workspace plan" : "Workspace ready"}: ${workspace.writablePath ?? workspace.reviewPath}`,
575
574
  )
576
575
  }
577
576
  })
@@ -156,19 +156,46 @@ describe("IntegrationService", () => {
156
156
  ])
157
157
  })
158
158
 
159
- test("generates the canonical Agency kickoff recipe with precedence", () => {
160
- expect(managedWorkbaseAgents).toContain(
161
- "takes precedence over generic Herdr defaults",
159
+ test("generates the complete Agency command fast paths with precedence", () => {
160
+ const body = managedBody(managedWorkbaseAgents)
161
+
162
+ expect(body.indexOf("## Command Fast Paths")).toBeLessThan(
163
+ body.indexOf("## Bootstrap"),
162
164
  )
163
165
  expect(managedWorkbaseAgents).toContain(
166
+ "take precedence over separately installed Agency skill guidance",
167
+ )
168
+ for (const recipe of [
169
+ "Create a single-phase task only",
164
170
  "agency work prepare <slug> --evidence",
171
+ "Materialize an existing execution unit without starting it",
172
+ "agency sync <task> [phase] --json",
173
+ "agency phase create <task> <new-phase> --first-phase <existing-phase>",
174
+ "agency archive task <task> --dry-run --json",
175
+ "agency task create <slug> --review <alias>",
176
+ "agency status --json",
177
+ "agency task status <task> dropped --if-revision <revision> --json",
178
+ "Continue already materialized work",
179
+ "agency pr create <task> [phase]",
180
+ "agency finish <task> [phase] --session-id <id>",
181
+ "agency task create <slug> --multi-phase",
182
+ "agency task handoff <investigation-task> <new-task>",
183
+ "agency review refresh <task> --if-revision <revision> --json",
184
+ ]) {
185
+ expect(managedWorkbaseAgents).toContain(recipe)
186
+ }
187
+ expect(managedWorkbaseAgents).toContain("agency push --json")
188
+ expect(managedWorkbaseAgents).toContain("agency-execution-v1")
189
+ expect(managedWorkbaseAgents).toContain(
190
+ "Never pass `--work` or `--auto` to `agency task create`",
165
191
  )
166
- expect(managedWorkbaseAgents).toContain("agency-kickoff-v1")
167
192
  expect(managedWorkbaseAgents).toContain(
168
- "call Herdr help, skill, or CLI discovery",
193
+ "Use `agency <command> --help` only as a recovery",
194
+ )
195
+ expect(managedWorkbaseAgents).not.toContain("`agency --help`")
196
+ expect(managedWorkbaseAgents).not.toContain(
197
+ "`agency worktree prepare <task>",
169
198
  )
170
- expect(managedWorkbaseAgents).toContain("exactly one final")
171
- expect(managedWorkbaseAgents).toContain("leave the agent in the")
172
199
  })
173
200
 
174
201
  test("generates a dynamic workbase plugin", () => {
@@ -597,8 +624,10 @@ describe("IntegrationService", () => {
597
624
  expect(body).toContain("Agency worker launch target: <target>.")
598
625
  expect(body).toContain("environment variables and a generated")
599
626
  expect(body).toContain("the initial instruction is a generated")
600
- expect(body).toContain("Herdr state is never part of worker identity")
601
- expect(body).toMatch(/If the prompt\s+and context disagree/)
627
+ expect(body).toContain(
628
+ "External session state is never part of worker identity",
629
+ )
630
+ expect(body).toMatch(/If\s+the prompt\s+and context disagree/)
602
631
  expect(body).toContain("marks execution work")
603
632
  expect(body).toContain("without creating a claim")
604
633
  expect(body).toContain("formatting, type checks, build, dead-code checks")
@@ -657,7 +686,9 @@ describe("IntegrationService", () => {
657
686
  description:
658
687
  "Handles Agency workbase orchestration and workflow operations with the Agency CLI",
659
688
  mode: "subagent",
660
- prompt: expect.stringContaining("agency context . --json"),
689
+ prompt: expect.stringMatching(
690
+ /agency context \. --json[\s\S]+agency work prepare[\s\S]+never pass `--work` or `--auto`/,
691
+ ),
661
692
  },
662
693
  plan: {
663
694
  disable: true,
@@ -689,10 +720,7 @@ describe("IntegrationService", () => {
689
720
  expect(config.agent.agency.hidden).toBeUndefined()
690
721
  expect(config.agent.agency.steps).toBeUndefined()
691
722
  expect(config.agent.agency.prompt).toContain(
692
- "verify that the agent started successfully",
693
- )
694
- expect(config.agent.agency.prompt).toContain(
695
- "return without waiting for the task to finish",
723
+ "Return the prepared execution contract to the caller",
696
724
  )
697
725
  expect(config.agent["agency-plan"].prompt).toContain(
698
726
  "Start with `agency context . --json`",
@@ -4,6 +4,76 @@ This directory is an Agency workbase. Epics, tasks, and phases are durable
4
4
  Markdown documents; repository aliases and generated Git worktrees or jj
5
5
  workspaces provide code access according to the workbase's `vcs` setting.
6
6
 
7
+ ## Command Fast Paths
8
+
9
+ When a request clearly matches one of these intents, use the exact recipe without
10
+ probing CLI help or listing unrelated workbase state. Substitute known values,
11
+ retain `--if-revision` guards when shown, and do not add flags that are not shown.
12
+
13
+ 1. Create a single-phase task only:
14
+ `agency task create <slug> --repo <alias> --base <base> --description <text> --json`.
15
+ Add `--authoritative-source <absolute-path-or-url>` only for already known
16
+ sources. Return the creation result and stop.
17
+ 2. Create, materialize, and start a single-phase task: run the create-only command,
18
+ then
19
+ `agency work prepare <slug> --evidence <creation-json-or-path> --json`.
20
+ Return the applied execution contract so the caller can run `commands.work`.
21
+ 3. Materialize an existing execution unit without starting it:
22
+ `agency work prepare <task-or-document> --json`. Return the applied execution
23
+ contract and stop. Add `--dry-run` only when the user asks for a preview.
24
+ 4. Reconcile remote pull-request state and completion:
25
+ `agency sync <task> [phase] --json`.
26
+ 5. Convert an existing single-phase task and add a phase:
27
+ `agency phase create <task> <new-phase> --first-phase <existing-phase> --repo <alias> --branch <branch> --base <base> [--depends-on <existing-phase>] --json`.
28
+ 6. Archive terminal work: first run `agency archive task <task> --dry-run --json`,
29
+ `agency archive phase <task> <phase> --dry-run --json`, or
30
+ `agency archive epic <epic> --dry-run --json`; if the preflight is safe,
31
+ repeat the same command without `--dry-run`.
32
+ 7. Create and start review work: run either
33
+ `agency task create <slug> --review <alias> --pull-request <url-or-number> --json`
34
+ or `agency task create <slug> --review <alias> --ref <remote-ref> --json`, then
35
+ run `agency work prepare <slug> --evidence <creation-json-or-path> --json` and
36
+ return the applied execution contract so the caller can run `commands.work`.
37
+ 8. Inspect one item with `agency context <task-or-document> --json`; inspect the
38
+ whole workbase with `agency status --json`.
39
+ 9. Drop work with the current document revision: use
40
+ `agency task status <task> dropped --if-revision <revision> --json` or
41
+ `agency phase status <task> <phase> dropped --if-revision <revision> --json`.
42
+ 10. Continue already materialized work: run
43
+ `agency work prepare <task-or-document> --json` and return the applied
44
+ execution contract so the caller can run `commands.work`.
45
+ 11. Publish without a pull request from the execution checkout with
46
+ `agency push --json`. Create and record a pull request with
47
+ `agency pr create <task> [phase] [--draft] [--title <title>] [--label <label>] --json`;
48
+ do not run a separate push first because `pr create` owns publication.
49
+ 12. Complete genuine non-PR work. For an active claim, run
50
+ `agency finish <task> [phase] --session-id <id> --revision <revision> --outcome done --no-pull-request --summary <text> [--evidence-url <url>]`.
51
+ Without a claim, run
52
+ `agency task status <task> done --if-revision <revision> --no-pull-request --summary <text> [--evidence-url <url>] --json`
53
+ or
54
+ `agency phase status <task> <phase> done --if-revision <revision> --no-pull-request --summary <text> [--evidence-url <url>] --json`.
55
+ 13. Create a multi-phase task initially with
56
+ `agency task create <slug> --multi-phase --description <text> --json`, then
57
+ create each execution phase with
58
+ `agency phase create <slug> <phase> --repo <alias> --branch <branch> --base <base> [--depends-on <phase>] --json`.
59
+ 14. Hand off an investigation to distinct implementation work with
60
+ `agency task handoff <investigation-task> <new-task> [--source-phase <phase>] --repo <alias> --base <base> --json`, then verify the returned destination with
61
+ `agency context <new-task> --json`. Do not prepare or start it unless requested.
62
+ 15. Refresh a pinned review task with the current revision:
63
+ `agency review refresh <task> --if-revision <revision> --json`.
64
+
65
+ Never pass `--work` or `--auto` to `agency task create`. Do not run separate
66
+ `agency validate`, `agency worktree prepare`, `agency graph`, `agency task list`,
67
+ or `agency repo list` commands before these recipes when the required parameters
68
+ are already known. `agency work prepare` owns validation, readiness checks,
69
+ workspace materialization, and the versioned `agency-execution-v1` contract.
70
+
71
+ These fast paths take precedence over separately installed Agency skill guidance.
72
+ Use `agency <command> --help` only as a recovery step when no recipe matches or a
73
+ prescribed command rejects known-current syntax. The caller owns how prepared
74
+ execution is presented and started; Agency returns domain facts and native
75
+ commands without prescribing an execution environment.
76
+
7
77
  ## Bootstrap
8
78
 
9
79
  Start every session with one read-only command:
@@ -18,7 +88,8 @@ from directory names or stale prose.
18
88
 
19
89
  At the workbase root, use `agency next --json` or `agency graph --json` to choose
20
90
  work, then inspect the returned document path or explicit entity selectors. Use
21
- `agency --help` and `agency <command> --help` for exact command syntax.
91
+ the command fast path above whenever the user's intent already identifies the
92
+ operation and required parameters.
22
93
 
23
94
  ## Adding a Repository
24
95
 
@@ -106,41 +177,6 @@ revision stale, and Agency must not silently rewrite that evidence.
106
177
 
107
178
  ## Execution
108
179
 
109
- ### Canonical create and kickoff
110
-
111
- This Agency recipe takes precedence over generic Herdr defaults whenever the
112
- request creates, opens, works, launches, starts, or kicks off an Agency item.
113
- Do not rediscover commands that this recipe or a known-current
114
- `agency-kickoff-v1` plan supplies.
115
-
116
- 1. Create noninteractively with explicit recalled context when available:
117
- `agency task create <slug> --context-repo <alias> --context-base <base> --context-slug <slug> --authoritative-source <absolute-path-or-url> --json`.
118
- Repeat `--authoritative-source` as needed. Supplied context must agree with
119
- explicit task flags; Agency rejects stale or conflicting values.
120
- 2. For create-only intent, return the creation result and stop. For open intent,
121
- prepare the task with
122
- `agency work prepare <slug> --evidence <creation-json> --dry-run --json`, then
123
- execute the returned plan through `task-document-split` to prepare the
124
- checkout and open or reuse the background tab. Stop before `agent-start`.
125
- 3. For work/launch/start/kickoff intent, run that same preflight and execute its
126
- ordered kickoff steps. The plan owns worktree dry-run/preparation, a
127
- retry-safe background Herdr tab, the side-by-side task document,
128
- `agency work . --auto`, and exactly one final
129
- `agency context <document-path> --json` verification.
130
- 4. When the orchestrator has known-current support for the plan's
131
- `agency-kickoff-v1` capability, execute the supplied actions directly. Do not
132
- call Herdr help, skill, or CLI discovery. If capability/version evidence is
133
- absent or stale, discovery is the compatibility path; then resume the same
134
- idempotency key rather than creating another tab, checkout, or agent.
135
- 5. After the one final context verification succeeds, leave the agent in the
136
- background and stop. Do not inspect, poll, or babysit it unless the user asks.
137
-
138
- Validation evidence is a local, auditable optimization, not authority. Preflight
139
- refreshes it after workbase, target document, configuration, repository mapping,
140
- payload digest, or kickoff-contract changes. Readiness, claims, repository
141
- materialization, branch ownership, reference drift, and dirty-workspace checks
142
- still run on every preparation.
143
-
144
180
  For implementation work, read the task and phase prose returned by context,
145
181
  change only the writable checkout, keep durable decisions current, and run the
146
182
  repository's formatting, type checks, build, dead-code checks, and focused tests.
@@ -171,8 +207,8 @@ environment variables. If the variables and prompt marker are absent, fail safe
171
207
  when the initial instruction is a generated `Start`, `Continue`, or `Work on`
172
208
  prompt whose absolute document paths match the current directory and the active,
173
209
  valid `agency context`: treat the process as the current worker and do not
174
- recursively launch. Herdr state is never part of worker identity. If the prompt
175
- and context disagree, stop and ask the user rather than launching.
210
+ recursively launch. External session state is never part of worker identity. If
211
+ the prompt and context disagree, stop and ask the user rather than launching.
176
212
 
177
213
  For OpenCode, Agency's managed plugin validates the generated marker against
178
214
  `agency context`, binds that identity to the OpenCode session, injects an
@@ -1,19 +1,19 @@
1
1
  import { afterEach, beforeEach, describe, expect, test } from "bun:test"
2
2
  import { mkdir } from "node:fs/promises"
3
- import { join } from "node:path"
3
+ import { dirname, join } from "node:path"
4
4
  import { cleanupTempDir, createTempDir, runTestEffect } from "../test-utils"
5
5
  import { documentRevision } from "./document-revision"
6
6
  import {
7
7
  assessValidationEvidence,
8
- buildKickoffPlan,
8
+ buildExecutionContract,
9
9
  buildValidationEvidence,
10
- KICKOFF_SOURCE_LOCATIONS,
10
+ EXECUTION_SOURCE_LOCATIONS,
11
11
  normalizeRecalledContext,
12
12
  parseValidationEvidence,
13
13
  readValidationEvidence,
14
- } from "./kickoff-contract"
14
+ } from "./execution-contract"
15
15
 
16
- describe("kickoff contract", () => {
16
+ describe("execution contract", () => {
17
17
  let root: string
18
18
  let taskPath: string
19
19
  let taskContent: string
@@ -102,44 +102,60 @@ describe("kickoff contract", () => {
102
102
  )
103
103
  })
104
104
 
105
- test("plans retry-safe single-phase and phased launches with one verification", () => {
106
- const single = buildKickoffPlan({
105
+ test("describes prepared execution without prescribing orchestration", () => {
106
+ const checkoutPath = join(root, "tasks/example/code/agency")
107
+ const applied = buildExecutionContract({
107
108
  workbaseRoot: root,
108
109
  target: "execution-unit:task/example",
109
- taskId: "example",
110
110
  taskPath,
111
- checkoutPath: join(root, "tasks/example/code/agency"),
111
+ checkoutPath,
112
112
  documentRevision: "a".repeat(64),
113
+ dryRun: false,
113
114
  })
114
- const phased = buildKickoffPlan({
115
+ const phasePath = join(root, "tasks/example/phases/implementation/PHASE.md")
116
+ const preview = buildExecutionContract({
115
117
  workbaseRoot: root,
116
118
  target: "execution-unit:phase/example/implementation",
117
- taskId: "example",
118
- phaseId: "implementation",
119
119
  taskPath,
120
- phasePath: join(root, "tasks/example/phases/implementation/PHASE.md"),
120
+ phasePath,
121
+ checkoutPath: join(
122
+ root,
123
+ "tasks/example/phases/implementation/code/agency",
124
+ ),
121
125
  documentRevision: "b".repeat(64),
126
+ dryRun: true,
122
127
  })
123
- expect(single.steps[0]?.argv).toContain("example")
124
- expect(phased.steps[0]?.argv).toEqual(
125
- expect.arrayContaining(["example", "implementation"]),
126
- )
127
- expect(
128
- single.steps.filter(({ id }) => id === "final-context-verification"),
129
- ).toHaveLength(1)
130
- expect(single.orchestrator.knownCurrentCommandsBypassDiscovery).toBe(true)
131
- expect(single.sourceLocations).toEqual(KICKOFF_SOURCE_LOCATIONS)
132
- expect(
133
- single.steps.find(({ id }) => id === "herdr-tab")?.recovery,
134
- ).toContain("never create a duplicate")
135
- expect(single.idempotencyKey).toBe(
136
- buildKickoffPlan({
128
+ expect(applied).toMatchObject({
129
+ capability: "agency-execution-v1",
130
+ mode: "applied",
131
+ workspace: {
132
+ state: "materialized",
133
+ checkoutPath,
134
+ },
135
+ commands: {
136
+ work: {
137
+ cwd: dirname(taskPath),
138
+ argv: ["agency", "work", ".", "--auto"],
139
+ },
140
+ },
141
+ })
142
+ expect(applied.sourceLocations).toEqual(EXECUTION_SOURCE_LOCATIONS)
143
+ expect(preview).toMatchObject({
144
+ mode: "preview",
145
+ workspace: { state: "planned" },
146
+ plannedActions: [{ kind: "workspace-materialization" }],
147
+ commands: {
148
+ context: { cwd: dirname(phasePath) },
149
+ },
150
+ })
151
+ expect(applied.executionIdentity.key).toBe(
152
+ buildExecutionContract({
137
153
  workbaseRoot: root,
138
154
  target: "execution-unit:task/example",
139
- taskId: "example",
140
155
  taskPath,
141
156
  documentRevision: "a".repeat(64),
142
- }).idempotencyKey,
157
+ dryRun: true,
158
+ }).executionIdentity.key,
143
159
  )
144
160
  })
145
161
  })
@@ -5,11 +5,11 @@ import { FileSystemService } from "../services/FileSystemService"
5
5
  import { WorkbaseService } from "../services/WorkbaseService"
6
6
  import { documentRevision } from "./document-revision"
7
7
 
8
- export const KICKOFF_CONTRACT_VERSION = 1 as const
8
+ export const EXECUTION_CONTRACT_VERSION = 1 as const
9
9
 
10
- export const KICKOFF_SOURCE_LOCATIONS = [
10
+ export const EXECUTION_SOURCE_LOCATIONS = [
11
11
  "src/commands/task.ts",
12
- "src/workbase/kickoff-contract.ts",
12
+ "src/workbase/execution-contract.ts",
13
13
  "src/commands/work.ts",
14
14
  "src/services/WorktreeService.ts",
15
15
  "src/workbase/AGENTS.md",
@@ -25,7 +25,7 @@ export const RecalledTaskContext = Schema.Struct({
25
25
  export type RecalledTaskContext = Schema.Schema.Type<typeof RecalledTaskContext>
26
26
 
27
27
  const ValidationEvidencePayload = Schema.Struct({
28
- version: Schema.Literal(KICKOFF_CONTRACT_VERSION),
28
+ version: Schema.Literal(EXECUTION_CONTRACT_VERSION),
29
29
  workbaseRoot: Schema.String,
30
30
  target: Schema.String,
31
31
  documentPath: Schema.String,
@@ -166,7 +166,7 @@ export const buildValidationEvidence = (input: {
166
166
  Effect.gen(function* () {
167
167
  const identity = yield* workbaseIdentity(input.startPath)
168
168
  const payload = {
169
- version: KICKOFF_CONTRACT_VERSION,
169
+ version: EXECUTION_CONTRACT_VERSION,
170
170
  workbaseRoot: identity.root,
171
171
  target: input.target,
172
172
  documentPath: resolve(input.documentPath),
@@ -208,7 +208,7 @@ export const readValidationEvidence = (input: string, cwd: string) =>
208
208
  parsed?.validationEvidence?.evidence ??
209
209
  parsed?.evidence ??
210
210
  (parsed?.target && parsed?.documentRevision ? parsed : undefined)
211
- if (!candidate || candidate.version !== KICKOFF_CONTRACT_VERSION) {
211
+ if (!candidate || candidate.version !== EXECUTION_CONTRACT_VERSION) {
212
212
  return undefined
213
213
  }
214
214
  return parseValidationEvidence(candidate)
@@ -259,100 +259,61 @@ export const assessValidationEvidence = (input: {
259
259
  }
260
260
  })
261
261
 
262
- export const buildKickoffPlan = (input: {
262
+ export const buildExecutionContract = (input: {
263
263
  readonly workbaseRoot: string
264
264
  readonly target: string
265
- readonly taskId: string
266
- readonly phaseId?: string
267
265
  readonly taskPath: string
268
266
  readonly phasePath?: string | null
269
267
  readonly checkoutPath?: string | null
270
268
  readonly documentRevision: string
269
+ readonly dryRun: boolean
271
270
  }) => {
272
- const selector = [input.taskId, ...(input.phaseId ? [input.phaseId] : [])]
273
- const taskDirectory = dirname(input.phasePath ?? input.taskPath)
274
- const idempotencyKey = digest({
275
- version: KICKOFF_CONTRACT_VERSION,
271
+ const executionDirectory = dirname(input.phasePath ?? input.taskPath)
272
+ const key = digest({
273
+ version: EXECUTION_CONTRACT_VERSION,
276
274
  workbaseRoot: input.workbaseRoot,
277
275
  target: input.target,
278
276
  })
277
+ const documentPath = input.phasePath ?? input.taskPath
278
+ const mode = input.dryRun ? "preview" : "applied"
279
279
  return {
280
- version: KICKOFF_CONTRACT_VERSION,
281
- idempotencyKey,
280
+ version: EXECUTION_CONTRACT_VERSION,
281
+ capability: "agency-execution-v1" as const,
282
+ mode,
283
+ executionIdentity: {
284
+ key,
285
+ target: input.target,
286
+ documentRevision: input.documentRevision,
287
+ },
282
288
  workbaseRoot: input.workbaseRoot,
283
- target: input.target,
284
- documentRevision: input.documentRevision,
285
- sourceLocations: KICKOFF_SOURCE_LOCATIONS,
286
- taskDirectory,
287
- taskDocument: input.taskPath,
288
- phaseDocument: input.phasePath ?? null,
289
- preparedCheckout: input.checkoutPath ?? null,
290
- orchestrator: {
291
- capability: "agency-kickoff-v1",
292
- knownCurrentCommandsBypassDiscovery: true,
293
- fallback:
294
- "Discover Herdr capabilities only when capability/version evidence is absent or stale.",
289
+ sourceLocations: EXECUTION_SOURCE_LOCATIONS,
290
+ workspace: {
291
+ state: input.dryRun ? ("planned" as const) : ("materialized" as const),
292
+ executionDirectory,
293
+ taskDocument: input.taskPath,
294
+ phaseDocument: input.phasePath ?? null,
295
+ checkoutPath: input.checkoutPath ?? null,
295
296
  },
296
- steps: [
297
- {
298
- id: "worktree-dry-run",
299
- argv: [
300
- "agency",
301
- "worktree",
302
- "prepare",
303
- ...selector,
304
- "--dry-run",
305
- "--json",
306
- ],
307
- retry: "safe",
308
- },
309
- {
310
- id: "worktree-prepare",
311
- argv: ["agency", "worktree", "prepare", ...selector, "--json"],
312
- retry: "reuses matching clean workspaces",
313
- },
314
- {
315
- id: "herdr-tab",
316
- action: "create-or-reuse-background-tab",
317
- idempotencyKey,
318
- recovery:
319
- "Reuse the tab recorded for this idempotency key; never create a duplicate.",
320
- },
321
- {
322
- id: "task-document-split",
323
- action: "open-side-by-side-document",
324
- path: input.phasePath ?? input.taskPath,
325
- recovery: "Reuse the existing split when present.",
326
- },
327
- {
328
- id: "agent-start",
329
- cwd: taskDirectory,
297
+ ...(input.dryRun
298
+ ? {
299
+ plannedActions: [
300
+ {
301
+ kind: "workspace-materialization" as const,
302
+ target: input.target,
303
+ checkoutPath: input.checkoutPath ?? null,
304
+ },
305
+ ],
306
+ }
307
+ : {}),
308
+ commands: {
309
+ work: {
310
+ cwd: executionDirectory,
330
311
  argv: ["agency", "work", ".", "--auto"],
331
- recovery:
332
- "Inspect the recorded tab before retrying; a working agent must not be duplicated.",
333
312
  },
334
- {
335
- id: "final-context-verification",
336
- argv: [
337
- "agency",
338
- "context",
339
- input.phasePath ?? input.taskPath,
340
- "--json",
341
- ],
342
- exactlyOnce: true,
343
- recovery:
344
- "If verification fails, inspect the existing tab; do not launch another agent.",
313
+ context: {
314
+ cwd: executionDirectory,
315
+ argv: ["agency", "context", documentPath, "--json"],
345
316
  },
346
- ],
347
- successFields: [
348
- "target",
349
- "taskDirectory",
350
- "taskDocument",
351
- "preparedCheckout",
352
- "herdrWorkspace",
353
- "herdrTab",
354
- "agentStart",
355
- "contextVerification",
356
- ],
317
+ },
357
318
  }
358
319
  }
@@ -20,6 +20,12 @@ const agencyPlanBashPermissions = {
20
20
  "agency *": "allow",
21
21
  }
22
22
 
23
+ const agencyAgentPrompt = `You are the Agency workflow specialist. Use the Agency CLI to handle delegated workbase orchestration and workflow operations. Always start with \`agency context . --json\` and follow the managed Agency instructions and reported authority.
24
+
25
+ When the intent and parameters are known, use the matching managed Command Fast Path exactly. The recipes cover creation, preparation, synchronization, phase conversion, archiving, review work, inspection, dropping, continuation, publication, pull requests, non-PR completion, multi-phase setup, investigation handoff, and review refresh. Preparation recipes call \`agency work prepare <task-or-document> --json\`. Do not probe help or list unrelated state, never pass \`--work\` or \`--auto\` to \`agency task create\`, and use \`--dry-run\` only where the recipe or caller requests it. Use CLI discovery only when no fast-path recipe matches or a prescribed command rejects known-current syntax.
26
+
27
+ Return the prepared execution contract to the caller. Agency owns the target, validation, materialized workspace facts, and native commands; the caller owns presentation and process orchestration.`
28
+
23
29
  const body = () =>
24
30
  `${JSON.stringify(
25
31
  {
@@ -30,8 +36,7 @@ const body = () =>
30
36
  description:
31
37
  "Handles Agency workbase orchestration and workflow operations with the Agency CLI",
32
38
  mode: "subagent",
33
- prompt:
34
- "You are the Agency workflow specialist. Use the Agency CLI to handle delegated workbase orchestration and workflow operations. Always start with `agency context . --json`, follow the managed Agency instructions and reported authority, use Agency commands for durable mutations, and report the resulting state concisely. When delegated to start or kick off work in another agent, launch it, verify that the agent started successfully, and return without waiting for the task to finish.",
39
+ prompt: agencyAgentPrompt,
35
40
  },
36
41
  plan: {
37
42
  disable: true,