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.
- package/README.md +116 -10
- package/cmd/relay-flow/main.go +2 -0
- package/cmd/relay-flow/pi_wiring_test.go +64 -0
- package/cmd/relay-flow/serve.go +2 -0
- package/examples/beads-workflow.yaml +3 -3
- package/examples/config-reference.yaml +144 -0
- package/examples/minimal-beads-task-workflow.yaml +34 -0
- package/examples/minimal-jira-task-workflow.yaml +68 -0
- package/examples/workflow-reference.yaml +111 -0
- package/internal/harness/opencode/opencode_test.go +1 -1
- package/internal/harness/opencode/repo_setup.go +1 -1
- package/internal/harness/pi/config_test.go +46 -0
- package/internal/harness/pi/lifecycle_test.go +69 -0
- package/internal/harness/pi/pi.go +261 -0
- package/internal/harness/pi/pi_test.go +322 -0
- package/internal/harness/pi/prompt_test.go +121 -0
- package/internal/harness/pi/testdata/pi-0.84.1/capture.json +126 -0
- package/internal/harness/pi/testdata/pi-0.84.1/noninteractive-output.txt +11 -0
- package/internal/harness/pi/testdata/pi-0.84.1/tui-output-sanitized.txt +17 -0
- package/internal/harness/pi/validation_test.go +168 -0
- package/internal/runner/herdr/baseref.go +60 -0
- package/internal/runner/herdr/herdr.go +578 -0
- package/internal/runner/herdr/herdr_test.go +688 -0
- package/internal/runner/herdr/herdrcli/contract.go +128 -0
- package/internal/runner/herdr/herdrcli/exec.go +68 -0
- package/internal/runner/herdr/herdrcli/herdrcli_test.go +274 -0
- package/internal/runner/herdr/herdrcli/live_test.go +132 -0
- package/internal/runner/herdr/herdrcli/operations.go +281 -0
- package/internal/runner/herdr/herdrcli/response.go +116 -0
- package/internal/runner/herdr/herdrcli/testdata/empty-panes.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/empty-tabs.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/error-not-git-worktree.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/error-pane-not-found.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/error-workspace-not-found.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/error-worktree-not-found.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/malformed.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-close.json +6 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-get.json +25 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-list.json +45 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-process-info-shell.json +22 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-process-info.json +23 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-rename.json +23 -0
- package/internal/runner/herdr/herdrcli/testdata/snapshot.json +213 -0
- package/internal/runner/herdr/herdrcli/testdata/strict-herdr.sh +175 -0
- package/internal/runner/herdr/herdrcli/testdata/tab-create.json +31 -0
- package/internal/runner/herdr/herdrcli/testdata/tab-list.json +26 -0
- package/internal/runner/herdr/herdrcli/testdata/workspace-close.json +6 -0
- package/internal/runner/herdr/herdrcli/testdata/worktree-create.json +58 -0
- package/internal/runner/herdr/herdrcli/testdata/worktree-list.json +35 -0
- package/internal/runner/herdr/herdrcli/testdata/worktree-open.json +59 -0
- package/internal/task/beads/beads.go +0 -16
- package/internal/task/beads/config_compatibility_test.go +7 -8
- package/internal/task/jira/filters_test.go +32 -6
- package/internal/task/jira/jira.go +27 -17
- package/package.json +1 -1
|
@@ -232,6 +232,33 @@ func TestCompileFilterAssigneeMatchesEmail(t *testing.T) {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
func TestCompileFilterCurrentUserMatchesAuthenticatedJiraEmail(t *testing.T) {
|
|
236
|
+
sys := newSystemWithFake(t, &fakeJira{}).(*system)
|
|
237
|
+
sys.currentUser = "me@example.com"
|
|
238
|
+
match, err := sys.CompileFilter(config.RawValues{
|
|
239
|
+
"filters": map[string]any{"assignees": []any{"currentUser()"}},
|
|
240
|
+
})
|
|
241
|
+
if err != nil {
|
|
242
|
+
t.Fatalf("CompileFilter failed: %v", err)
|
|
243
|
+
}
|
|
244
|
+
if !match(task.Ticket{Key: "PAY-1", Fields: map[string]any{"assignee": "ME@EXAMPLE.COM"}}) {
|
|
245
|
+
t.Fatal("currentUser() did not resolve to the authenticated Jira email")
|
|
246
|
+
}
|
|
247
|
+
if match(task.Ticket{Key: "PAY-2", Fields: map[string]any{"assignee": "other@example.com"}}) {
|
|
248
|
+
t.Fatal("currentUser() matched another Jira assignee")
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
func TestCompileFilterCurrentUserRequiresCredentials(t *testing.T) {
|
|
253
|
+
sys := newSystemWithFake(t, &fakeJira{})
|
|
254
|
+
_, err := sys.CompileFilter(config.RawValues{
|
|
255
|
+
"filters": map[string]any{"assignees": []any{"currentUser()"}},
|
|
256
|
+
})
|
|
257
|
+
if err == nil || !strings.Contains(err.Error(), "authenticated Jira credentials") {
|
|
258
|
+
t.Fatalf("CompileFilter error = %v, want authenticated-credentials error", err)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
235
262
|
func TestCompileFilterAssigneeMatchAndMismatch(t *testing.T) {
|
|
236
263
|
// Assignee is a supported structured filter dimension
|
|
237
264
|
// (repo-workflow-routing: "parent statuses, issue types, labels, and
|
|
@@ -253,7 +280,7 @@ func TestCompileFilterAssigneeMatchAndMismatch(t *testing.T) {
|
|
|
253
280
|
}
|
|
254
281
|
}
|
|
255
282
|
|
|
256
|
-
func
|
|
283
|
+
func TestCompileFilterDoesNotUseEffectiveAssigneeAsFilter(t *testing.T) {
|
|
257
284
|
sys, err := newSystem(context.Background(), &fakeClient{fake: &fakeJira{}}, task.RepoSpec{
|
|
258
285
|
Name: "payments",
|
|
259
286
|
RootConfig: config.RawValues{"assignee": "root@example.com"},
|
|
@@ -266,11 +293,10 @@ func TestCompileFilterInheritsEffectiveAssigneeCaseInsensitively(t *testing.T) {
|
|
|
266
293
|
if err != nil {
|
|
267
294
|
t.Fatal(err)
|
|
268
295
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
t.Fatal("ticket assigned to another user matched effective assignee")
|
|
296
|
+
for _, assignee := range []string{"root@example.com", "repo.bot@example.COM", "other@example.com"} {
|
|
297
|
+
if !match(task.Ticket{Fields: map[string]any{"assignee": assignee}}) {
|
|
298
|
+
t.Fatalf("assignee %q was filtered without filters.assignees", assignee)
|
|
299
|
+
}
|
|
274
300
|
}
|
|
275
301
|
}
|
|
276
302
|
|
|
@@ -53,6 +53,7 @@ const (
|
|
|
53
53
|
defaultStartParentStatus = "In Progress"
|
|
54
54
|
defaultWorkTaskStatus = "In Progress"
|
|
55
55
|
defaultEndParentStatus = "Done"
|
|
56
|
+
currentUserAssigneeFilter = "currentUser()"
|
|
56
57
|
defaultMailboxDescription = `Parent ticket: {{ticket}}
|
|
57
58
|
Workflow: {{workflow}}
|
|
58
59
|
Node: {{node}}
|
|
@@ -147,7 +148,12 @@ func init() {
|
|
|
147
148
|
if err != nil {
|
|
148
149
|
return nil, err
|
|
149
150
|
}
|
|
150
|
-
|
|
151
|
+
sys, err := newSystem(ctx, client, spec)
|
|
152
|
+
if err != nil {
|
|
153
|
+
return nil, err
|
|
154
|
+
}
|
|
155
|
+
sys.currentUser = strings.TrimSpace(creds.Email)
|
|
156
|
+
return sys, nil
|
|
151
157
|
},
|
|
152
158
|
})
|
|
153
159
|
}
|
|
@@ -173,10 +179,11 @@ func sharedClient(site, email, token string) (*jirarest.HTTPClient, error) {
|
|
|
173
179
|
// system is the repo-bound Jira task.System. It is safe for concurrent use;
|
|
174
180
|
// the REST client owns connection reuse, caches, and request limiting.
|
|
175
181
|
type system struct {
|
|
176
|
-
cli
|
|
177
|
-
repoName
|
|
178
|
-
|
|
179
|
-
|
|
182
|
+
cli jirarest.Client
|
|
183
|
+
repoName string
|
|
184
|
+
currentUser string
|
|
185
|
+
base config.RawValues
|
|
186
|
+
effective Config // root+repo merged
|
|
180
187
|
}
|
|
181
188
|
|
|
182
189
|
func newSystem(ctx context.Context, cli jirarest.Client, spec task.RepoSpec) (*system, error) {
|
|
@@ -323,9 +330,11 @@ func (s *system) CompileFilter(workflowTaskConfig config.RawValues) (func(task.T
|
|
|
323
330
|
return nil, err
|
|
324
331
|
}
|
|
325
332
|
f := cfg.Filters
|
|
326
|
-
|
|
327
|
-
|
|
333
|
+
resolvedAssignees, err := s.resolveAssigneeFilters(f.Assignees)
|
|
334
|
+
if err != nil {
|
|
335
|
+
return nil, err
|
|
328
336
|
}
|
|
337
|
+
f.Assignees = resolvedAssignees
|
|
329
338
|
return func(t task.Ticket) bool {
|
|
330
339
|
if len(f.ParentStatuses) > 0 && !contains(f.ParentStatuses, strField(t.Fields, "status")) {
|
|
331
340
|
return false
|
|
@@ -348,17 +357,18 @@ func (s *system) CompileFilter(workflowTaskConfig config.RawValues) (func(task.T
|
|
|
348
357
|
}, nil
|
|
349
358
|
}
|
|
350
359
|
|
|
351
|
-
func
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
360
|
+
func (s *system) resolveAssigneeFilters(values []string) ([]string, error) {
|
|
361
|
+
resolved := make([]string, 0, len(values))
|
|
362
|
+
for _, value := range values {
|
|
363
|
+
if value == currentUserAssigneeFilter {
|
|
364
|
+
if s.currentUser == "" {
|
|
365
|
+
return nil, fmt.Errorf("jira: filters.assignees value %q requires authenticated Jira credentials", currentUserAssigneeFilter)
|
|
366
|
+
}
|
|
367
|
+
value = s.currentUser
|
|
368
|
+
}
|
|
369
|
+
resolved = append(resolved, value)
|
|
361
370
|
}
|
|
371
|
+
return resolved, nil
|
|
362
372
|
}
|
|
363
373
|
|
|
364
374
|
// --- Claim ---
|