relay-flow 0.2.3-alpha → 0.2.4-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.
Files changed (55) hide show
  1. package/README.md +116 -10
  2. package/cmd/relay-flow/main.go +2 -0
  3. package/cmd/relay-flow/pi_wiring_test.go +64 -0
  4. package/cmd/relay-flow/serve.go +2 -0
  5. package/examples/beads-workflow.yaml +3 -3
  6. package/examples/config-reference.yaml +144 -0
  7. package/examples/minimal-beads-task-workflow.yaml +34 -0
  8. package/examples/minimal-jira-task-workflow.yaml +68 -0
  9. package/examples/workflow-reference.yaml +111 -0
  10. package/internal/harness/opencode/opencode_test.go +1 -1
  11. package/internal/harness/opencode/repo_setup.go +1 -1
  12. package/internal/harness/pi/config_test.go +46 -0
  13. package/internal/harness/pi/lifecycle_test.go +69 -0
  14. package/internal/harness/pi/pi.go +261 -0
  15. package/internal/harness/pi/pi_test.go +322 -0
  16. package/internal/harness/pi/prompt_test.go +121 -0
  17. package/internal/harness/pi/testdata/pi-0.84.1/capture.json +126 -0
  18. package/internal/harness/pi/testdata/pi-0.84.1/noninteractive-output.txt +11 -0
  19. package/internal/harness/pi/testdata/pi-0.84.1/tui-output-sanitized.txt +17 -0
  20. package/internal/harness/pi/validation_test.go +168 -0
  21. package/internal/runner/herdr/baseref.go +60 -0
  22. package/internal/runner/herdr/herdr.go +578 -0
  23. package/internal/runner/herdr/herdr_test.go +688 -0
  24. package/internal/runner/herdr/herdrcli/contract.go +128 -0
  25. package/internal/runner/herdr/herdrcli/exec.go +68 -0
  26. package/internal/runner/herdr/herdrcli/herdrcli_test.go +274 -0
  27. package/internal/runner/herdr/herdrcli/live_test.go +132 -0
  28. package/internal/runner/herdr/herdrcli/operations.go +281 -0
  29. package/internal/runner/herdr/herdrcli/response.go +116 -0
  30. package/internal/runner/herdr/herdrcli/testdata/empty-panes.json +1 -0
  31. package/internal/runner/herdr/herdrcli/testdata/empty-tabs.json +1 -0
  32. package/internal/runner/herdr/herdrcli/testdata/error-not-git-worktree.json +1 -0
  33. package/internal/runner/herdr/herdrcli/testdata/error-pane-not-found.json +1 -0
  34. package/internal/runner/herdr/herdrcli/testdata/error-workspace-not-found.json +1 -0
  35. package/internal/runner/herdr/herdrcli/testdata/error-worktree-not-found.json +1 -0
  36. package/internal/runner/herdr/herdrcli/testdata/malformed.json +1 -0
  37. package/internal/runner/herdr/herdrcli/testdata/pane-close.json +6 -0
  38. package/internal/runner/herdr/herdrcli/testdata/pane-get.json +25 -0
  39. package/internal/runner/herdr/herdrcli/testdata/pane-list.json +45 -0
  40. package/internal/runner/herdr/herdrcli/testdata/pane-process-info-shell.json +22 -0
  41. package/internal/runner/herdr/herdrcli/testdata/pane-process-info.json +23 -0
  42. package/internal/runner/herdr/herdrcli/testdata/pane-rename.json +23 -0
  43. package/internal/runner/herdr/herdrcli/testdata/snapshot.json +213 -0
  44. package/internal/runner/herdr/herdrcli/testdata/strict-herdr.sh +175 -0
  45. package/internal/runner/herdr/herdrcli/testdata/tab-create.json +31 -0
  46. package/internal/runner/herdr/herdrcli/testdata/tab-list.json +26 -0
  47. package/internal/runner/herdr/herdrcli/testdata/workspace-close.json +6 -0
  48. package/internal/runner/herdr/herdrcli/testdata/worktree-create.json +58 -0
  49. package/internal/runner/herdr/herdrcli/testdata/worktree-list.json +35 -0
  50. package/internal/runner/herdr/herdrcli/testdata/worktree-open.json +59 -0
  51. package/internal/task/beads/beads.go +0 -16
  52. package/internal/task/beads/config_compatibility_test.go +7 -8
  53. package/internal/task/jira/filters_test.go +32 -6
  54. package/internal/task/jira/jira.go +27 -17
  55. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
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 built-in) 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 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.
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
 
@@ -51,6 +51,26 @@ assistant report it shows a native Approve/Reject dialog. Approval delivers
51
51
  delivers nothing. `reportId` comes from the harness session/message identity;
52
52
  `nodeVisitID` is internal and is never part of either plugin payload.
53
53
 
54
+ Pi plugin: install the same published package manually in Pi's global package
55
+ settings before starting a Pi harness session:
56
+
57
+ ```sh
58
+ pi install npm:relay-flow-plugin@<version>
59
+ ```
60
+
61
+ Relay-flow does not install or configure the package automatically. Pi resolves
62
+ `pi.ts` from the package's `pi.extensions` manifest entry, so do not add
63
+ `-e`/`--extension` to the relay-flow launch command. The runner launches Pi in
64
+ an interactive PTY; the package's OpenCode entry point remains available for
65
+ OpenCode sessions.
66
+
67
+ The plugin is the report-path half of the harness contract: it registers each
68
+ emitted harness session with `{runId, node, sessionId}`, parses the agent's
69
+ structured report, applies the agent/HITL nudge policy, and delivers
70
+ `{runId, node, reportId, report}` via `relay-flow report` with retry.
71
+ `reportId` comes from the harness session/message identity; `nodeVisitID` is
72
+ internal and is never part of either plugin payload.
73
+
54
74
  ### One-time machine setup
55
75
 
56
76
  ```sh
@@ -68,6 +88,13 @@ relay-flow init --task-plugin beads --runner-plugin orca --harness-plugin openco
68
88
 
69
89
  Beads authentication is owned by the Beads workspace and its `bd`/Dolt configuration. `relay-flow task auth` is a no-op for Beads and does not create relay-flow credentials; do not add a Jira token to a Beads-only installation.
70
90
 
91
+ Pi is available through the existing harness selection. For a non-interactive
92
+ setup, select it with the existing flags:
93
+
94
+ ```sh
95
+ relay-flow init --task-plugin jira --runner-plugin orca --harness-plugin pi
96
+ ```
97
+
71
98
  The full machine layout is fixed under `~/.relay-flow` (0700):
72
99
 
73
100
  ```
@@ -174,10 +201,14 @@ Beads workflow filters are structured and evaluated in relay-flow. For example,
174
201
  taskConfig:
175
202
  filters:
176
203
  parentStatuses: [open]
177
- issueTypes: [epic]
204
+ issueTypes: [task]
178
205
  labels: [relay-ready]
179
206
  ```
180
207
 
208
+ Jira and Beads use the same conceptual `Task` issue type, but each adapter
209
+ keeps its provider-native spelling: Jira uses `Task` and Beads uses `task`.
210
+ Filter values are exact and are not translated between providers.
211
+
181
212
  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.
182
213
 
183
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`.
@@ -190,7 +221,12 @@ relay-flow workflow submit --file <path>
190
221
 
191
222
  Workflows live at `~/.relay-flow/workflows/<name>.yaml` after submit. Replacement and removal are rejected while any run of that workflow is active.
192
223
 
193
- Use [`examples/default-story-workflow.yaml`](examples/default-story-workflow.yaml) as a fully annotated Jira-oriented starting point, or [`examples/beads-workflow.yaml`](examples/beads-workflow.yaml) for the Beads filter and lifecycle shape. Replace the repo name and uncomment only the optional fields you need.
224
+ 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
+
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.
194
230
 
195
231
  ### Run
196
232
 
@@ -284,19 +320,89 @@ A parent moved to `in_progress` stays visible to the claimed-parent poll, which
284
320
 
285
321
  Beads and Jira use the shared `filters`, `templates`, optional top-level
286
322
  `assignee`, and `transitionTo` field names. `transitionTo` uses
287
- `parentStatus` for the parent issue and `taskStatus` for a mailbox. In both
288
- adapters `assignee` is the default assignee filter when `filters.assignees` is
289
- absent, and an `assignee` in effect for a node also assigns that node's
290
- mailbox. Beads requires the repo-only `taskConfig.beadsDir` shown above; Jira
291
- instead uses its repo `project` and `component` keys. `project` and
292
- `component` are not Beads fields, and a Beads issue prefix is not a component
293
- or workspace selector. Beads status values remain native (`open`,
323
+ `parentStatus` for the parent issue and `taskStatus` for a mailbox. Both
324
+ adapters support the structured `filters.assignees` list. Jira values are
325
+ normalized account emails; Beads values are the provider's assignee strings.
326
+ Jira additionally supports the reserved `currentUser()` value inside
327
+ `filters.assignees`; relay-flow resolves it to the authenticated Jira email
328
+ without sending it as JQL. On first Jira authentication, when no root
329
+ `taskConfig.assignee` is configured, relay-flow stores the authenticated email
330
+ as the root mailbox assignee. In both adapters `assignee` applies to a node's
331
+ mailbox assignment; only `filters.assignees` controls parent-ticket pickup.
332
+ When `filters.assignees` is absent, there is no assignee filter. To make that
333
+ explicit while keeping mailbox assignment, set an empty list:
334
+
335
+ ```yaml
336
+ taskConfig:
337
+ filters:
338
+ assignees: []
339
+ ```
340
+
341
+ This still applies the other filters, such as project, status, issue type, and
342
+ labels. If mailbox assignment should also be disabled for a workflow, override
343
+ `assignee` with an empty string at that scope as well. Beads requires the repo-only
344
+ `taskConfig.beadsDir` shown above; Jira instead uses its repo `project` and
345
+ `component` keys. `project` and `component` are not Beads fields, and a Beads
346
+ issue prefix is not a component or workspace selector. Beads status values remain native (`open`,
294
347
  `in_progress`, `blocked`, `deferred`, `hooked`, `closed`), while Jira values
295
348
  remain native (`In Progress`, `Done`, and so on); relay-flow does not
296
349
  translate arbitrary values between providers. Beads rejects workflow/node-level
297
350
  template overrides because the fixed task text rendering contract has no
298
351
  lower-scope input.
299
352
 
353
+ ### Pi harness
354
+
355
+ Pi has one built-in coding agent. Relay-flow keeps Pi's configured model,
356
+ provider, tools, extensions, and settings, while allowing a workflow node to
357
+ select a repository-owned role prompt. `agent: default` uses Pi's built-in
358
+ coding agent without an additional role prompt. A non-default value such as
359
+ `coder` or `reviewer` is resolved to a readable, non-empty
360
+ `.pi/roles/<agent>.md` file in the registered repository and passed to Pi with
361
+ `--append-system-prompt`.
362
+
363
+ Pi has no native `pi agent list` command. Role existence is therefore verified
364
+ by the filesystem check above; missing, empty, non-regular, or unsafe role
365
+ paths fail workflow preflight. The workflow agent value is never treated as a
366
+ model ID and relay-flow never passes an OpenCode-style `--agent` option.
367
+
368
+ For example, a Pi workflow with repository roles uses:
369
+
370
+ ```yaml
371
+ type: agent
372
+ agent: coder
373
+ ```
374
+
375
+ with:
376
+
377
+ ```text
378
+ .pi/roles/coder.md
379
+ .pi/roles/reviewer.md
380
+ ```
381
+
382
+ Pi node launches use the installed Pi 0.84.1 interactive command contract:
383
+
384
+ ```text
385
+ pi --name <ticket>:<node> [--append-system-prompt <role-file>] [--session-id <persisted-session-id>] <prompt>
386
+ ```
387
+
388
+ The prompt is one positional argument. Pi 0.84.1 rejects a bare `--`, so the
389
+ launch command does not include one. A persisted session uses
390
+ `--session-id`; print mode, JSON/RPC mode, and extension-install flags are not
391
+ used. The runner supplies a PTY for both standard streams, and the Pi process
392
+ remains available for interactive input after a response settles.
393
+
394
+ For a `hitl` node, a valid report is approved directly in Pi's host UI with
395
+ `ctx.ui.select`:
396
+
397
+ ```text
398
+ Approve relay-flow report for <ticket>:<node>
399
+ Approve
400
+ Reject
401
+ ```
402
+
403
+ Approve delivers the report. Reject or Escape submits nothing and leaves the
404
+ durable run waiting; Pi does not require an LLM Question tool for this step.
405
+
300
406
  ---
301
407
 
302
408
  ## Structured node report
@@ -37,6 +37,8 @@ import (
37
37
  // Adapter registrations (factories registered via init for plugin
38
38
  // name validation at init-time).
39
39
  _ "github.com/rajpopat27/relay-flow/internal/harness/opencode"
40
+ _ "github.com/rajpopat27/relay-flow/internal/harness/pi"
41
+ _ "github.com/rajpopat27/relay-flow/internal/runner/herdr"
40
42
  _ "github.com/rajpopat27/relay-flow/internal/runner/orca"
41
43
  _ "github.com/rajpopat27/relay-flow/internal/task/beads"
42
44
  _ "github.com/rajpopat27/relay-flow/internal/task/jira"
@@ -0,0 +1,64 @@
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "path/filepath"
6
+ "strings"
7
+ "testing"
8
+
9
+ "github.com/rajpopat27/relay-flow/internal/config"
10
+ "github.com/rajpopat27/relay-flow/internal/harness"
11
+ )
12
+
13
+ func TestPiAppearsInDynamicHarnessSelection(t *testing.T) {
14
+ names := harness.Names()
15
+ if !containsHarnessName(names, "pi") {
16
+ t.Fatalf("harness.Names() = %v, want pi", names)
17
+ }
18
+
19
+ var selected string
20
+ field, err := pluginSelectField("Select harness", names, &selected)
21
+ if err != nil {
22
+ t.Fatalf("pluginSelectField: %v", err)
23
+ }
24
+ var output bytes.Buffer
25
+ if err := field.RunAccessible(&output, strings.NewReader("1\n")); err != nil {
26
+ t.Fatalf("RunAccessible: %v", err)
27
+ }
28
+ if !strings.Contains(output.String(), "Select harness") {
29
+ t.Fatalf("selection output %q missing unchanged harness title", output.String())
30
+ }
31
+ }
32
+
33
+ func TestInitPiFlagUsesGenericHarnessSelectionPath(t *testing.T) {
34
+ home := t.TempDir()
35
+ code, output := captureStdout(t, func() int {
36
+ return cli(t, home, "", "init",
37
+ "--task-plugin", "jira",
38
+ "--runner-plugin", "orca",
39
+ "--harness-plugin", "pi")
40
+ })
41
+ if code != 0 {
42
+ t.Fatalf("Pi init exit = %d, want 0", code)
43
+ }
44
+ if !strings.Contains(output, "Harness: pi") {
45
+ t.Fatalf("Pi init output = %q, want generic harness summary", output)
46
+ }
47
+
48
+ cfg, err := config.LoadMachine(filepath.Join(home, ".relay-flow", "config.yaml"))
49
+ if err != nil {
50
+ t.Fatalf("load Pi machine config: %v", err)
51
+ }
52
+ if cfg.HarnessPlugin != "pi" {
53
+ t.Fatalf("harnessPlugin = %q, want pi", cfg.HarnessPlugin)
54
+ }
55
+ }
56
+
57
+ func containsHarnessName(names []string, want string) bool {
58
+ for _, name := range names {
59
+ if name == want {
60
+ return true
61
+ }
62
+ }
63
+ return false
64
+ }
@@ -34,6 +34,8 @@ import (
34
34
 
35
35
  // Adapter registrations (factories are registered by name via init).
36
36
  _ "github.com/rajpopat27/relay-flow/internal/harness/opencode"
37
+ _ "github.com/rajpopat27/relay-flow/internal/harness/pi"
38
+ _ "github.com/rajpopat27/relay-flow/internal/runner/herdr"
37
39
  _ "github.com/rajpopat27/relay-flow/internal/runner/orca"
38
40
  _ "github.com/rajpopat27/relay-flow/internal/task/beads"
39
41
  _ "github.com/rajpopat27/relay-flow/internal/task/jira"
@@ -16,13 +16,13 @@ taskConfig:
16
16
  parentStatuses:
17
17
  - open
18
18
  issueTypes:
19
- - epic
19
+ - task
20
20
  labels:
21
21
  - relay-ready
22
22
  # assignees:
23
23
  # - owner@example.com
24
- # Optional shared default; filters.assignees overrides it when provided.
25
- # An assignee in effect for a node also assigns that node's mailbox.
24
+ # Optional mailbox assignee inherited by nodes. It does not filter parent
25
+ # tickets; configure filters.assignees explicitly to filter pickup.
26
26
  # assignee: owner@example.com
27
27
 
28
28
  # transitionTo describes one lifecycle point, so configure it on a node.
@@ -0,0 +1,144 @@
1
+ # Complete relay-flow machine configuration reference.
2
+ #
3
+ # This example uses Jira + Orca + OpenCode. Replace the example values before
4
+ # use. The live machine file is ~/.relay-flow/config.yaml.
5
+ #
6
+ # One task plugin, runner plugin, and harness plugin are selected for the whole
7
+ # machine. Jira and Beads therefore use separate relay-flow configurations.
8
+
9
+ pollIntervalSeconds: 15
10
+ completedRunRetentionDays: 30
11
+
12
+ # These default to true when omitted. keepTerminalsAlive: true requires
13
+ # keepSessionsAlive: true.
14
+ keepTerminalsAlive: true
15
+ keepSessionsAlive: true
16
+
17
+ taskPlugin: jira
18
+
19
+ taskConfig:
20
+ # Root mailbox assignee. This does not filter parent tickets; use
21
+ # filters.assignees for ticket pickup. Replace it with a real Jira account
22
+ # email or omit it.
23
+ assignee: relay-bot@example.com
24
+
25
+ # Task-system filters are adapter-owned. These values are Jira-native.
26
+ filters:
27
+ parentStatuses:
28
+ - To Do
29
+ issueTypes:
30
+ - Task
31
+ labels:
32
+ - relay-ready
33
+ assignees:
34
+ - currentUser()
35
+ # Jira resolves currentUser() to the authenticated Jira email and does
36
+ # not send it as JQL. Ticket pickup is controlled only by this list;
37
+ # the top-level assignee controls mailbox assignment.
38
+
39
+ # Avoid setting transitionTo at root scope unless the same values should
40
+ # apply to every lifecycle point, including end. Prefer node-level values.
41
+ # transitionTo:
42
+ # parentStatus: In Progress
43
+ # taskStatus: In Progress
44
+
45
+ # Task-system text templates. The fixed report contract is appended by
46
+ # relay-flow and is not changed by these templates.
47
+ templates:
48
+ mailboxDescription: |-
49
+ Parent ticket: {{ticket}}
50
+ Workflow: {{workflow}}
51
+ Node: {{node}}
52
+ Node type: {{nodeType}}
53
+ Agent: {{agent}}
54
+ Mailbox: {{mailbox}}
55
+
56
+ Node work:
57
+ {{nodeDescription}}
58
+
59
+ Read this mailbox's comments for feedback from previous nodes.
60
+
61
+ summaryComment: |-
62
+ Summary for {{node}}
63
+
64
+ {{summaryReport}}
65
+
66
+ feedbackComment: |-
67
+ Feedback from {{sourceNode}} to {{targetNode}} mailbox {{mailbox}}
68
+
69
+ {{feedbackReport}}
70
+
71
+ runnerPlugin: orca
72
+
73
+ runnerConfig:
74
+ # Optional Orca base branch. When omitted, Orca derives it from the repo's
75
+ # primary worktree.
76
+ baseRef: main
77
+
78
+ harnessPlugin: opencode
79
+
80
+ harnessConfig:
81
+ # OpenCode supports initial, feedback, and hitl templates.
82
+ initial: |-
83
+ Task system: {{taskSystem}}
84
+ Use the {{taskSystem}} tools to read the parent ticket {{ticket}}.
85
+
86
+ Your mailbox is {{mailbox}}. Read its description and comments for node instructions and feedback.
87
+
88
+ feedback: |-
89
+ New feedback was added to the comments section of your mailbox subtask {{mailbox}}. Read it.
90
+
91
+ hitl: |-
92
+ Return 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.
93
+
94
+ repos:
95
+ payments:
96
+ # Code/runner repository. It must already be registered in Orca.
97
+ path: /work/payments
98
+
99
+ taskConfig:
100
+ # Jira repo-scoped required values. component is derived from the
101
+ # relay-flow repo name during repo registration.
102
+ project: PAY
103
+ component: payments
104
+
105
+ # Optional repo-level overrides inherit into workflows and nodes.
106
+ # assignee: relay-bot@example.com
107
+ # filters:
108
+ # labels:
109
+ # - backend
110
+
111
+ # Beads variant:
112
+ # taskPlugin: beads
113
+ # repos:
114
+ # payments:
115
+ # path: /work/payments
116
+ # taskConfig:
117
+ # beadsDir: /work/payments/.beads
118
+ #
119
+ # Beads uses the shared taskConfig fields (filters, assignee, transitionTo,
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.
122
+ #
123
+ # Pi variant:
124
+ # harnessPlugin: pi
125
+ # harnessConfig:
126
+ # initial: |
127
+ # Task system: {{taskSystem}}
128
+ # Use the {{taskSystem}} tools to read the parent ticket {{ticket}}.
129
+ #
130
+ # Your mailbox is {{mailbox}}. Read its description and comments for node instructions and feedback.
131
+ # feedback: |
132
+ # New feedback was added to the comments section of your mailbox subtask {{mailbox}}. Read it.
133
+ #
134
+ # Pi accepts only initial and feedback harness templates. Pi workflow nodes
135
+ # may use agent: default or a repository role in .pi/roles/<agent>.md; HITL
136
+ # approval is provided by Pi's host UI.
137
+ #
138
+ # Herdr variant:
139
+ # runnerPlugin: herdr
140
+ # runnerConfig:
141
+ # session: relay-flow
142
+ # socketPath: /run/user/1000/herdr.sock
143
+ #
144
+ # Do not combine Orca-only baseRef with Herdr-only configuration.
@@ -0,0 +1,34 @@
1
+ # Smallest Beads Task workflow.
2
+ # Register the code repository with a repo-scoped beadsDir before submitting.
3
+ # For Pi, use default or a repository role from .pi/roles/<agent>.md.
4
+ name: minimalBeadsTaskFlow
5
+
6
+ repos:
7
+ - payments
8
+
9
+ cleanupRunnerOnEnd: true
10
+
11
+ taskConfig:
12
+ filters:
13
+ parentStatuses:
14
+ - open
15
+ issueTypes:
16
+ - task
17
+ labels:
18
+ - relay-ready
19
+
20
+ nodes:
21
+ start:
22
+ onSuccess:
23
+ - target: implement
24
+
25
+ implement:
26
+ type: agent
27
+ agent: build
28
+ description: Implement the Beads Task and verify the changes.
29
+ onSuccess:
30
+ - target: end
31
+ onFailure:
32
+ - target: implement
33
+
34
+ end: {}
@@ -0,0 +1,68 @@
1
+ # Smallest Jira Task workflow with implementation, agent review, and HITL PR review.
2
+ # Register the repo with Jira project/component values before submitting.
3
+ # For Pi, replace build and plan with default.
4
+ name: minimalJiraTaskFlow
5
+
6
+ repos:
7
+ - payments
8
+
9
+ cleanupRunnerOnEnd: true
10
+
11
+ taskConfig:
12
+ filters:
13
+ parentStatuses:
14
+ - To Do
15
+ issueTypes:
16
+ - Task
17
+ labels:
18
+ - relay-ready
19
+ assignees:
20
+ - currentUser()
21
+ # Jira resolves currentUser() to the authenticated Jira email.
22
+ # Replace this list with `assignees: []` to match all assignees.
23
+
24
+ nodes:
25
+ start:
26
+ onSuccess:
27
+ - target: implement
28
+
29
+ implement:
30
+ type: agent
31
+ agent: build
32
+ description: |
33
+ Implement the Jira Task in the ticket worktree.
34
+ Run the relevant tests and verification commands.
35
+ onSuccess:
36
+ - target: review
37
+ when: Implementation and verification are complete.
38
+ onFailure:
39
+ - target: implement
40
+ when: Implementation needs another pass.
41
+
42
+ review:
43
+ type: agent
44
+ agent: plan
45
+ description: |
46
+ Review the implementation for correctness, regressions, missing tests,
47
+ and unresolved issues. Prepare the work for human PR review.
48
+ onSuccess:
49
+ - target: prReview
50
+ when: The implementation is ready for human PR review.
51
+ onFailure:
52
+ - target: implement
53
+ when: The review found issues requiring implementation changes.
54
+
55
+ prReview:
56
+ type: hitl
57
+ agent: plan
58
+ description: |
59
+ Review the implementation and proposed changes with a human.
60
+ Approve the work or request implementation changes.
61
+ onSuccess:
62
+ - target: end
63
+ when: The human approves the PR review.
64
+ onFailure:
65
+ - target: implement
66
+ when: The human requests changes.
67
+
68
+ end: {}
@@ -0,0 +1,111 @@
1
+ # Complete workflow definition reference.
2
+ #
3
+ # This example uses Jira-native values and the OpenCode agent names build and
4
+ # plan. For Beads, use provider-native values: open, task, in_progress, and
5
+ # closed. For Pi, use default or a repository role from .pi/roles/<agent>.md.
6
+
7
+ name: referenceFlow
8
+
9
+ repos:
10
+ - payments
11
+
12
+ cleanupRunnerOnEnd: true
13
+
14
+ taskConfig:
15
+ # Optional assignee inherited by node mailboxes. It does not filter parent
16
+ # tickets; use filters.assignees for ticket pickup.
17
+ assignee: developer@example.com
18
+
19
+ filters:
20
+ parentStatuses:
21
+ - To Do
22
+ issueTypes:
23
+ - Task
24
+ labels:
25
+ - relay-ready
26
+ assignees:
27
+ - currentUser()
28
+ # Jira-only symbolic value: resolves to the authenticated Jira email.
29
+ # Other assignee values are normalized Jira emails, not JQL expressions.
30
+
31
+ # filters.assignees controls ticket pickup independently of assignee.
32
+ # Use an explicit empty list to include all assignees while retaining any
33
+ # inherited mailbox assignment:
34
+ # filters:
35
+ # assignees: []
36
+ # To disable mailbox assignment too, set assignee: "" at this scope.
37
+
38
+ # Prefer lifecycle-specific transitionTo values on start, work nodes, and
39
+ # end. A value here is inherited by every lifecycle point that reads it.
40
+ # transitionTo:
41
+ # parentStatus: In Progress
42
+ # taskStatus: In Progress
43
+
44
+ nodes:
45
+ start:
46
+ # start has no type, agent, description, nudgePrompt, or failure routes.
47
+ taskConfig:
48
+ transitionTo:
49
+ parentStatus: In Progress
50
+ onSuccess:
51
+ - target: implement
52
+ when: The parent ticket is ready for implementation.
53
+
54
+ implement:
55
+ type: agent
56
+ agent: build
57
+ description: |
58
+ Implement the parent ticket in the ticket worktree.
59
+ Run the relevant tests and verification commands.
60
+ nudgePrompt: |
61
+ Continue working on {{ticket}} at node {{node}}.
62
+ Read the latest mailbox feedback.
63
+ Valid next steps are: {{nextSteps}}.
64
+ Return the complete report contract.
65
+ taskConfig:
66
+ transitionTo:
67
+ taskStatus: In Progress
68
+ onSuccess:
69
+ - target: review
70
+ when: Implementation and verification are complete.
71
+ onFailure:
72
+ - target: implement
73
+ when: Implementation needs another pass.
74
+
75
+ review:
76
+ type: agent
77
+ agent: plan
78
+ description: |
79
+ Review the implementation for correctness, regressions, missing tests,
80
+ and unresolved issues. Prepare the work for human PR review.
81
+ taskConfig:
82
+ transitionTo:
83
+ taskStatus: In Progress
84
+ onSuccess:
85
+ - target: prReview
86
+ when: The implementation is ready for human PR review.
87
+ onFailure:
88
+ - target: implement
89
+ when: The review found issues requiring implementation changes.
90
+
91
+ prReview:
92
+ type: hitl
93
+ agent: plan
94
+ description: |
95
+ Review the implementation and proposed changes with a human.
96
+ Approve the work or request implementation changes.
97
+ taskConfig:
98
+ transitionTo:
99
+ taskStatus: In Progress
100
+ onSuccess:
101
+ - target: end
102
+ when: The human approves the PR review.
103
+ onFailure:
104
+ - target: implement
105
+ when: The human requests changes.
106
+
107
+ end:
108
+ # end has no type, agent, description, nudgePrompt, or routes.
109
+ taskConfig:
110
+ transitionTo:
111
+ parentStatus: Done
@@ -14,7 +14,7 @@ import (
14
14
  "github.com/rajpopat27/relay-flow/internal/workflow"
15
15
  )
16
16
 
17
- const configuredPlugin = "relay-flow-plugin@0.2.3-alpha"
17
+ const configuredPlugin = "relay-flow-plugin@0.2.4-alpha"
18
18
 
19
19
  func TestBuildCommandArgv(t *testing.T) {
20
20
  t.Setenv("RELAY_FLOW_HOME", "/var/lib/relay-flow-test")
@@ -10,7 +10,7 @@ import (
10
10
  "github.com/rajpopat27/relay-flow/internal/config"
11
11
  )
12
12
 
13
- const relayFlowPlugin = "relay-flow-plugin@0.2.3-alpha"
13
+ const relayFlowPlugin = "relay-flow-plugin@0.2.4-alpha"
14
14
 
15
15
  type jsoncToken struct {
16
16
  kind byte