relay-flow 0.2.5-alpha → 0.2.7-alpha

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
@@ -1,36 +1,160 @@
1
1
  # relay-flow
2
2
 
3
- Durable, graph-based agent workflow runner. A ticket is the unit of work; a workflow YAML declares nodes and routes; a durable engine (go-workflows + SQLite) drives progression, waits, retries, and recovery. The task system (Jira and Beads built-ins) supplies parent tickets and mailbox subtasks; the runner (Orca built-in) owns worktrees and terminals; the harness (OpenCode and Pi built-ins) owns agent sessions and report semantics. All three are pluggable.
3
+ Durable, graph-based agent workflow runner. A ticket is the unit of work; a workflow YAML declares nodes and routes; a durable executor drives progression, waits, retries, and recovery. Task systems (Jira and Beads), runners (Orca and Herdr), harnesses (OpenCode and Pi), and durable executors (go-workflows and Temporal) are selectable independently.
4
4
 
5
5
  This is a ground-up rewrite. The previous per-workflow, in-memory daemon is gone. There is no migration path and no compatibility layer.
6
6
 
7
7
  ---
8
8
 
9
- ## Setup
9
+ ## Quick start
10
10
 
11
- ### Prerequisites
11
+ ### 1. Install relay-flow
12
12
 
13
- | Tool | Why |
14
- |---|---|
15
- | [opencode](https://opencode.ai) | Agents run in opencode sessions (harness) |
16
- | [Orca](https://github.com/Necmttn/orca) CLI + app | Worktrees + terminals (runner) |
17
- | `bd` CLI | Beads task-system access (required when `taskPlugin: beads`) |
18
- | Dolt | External/server-backed Beads only |
19
- | Jira API token | Jira REST API v3 access (required when `taskPlugin: jira`) |
20
- | Go 1.24+ | Build the CLI |
13
+ Install the latest released CLI with Homebrew:
21
14
 
22
- ### Install
15
+ ```sh
16
+ brew install rajpopat27/tap/relay-flow
17
+ ```
18
+
19
+ ### 2. Install the harness extension
20
+
21
+ Choose the harness that will run your agent sessions. Both commands install the
22
+ same `relay-flow-plugin` package with host-specific entrypoints.
23
+
24
+ **OpenCode**
25
+
26
+ ```sh
27
+ opencode plugin relay-flow-plugin@0.2.7-alpha
28
+ ```
29
+
30
+ **Pi**
31
+
32
+ ```sh
33
+ pi install npm:relay-flow-plugin@0.2.7-alpha
34
+ ```
35
+
36
+ Pi loads the package's `pi.ts` extension from its manifest. Do not add
37
+ `-e`/`--extension` to relay-flow's Pi launch command.
38
+
39
+ ### 3. Initialize relay-flow
40
+
41
+ Choose one task system, runner, harness, and durable executor. This example
42
+ uses Jira, Orca, OpenCode, and the default embedded executor:
23
43
 
24
44
  ```sh
25
- go install github.com/rajpopat27/relay-flow/cmd/relay-flow@latest
45
+ relay-flow init \
46
+ --task-plugin jira \
47
+ --runner-plugin orca \
48
+ --harness-plugin opencode
26
49
  ```
27
50
 
51
+ The default executor is `goworkflows` with SQLite. To use Temporal instead,
52
+ add `--executor-plugin temporal`, `--temporal-address <host:port>`, and
53
+ `--temporal-namespace <name>` to the command.
54
+
55
+ ### 4. Authenticate the task system
56
+
57
+ For Jira, authenticate the selected task plugin:
58
+
59
+ ```sh
60
+ relay-flow task auth
61
+ ```
62
+
63
+ For Beads, skip this command and initialize/authenticate the Beads workspace
64
+ with `bd` and, when needed, Dolt. See [Beads task system](#beads-task-system)
65
+ below.
66
+
67
+ ### 5. Start the server
68
+
69
+ ```sh
70
+ relay-flow serve --background
71
+ ```
72
+
73
+ ### 6. Register a repository
74
+
75
+ The repository must already exist in the selected runner. For Orca:
76
+
77
+ ```sh
78
+ orca repo add --path /work/payments
79
+ relay-flow repo register
80
+ ```
81
+
82
+ For Herdr, register the repository path directly; relay-flow creates ticket
83
+ worktrees lazily. The interactive registration asks for the task-system values
84
+ required by the selected task plugin.
85
+
86
+ ### 7. Submit a workflow
87
+
88
+ ```sh
89
+ relay-flow workflow submit --file examples/minimal-jira-task-workflow.yaml
90
+ relay-flow workflow list
91
+ ```
92
+
93
+ Replace the example workflow with
94
+ `examples/minimal-beads-task-workflow.yaml` when using Beads. The workflow's
95
+ `repos` value must match the name used during repository registration.
96
+
97
+ ## Supported plugins
98
+
99
+ Each category is a replaceable boundary. Select one plugin from each category
100
+ at initialization; the workflow YAML and core orchestration do not change when
101
+ you switch an implementation.
102
+
103
+ | Category | Supported plugins | Owns |
104
+ |---|---|---|
105
+ | Task system | `jira`, `beads` | Parent tickets, mailbox subtasks, task state, labels, comments, and task configuration |
106
+ | Runner | `orca`, `herdr` | Ticket worktrees, environments, terminals, process liveness, and cleanup |
107
+ | Harness | `opencode`, `pi` | Agent launch commands, sessions, prompts, report parsing, nudges, and resume behavior |
108
+ | Durable executor | `goworkflows`, `temporal` | Graph progression, waits, retries, recovery, and durable execution state |
109
+
110
+ The default durable executor is `goworkflows`, which stores execution state in
111
+ SQLite. `temporal` uses an external Temporal server and is selected with the
112
+ Temporal address and namespace during `init`.
113
+
114
+ The plugin composition is explicit:
115
+
116
+ ```text
117
+ Task system (Jira or Beads)
118
+
119
+
120
+ Durable executor (go-workflows or Temporal)
121
+
122
+
123
+ Runner (Orca or Herdr)
124
+
125
+
126
+ Harness (OpenCode or Pi)
127
+
128
+
129
+ relay-flow report transport
130
+ ```
131
+
132
+ The equivalent non-interactive selection is:
133
+
134
+ ```sh
135
+ relay-flow init \
136
+ --task-plugin <jira|beads> \
137
+ --runner-plugin <orca|herdr> \
138
+ --harness-plugin <opencode|pi> \
139
+ --executor-plugin <goworkflows|temporal>
140
+ ```
141
+
142
+ `--executor-plugin` defaults to `goworkflows`. Jira requires Jira credentials;
143
+ Beads requires the `bd` CLI and a configured workspace. Orca requires its CLI
144
+ and app; Herdr requires its CLI/server. OpenCode and Pi each require their
145
+ corresponding agent runtime. These integrations remain behind their small
146
+ contracts, so task-system fields do not leak into runners or harnesses.
147
+
148
+ ## Detailed setup
149
+
150
+ ### Harness configuration
151
+
28
152
  OpenCode plugin configuration uses both entrypoints. The server entrypoint is listed in `opencode.json`:
29
153
 
30
154
  ```json
31
155
  {
32
156
  "$schema": "https://opencode.ai/config.json",
33
- "plugin": ["relay-flow-plugin"]
157
+ "plugin": ["relay-flow-plugin@0.2.7-alpha"]
34
158
  }
35
159
  ```
36
160
 
@@ -39,7 +163,7 @@ The native HITL approval entrypoint is listed in `.opencode/tui.json`:
39
163
  ```json
40
164
  {
41
165
  "$schema": "https://opencode.ai/tui.json",
42
- "plugin": ["relay-flow-plugin"]
166
+ "plugin": ["relay-flow-plugin@0.2.7-alpha"]
43
167
  }
44
168
  ```
45
169
 
@@ -55,7 +179,7 @@ Pi plugin: install the same published package manually in Pi's global package
55
179
  settings before starting a Pi harness session:
56
180
 
57
181
  ```sh
58
- pi install npm:relay-flow-plugin@<version>
182
+ pi install npm:relay-flow-plugin@0.2.7-alpha
59
183
  ```
60
184
 
61
185
  Relay-flow does not install or configure the package automatically. Pi resolves
@@ -71,14 +195,14 @@ structured report, applies the agent/HITL nudge policy, and delivers
71
195
  `reportId` comes from the harness session/message identity; `nodeVisitID` is
72
196
  internal and is never part of either plugin payload.
73
197
 
74
- ### One-time machine setup
198
+ ### Machine setup details
75
199
 
76
200
  ```sh
77
201
  relay-flow init
78
202
  relay-flow task auth
79
203
  ```
80
204
 
81
- `init` only selects the task system, runner, and harness (singleton options are automatic), writes machine config, and initializes SQLite. `task auth` delegates authentication to that selected task plug-in. Jira prompts for its site, email, and masked API token, validates `/myself`, and owns the system-wide `credentials.yaml`; for scripts, pass `task auth --site`, `--email`, and `--token`. A normal init rerun refuses existing state. `relay-flow init --force` updates safe stopped instances while preserving durable and repo state.
205
+ `init` selects the task system, runner, harness, and durable executor (singleton options are automatic), writes machine config, and initializes the selected execution backend. `task auth` delegates authentication to that selected task plug-in. Jira prompts for its site, email, and masked API token, validates `/myself`, and owns the system-wide `credentials.yaml`; for scripts, pass `task auth --site`, `--email`, and `--token`. A normal init rerun refuses existing state. `relay-flow init --force` updates safe stopped instances while preserving durable and repo state.
82
206
 
83
207
  For Beads, select the plugin explicitly when scripting setup:
84
208
 
@@ -135,7 +259,7 @@ Shows a multi-select titled `Select repositories`; use Space to select Orca repo
135
259
 
136
260
  Each Jira poll uses REST v3 enhanced search and requests linked-issue status with the candidate fields. Tickets with any unfinished inward `Blocks` issue are filtered before routing; no per-ticket blocker lookup is made.
137
261
 
138
- For scripts, use `relay-flow repo register --name <name> --path <path> --set project=<project>`. Component is always derived from `--name` and cannot be overridden. Registration is rejected while another repo already holds the same canonical task scope.
262
+ For scripted Jira registration, use `relay-flow repo register --name <name> --path <path> --set project=<project>`. Component is always derived from `--name` and cannot be overridden. Registration is rejected while another repo already holds the same canonical task scope.
139
263
 
140
264
  ### Beads task system
141
265
 
@@ -177,7 +301,13 @@ bd init \
177
301
  # Example external workspace: /var/lib/beads/payments/.beads
178
302
  ```
179
303
 
180
- Register the code repository and its Beads workspace separately. `beadsDir` is required for every Beads repo, must name an existing directory, and is the task scope used to reject duplicate workspace registration:
304
+ For the relay-flow installation used by this repository, the configured Beads workspace is:
305
+
306
+ ```text
307
+ /home/raj/.beads/relay-flow/.beads
308
+ ```
309
+
310
+ Register the code repository and its Beads workspace separately. `beadsDir` is required for every Beads repo and must name an existing directory. It selects the Beads workspace/database; it is not the code repository path or a workflow filter:
181
311
 
182
312
  ```sh
183
313
  # Local/embedded workspace
@@ -193,7 +323,7 @@ relay-flow repo register \
193
323
  --set beadsDir=/var/lib/beads/payments/.beads
194
324
  ```
195
325
 
196
- The registered `--path` remains the code/runner repository. Every `bd` command runs with that path as its working directory and the configured `beadsDir` as `BEADS_DIR`, even when unrelated Beads selector variables exist in the relay-flow environment. Two repos may register different canonical `beadsDir` values; a second repo pointing at the same workspace is rejected. A Beads prefix such as `payments-...` is optional and only makes issue IDs recognizable—it is not a component, workspace selector, poller selector, or database isolation mechanism.
326
+ The registered `--path` remains the code/runner repository. Every `bd` command runs with that path as its working directory and the configured `beadsDir` as `BEADS_DIR`, even when unrelated Beads selector variables exist in the relay-flow environment. Multiple code repositories may share one Beads/Dolt workspace when their derived repository labels differ. Relay-flow derives one reserved label, `repo:<lowercase-registered-name>`, for each repository; names must use letters, numbers, `.`, `_`, or `-`, and be at most 250 characters so the derived label stays within Beads' 255-character limit. The label is not entered in workflow configuration. A parent must carry exactly one such repository label to be eligible for that repo's poller: missing or ambiguous `repo:` labels are ignored before workflow filters and routing. Workflow ownership labels remain independent `wf:<workflow>` labels. A collision between derived labels in the same workspace is rejected before the second repository is registered. A Beads prefix such as `payments-...` is optional and only makes issue IDs recognizable—it is not a component, workspace selector, poller selector, or database isolation mechanism.
197
327
 
198
328
  Beads workflow filters are structured and evaluated in relay-flow. For example, `examples/beads-workflow.yaml` uses the Beads status and issue-type fields:
199
329
 
@@ -211,7 +341,7 @@ Filter values are exact and are not translated between providers.
211
341
 
212
342
  The supported Beads status names are `open`, `in_progress`, `blocked`, `deferred`, `hooked`, and `closed`. The claimed-parent poll uses the canonical active set `open,in_progress,blocked,deferred`; it intentionally does not substitute `hooked` for `deferred`. Omitted lifecycle settings move the parent to `in_progress` at `start`, a work-node mailbox to `in_progress`, and the parent to `closed` at `end` — the same shape as Jira, with Beads-native values. Relay-flow creates one Repo Poller per registered repo, not one poller per workflow. Each poll reads ready top-level parents and relay-owned active parents, deduplicates them, and never routes mailbox children. Claims are permanent `wf:<workflow>` labels.
213
343
 
214
- Beads does not need relay-flow credentials or a Beads-specific poller. In server mode, leave Dolt and Beads server setup running outside relay-flow and point each repo at its own `beadsDir`.
344
+ Beads does not need relay-flow credentials or a Beads-specific poller. In server mode, leave Dolt and Beads server setup running outside relay-flow; each registered code repository still supplies its `beadsDir`, and repositories that share that workspace are isolated by their derived `repo:` labels.
215
345
 
216
346
  ### Submit a workflow
217
347
 
@@ -223,10 +353,7 @@ Workflows live at `~/.relay-flow/workflows/<name>.yaml` after submit. Replacemen
223
353
 
224
354
  Use [`examples/config-reference.yaml`](examples/config-reference.yaml) for the complete machine configuration, [`examples/workflow-reference.yaml`](examples/workflow-reference.yaml) for the complete workflow schema, or the provider-specific minimal workflows [`examples/minimal-jira-task-workflow.yaml`](examples/minimal-jira-task-workflow.yaml) and [`examples/minimal-beads-task-workflow.yaml`](examples/minimal-beads-task-workflow.yaml). Runtime node agents should follow [`docs/agent-instructions.md`](docs/agent-instructions.md). The existing [`examples/default-story-workflow.yaml`](examples/default-story-workflow.yaml) remains a more detailed Jira Story example, while [`examples/beads-workflow.yaml`](examples/beads-workflow.yaml) shows the Beads lifecycle shape. Replace the repo name and uncomment only the optional fields you need.
225
355
 
226
- Task, runner, and harness plugins are selected machine-wide. A single relay-flow
227
- configuration cannot run Jira and Beads simultaneously; use separate
228
- `RELAY_FLOW_HOME` directories or machine configurations when both providers are
229
- needed.
356
+ Task, runner, harness, and durable executor plugins are selected machine-wide. A single relay-flow configuration cannot run Jira and Beads simultaneously; use separate `RELAY_FLOW_HOME` directories or machine configurations when both providers are needed.
230
357
 
231
358
  ### Run
232
359
 
@@ -256,6 +383,8 @@ cleanupRunnerOnEnd: false # optional; when true the runner tears down at
256
383
  taskConfig: # optional; adapter-owned; merged root → repo → workflow → node
257
384
  filters:
258
385
  parentStatuses: [To Do]
386
+ labels: ["workflow:true"]
387
+ assignees: ["currentUser()"] # Jira resolves this to the authenticated email.
259
388
 
260
389
  nodes:
261
390
  start:
@@ -265,20 +394,43 @@ nodes:
265
394
  onSuccess: [{ target: coding }]
266
395
 
267
396
  coding:
268
- type: agent # or hitl
269
- agent: build # opencode agent
397
+ type: agent
398
+ agent: build
270
399
  description: | # becomes the mailbox description and launch prompt
271
- Implement the ticket.
400
+ Implement the ticket in the current worktree.
401
+ nudgePrompt: |
402
+ Continue working on {{ticket}}. Read the parent {{taskSystem}} ticket
403
+ {{ticket}} and your assigned ticket {{mailbox}} to understand the requirements. Read
404
+ the latest feedback, address the requested changes, and work on the next
405
+ bounded task slice. Return the complete report. Valid choices are:
406
+ {{nextSteps}}.
272
407
  onSuccess: [{ target: reviewing, when: "work complete" }]
273
408
  onFailure: [{ target: coding, when: "retry" }]
274
- nudgePrompt: "Check edge cases for {{ticket}} before reporting." # optional custom instructions
275
409
 
276
410
  reviewing:
411
+ type: agent
412
+ agent: plan
413
+ description: Review the completed implementation and report required changes.
414
+ nudgePrompt: |
415
+ Review {{ticket}}. Read the parent {{taskSystem}} ticket {{ticket}} and
416
+ your assigned ticket {{mailbox}} to understand the requirements. Re-check the
417
+ implementation and latest coding feedback, then return the complete
418
+ report. Valid choices are:
419
+ {{nextSteps}}.
420
+ onSuccess: [{ target: humanReview, when: "ready for human review" }]
421
+ onFailure: [{ target: coding, when: "changes required" }]
422
+
423
+ humanReview:
277
424
  type: hitl
278
- agent: build
279
- description: Human review.
280
- onSuccess: [{ target: end }]
281
- onFailure: [{ target: coding }]
425
+ agent: plan
426
+ description: Approve the reviewed implementation or request changes.
427
+ nudgePrompt: |
428
+ Review the completed work for {{ticket}} with the human. Read the parent
429
+ {{taskSystem}} ticket {{ticket}} and your assigned ticket {{mailbox}} to
430
+ understand the requirements. Return the complete report. Valid choices are:
431
+ {{nextSteps}}.
432
+ onSuccess: [{ target: end, when: "approved" }]
433
+ onFailure: [{ target: coding, when: "changes requested" }]
282
434
 
283
435
  end: {}
284
436
  ```
@@ -462,7 +614,7 @@ tickets are not reopened automatically.
462
614
  ## Architecture
463
615
 
464
616
  - **Task system** owns parent tickets, mailbox subtasks, task state, labels, comments, and adapter config. The parent ticket is the unit of work.
465
- - **Durable workflow engine** (go-workflows + SQLite) owns graph progression, waits, reports, retries, and recovery. No custom state machine.
617
+ - **Durable workflow engine** (`goworkflows` + SQLite or `temporal`) owns graph progression, waits, reports, retries, and recovery. No custom state machine.
466
618
  - **Mailbox subtask** is one agent/HITL node's scratch space; its description defines the node's work and its comments hold the node's summary plus selected incoming feedback.
467
619
  - **Harness** owns agent launch, session/report behavior, parsing, nudging, and resume semantics.
468
620
  - **Runner** owns ticket worktrees/environments, terminals, liveness, and execution of harness commands.
@@ -361,9 +361,9 @@ issue_json() {
361
361
  case "$issue" in
362
362
  demo-parent)
363
363
  if [ -f "$BD_STATE/claimed" ]; then
364
- labels='["wf:beadsComposition"]'
364
+ labels='["repo:payments","wf:beadsComposition"]'
365
365
  else
366
- labels='[]'
366
+ labels='["repo:payments"]'
367
367
  fi
368
368
  printf '[{"id":"demo-parent","title":"Composition parent","status":"%s","issue_type":"epic","priority":1,"labels":%s}]\n' "$status" "$labels"
369
369
  ;;
@@ -385,7 +385,7 @@ if [ "$#" -eq 6 ] && [ "$1" = list ] && [ "$2" = --ready ] && [ "$3" = --no-pare
385
385
  if [ -f "$BD_STATE/claimed" ]; then
386
386
  printf '[]\n'
387
387
  else
388
- printf '[{"id":"demo-parent","title":"Composition parent","status":"open","issue_type":"epic","priority":1,"labels":[]}]\n'
388
+ printf '[{"id":"demo-parent","title":"Composition parent","status":"open","issue_type":"epic","priority":1,"labels":["repo:payments"]}]\n'
389
389
  fi
390
390
  exit 0
391
391
  fi
@@ -4,6 +4,9 @@
4
4
  # relay-flow repo register --name payments --path /work/payments \
5
5
  # --set beadsDir=/work/payments/.beads
6
6
  #
7
+ # Relay-flow derives the reserved repo:payments label from the registered
8
+ # repository name. Parents without exactly one matching repo: label are
9
+ # ignored before workflow filters; wf:<workflow> remains a separate claim.
7
10
  # The Beads prefix is optional and is not a workspace/component selector.
8
11
  name: beadsStoryFlow
9
12
  repos:
@@ -118,7 +118,11 @@ repos:
118
118
  #
119
119
  # Beads uses the shared taskConfig fields (filters, assignee, transitionTo,
120
120
  # templates) but does not accept Jira-only project/component fields. beadsDir
121
- # is required at repo scope and must not be supplied only at root scope.
121
+ # is required at repo scope and must not be supplied only at root scope. The
122
+ # registered code path is the bd working directory; beadsDir selects the
123
+ # workspace/database. Shared workspaces isolate parents with the derived
124
+ # reserved repo:<lowercase-repo-name> label (the registered name may be at
125
+ # most 250 ASCII characters), not with workflow labels.
122
126
  #
123
127
  # Pi variant:
124
128
  # harnessPlugin: pi
@@ -11,10 +11,11 @@ taskConfig:
11
11
  - To Do
12
12
  issueTypes:
13
13
  - Story
14
- # labels:
15
- # - coding
16
- # assignees:
17
- # - owner@example.com
14
+ labels:
15
+ - "workflow:true"
16
+ assignees:
17
+ - currentUser()
18
+ # Jira resolves currentUser() to the authenticated Jira email.
18
19
  # assignee: default-node-owner@example.com
19
20
  # project: PAY
20
21
  # component: api
@@ -28,48 +29,62 @@ nodes:
28
29
  transitionTo:
29
30
  parentStatus: In Progress
30
31
  onSuccess:
31
- - target: implement
32
+ - target: coding
32
33
  when: The parent story is ready for implementation
33
34
 
34
- implement:
35
+ coding:
35
36
  type: agent
36
37
  agent: build
37
- description: Implement the parent story and verify the changes.
38
- # nudgePrompt: "Finish {{node}} for {{ticket}} and choose one of: {{nextSteps}}"
38
+ description: Implement the parent story in the current ticket worktree.
39
+ nudgePrompt: |
40
+ Continue working on {{ticket}}. Read the parent {{taskSystem}} ticket
41
+ {{ticket}} and your assigned ticket {{mailbox}} to understand the requirements. Read
42
+ the latest feedback, address the requested changes, and work on the next
43
+ bounded task slice. Return the complete report. Valid choices are:
44
+ {{nextSteps}}.
39
45
  taskConfig:
40
46
  # assignee: developer@example.com
41
47
  transitionTo:
42
48
  taskStatus: In Progress
43
49
  # parentStatus: In Progress
44
50
  onSuccess:
45
- - target: review
51
+ - target: reviewing
46
52
  when: Implementation and verification are complete
47
53
  onFailure:
48
- - target: implement
54
+ - target: coding
49
55
  when: Implementation needs another pass
50
56
 
51
- review:
57
+ reviewing:
52
58
  type: agent
53
59
  agent: plan
54
- description: Review the implementation for correctness, regressions, and missing tests.
55
- # nudgePrompt: "Complete the review for {{ticket}}. Valid routes: {{nextSteps}}"
60
+ description: Review the completed implementation and report required changes.
61
+ nudgePrompt: |
62
+ Review {{ticket}}. Read the parent {{taskSystem}} ticket {{ticket}} and
63
+ your assigned ticket {{mailbox}} to understand the requirements. Re-check the
64
+ implementation and latest coding feedback, then return the complete
65
+ report. Valid choices are:
66
+ {{nextSteps}}.
56
67
  taskConfig:
57
68
  # assignee: reviewer@example.com
58
69
  transitionTo:
59
70
  taskStatus: In Progress
60
71
  # parentStatus: In Review
61
72
  onSuccess:
62
- - target: prReview
73
+ - target: humanReview
63
74
  when: The changes are ready for human approval
64
75
  onFailure:
65
- - target: implement
76
+ - target: coding
66
77
  when: Code changes are required
67
78
 
68
- prReview:
79
+ humanReview:
69
80
  type: hitl
70
81
  agent: plan
71
- description: Discuss the pull request with the human and obtain approval.
72
- # nudgePrompt: "Review {{ticket}} with the human. Valid routes: {{nextSteps}}"
82
+ description: Approve the reviewed implementation or request changes.
83
+ nudgePrompt: |
84
+ Review the completed work for {{ticket}} with the human. Read the parent
85
+ {{taskSystem}} ticket {{ticket}} and your assigned ticket {{mailbox}} to
86
+ understand the requirements. Return the complete report. Valid choices are:
87
+ {{nextSteps}}.
73
88
  taskConfig:
74
89
  # assignee: human-reviewer@example.com
75
90
  transitionTo:
@@ -79,7 +94,7 @@ nodes:
79
94
  - target: end
80
95
  when: The human approves the review
81
96
  onFailure:
82
- - target: implement
97
+ - target: coding
83
98
  when: The human requests code changes
84
99
 
85
100
  end:
@@ -44,6 +44,21 @@ func (a *Activities) runSpec(w run.Work) runner.RunSpec {
44
44
  }
45
45
  }
46
46
 
47
+ // agentEnv returns the repo task system's agent workspace environment, or nil
48
+ // when the adapter exposes none. It is resolved at launch time so no workspace
49
+ // value is carried in durable workflow history.
50
+ func (a *Activities) agentEnv(repoName string) (map[string]string, error) {
51
+ sys, err := a.taskSystem(repoName)
52
+ if err != nil {
53
+ return nil, err
54
+ }
55
+ provider, ok := sys.(task.AgentEnvironment)
56
+ if !ok {
57
+ return nil, nil
58
+ }
59
+ return provider.AgentEnv(), nil
60
+ }
61
+
47
62
  // EnsureMailboxes ensures one mailbox per work node and returns the
48
63
  // node-to-mailbox map.
49
64
  func (a *Activities) EnsureMailboxes(ctx context.Context, w run.Work, specs []task.MailboxSpec) (map[string]task.Mailbox, error) {
@@ -220,6 +235,10 @@ func (a *Activities) EnsureNodeRuntime(ctx context.Context, nw run.NodeWork, rep
220
235
  if err != nil {
221
236
  return err
222
237
  }
238
+ spec.TaskEnv, err = a.agentEnv(nw.Repo)
239
+ if err != nil {
240
+ return err
241
+ }
223
242
  cmd, err := a.Harness.BuildCommand(spec)
224
243
  if err != nil {
225
244
  return err
@@ -71,7 +71,7 @@ func TestMailboxDescriptionAndLaunchPromptAreTaskSystemNeutral(t *testing.T) {
71
71
  if err != nil {
72
72
  t.Fatal(err)
73
73
  }
74
- want := "Task system: " + taskSystem + "\nUse the " + taskSystem + " tools to read the parent ticket PAY-101.\n\nYour mailbox is PAY-234. Read its description and comments for node instructions and feedback.\n\nReturn the complete report directly. Relay-flow will show a native TUI approval dialog after the report is valid. Do not use OpenCode's Question tool for relay-flow approval."
74
+ want := "Task system: " + taskSystem + "\nUse the " + taskSystem + " tools to read the parent ticket PAY-101.\n\nYour mailbox is PAY-234. Read its description and comments for node instructions and feedback.\n\nKeep the summary brief, and make the feedback as detailed and actionable as possible for the next agent.\n\nReturn the complete report directly. Relay-flow will show a native TUI approval dialog after the report is valid. Do not use OpenCode's Question tool for relay-flow approval."
75
75
  if prompt != want {
76
76
  t.Fatalf("RenderPrompt(%s) = %q, want %q", taskSystem, prompt, want)
77
77
  }