relay-flow 0.3.10-alpha → 0.3.12-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 +10 -22
- package/cmd/relay-flow/commands_test.go +40 -2
- package/cmd/relay-flow/main.go +13 -1
- package/examples/config-reference.yaml +23 -19
- package/examples/workflow-reference.yaml +3 -0
- package/internal/execution/goworkflows/activities.go +13 -22
- package/internal/execution/goworkflows/engine.go +1 -1
- package/internal/execution/goworkflows/engine_test.go +52 -3
- package/internal/execution/goworkflows/interpreter.go +24 -11
- package/internal/execution/goworkflows/mailbox_test.go +28 -1
- package/internal/execution/temporal/activities.go +13 -22
- package/internal/execution/temporal/compact_mailbox_test.go +56 -0
- package/internal/execution/temporal/interpreter.go +14 -2
- package/internal/execution/temporal/operations.go +1 -1
- package/internal/execution/temporal/operations_test.go +75 -0
- package/internal/harness/harness.go +22 -10
- package/internal/harness/opencode/opencode.go +17 -4
- package/internal/harness/opencode/opencode_test.go +25 -1
- package/internal/harness/opencode/repo_setup.go +1 -1
- package/internal/harness/opencode/task_env_test.go +2 -2
- package/internal/harness/pi/pi.go +15 -2
- package/internal/harness/pi/pi_test.go +7 -4
- package/internal/harness/pi/prompt_test.go +13 -5
- package/internal/harness/pi/task_env_test.go +2 -2
- package/internal/run/run.go +10 -0
- package/internal/server/api_test.go +31 -0
- package/internal/server/client.go +11 -1
- package/internal/server/fixture_test.go +4 -0
- package/internal/server/server.go +2 -0
- package/internal/task/jira/jira.go +16 -11
- package/internal/task/jira/templates_test.go +38 -2
- package/internal/task/task.go +1 -0
- package/internal/workflow/report.go +7 -0
- package/package.json +1 -1
|
@@ -66,17 +66,16 @@ const (
|
|
|
66
66
|
defaultWorkTaskStatus = "In Progress"
|
|
67
67
|
defaultEndParentStatus = "Done"
|
|
68
68
|
currentUserAssigneeFilter = "currentUser()"
|
|
69
|
-
defaultMailboxDescription = `
|
|
70
|
-
Workflow: {{workflow}}
|
|
71
|
-
Node: {{node}}
|
|
72
|
-
Node type: {{nodeType}}
|
|
73
|
-
Agent: {{agent}}
|
|
74
|
-
Mailbox: {{mailbox}}
|
|
75
|
-
|
|
76
|
-
Node work:
|
|
69
|
+
defaultMailboxDescription = `{{ticket}} / {{node}} — {{nodeType}} — {{agent}}
|
|
77
70
|
{{nodeDescription}}
|
|
78
71
|
|
|
79
|
-
|
|
72
|
+
Success routes:
|
|
73
|
+
{{successRoutes}}
|
|
74
|
+
|
|
75
|
+
Failure routes:
|
|
76
|
+
{{failureRoutes}}
|
|
77
|
+
|
|
78
|
+
{{report}}`
|
|
80
79
|
defaultSummaryComment = `Summary for {{node}}
|
|
81
80
|
|
|
82
81
|
{{summaryReport}}`
|
|
@@ -93,6 +92,7 @@ var knownTextVars = map[string]bool{
|
|
|
93
92
|
"nodeDescription": true, "nextSteps": true, "successRoutes": true,
|
|
94
93
|
"failureRoutes": true, "mailbox": true, "sourceNode": true,
|
|
95
94
|
"targetNode": true, "summaryReport": true, "feedbackReport": true,
|
|
95
|
+
"report": true,
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
// DefaultConfig supplies Jira task-system text defaults for relay-flow init.
|
|
@@ -509,6 +509,11 @@ func validateTemplates(templates Templates) error {
|
|
|
509
509
|
}
|
|
510
510
|
}
|
|
511
511
|
}
|
|
512
|
+
for _, key := range []string{"report", "ticket", "node", "nodeType", "agent", "nodeDescription", "successRoutes", "failureRoutes"} {
|
|
513
|
+
if !strings.Contains(templates.MailboxDescription, "{{"+key+"}}") {
|
|
514
|
+
return fmt.Errorf("mailboxDescription must contain {{%s}}", key)
|
|
515
|
+
}
|
|
516
|
+
}
|
|
512
517
|
if !strings.Contains(templates.SummaryComment, "{{summaryReport}}") {
|
|
513
518
|
return fmt.Errorf("summaryComment must contain {{summaryReport}}")
|
|
514
519
|
}
|
|
@@ -535,11 +540,11 @@ func (s *system) RenderText(kind task.TextKind, data task.TextData) (string, err
|
|
|
535
540
|
"runID": data.RunID, "ticket": data.Ticket,
|
|
536
541
|
"workflow": data.Workflow, "repo": data.Repo, "node": data.Node,
|
|
537
542
|
"nodeType": data.NodeType, "agent": data.Agent,
|
|
538
|
-
"nodeDescription": data.NodeDescription, "nextSteps": data.NextSteps,
|
|
543
|
+
"nodeDescription": strings.ReplaceAll(data.NodeDescription, "{{ticket}}", data.Ticket), "nextSteps": data.NextSteps,
|
|
539
544
|
"successRoutes": data.SuccessRoutes, "failureRoutes": data.FailureRoutes,
|
|
540
545
|
"mailbox": data.Mailbox, "sourceNode": data.SourceNode,
|
|
541
546
|
"targetNode": data.TargetNode, "summaryReport": data.SummaryReport,
|
|
542
|
-
"feedbackReport": data.FeedbackReport,
|
|
547
|
+
"feedbackReport": data.FeedbackReport, "report": data.Report,
|
|
543
548
|
}
|
|
544
549
|
return textVarPattern.ReplaceAllStringFunc(tmpl, func(match string) string {
|
|
545
550
|
return values[textVarPattern.FindStringSubmatch(match)[1]]
|
|
@@ -10,6 +10,7 @@ import (
|
|
|
10
10
|
"github.com/rajpopat27/relay-flow/internal/config"
|
|
11
11
|
"github.com/rajpopat27/relay-flow/internal/run"
|
|
12
12
|
"github.com/rajpopat27/relay-flow/internal/task"
|
|
13
|
+
"github.com/rajpopat27/relay-flow/internal/workflow"
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
func TestTaskTextTemplatesExposeValues(t *testing.T) {
|
|
@@ -17,7 +18,7 @@ func TestTaskTextTemplatesExposeValues(t *testing.T) {
|
|
|
17
18
|
sys, err := newSystem(context.Background(), &fakeClient{fake: fake}, task.RepoSpec{
|
|
18
19
|
Name: "payments",
|
|
19
20
|
RootConfig: config.RawValues{"templates": map[string]any{
|
|
20
|
-
"mailboxDescription": "mailbox {{runID}}|{{ticket}}|{{workflow}}|{{repo}}|{{node}}|{{nodeType}}|{{agent}}|{{nodeDescription}}|{{nextSteps}}|{{successRoutes}}|{{failureRoutes}}|{{mailbox}}",
|
|
21
|
+
"mailboxDescription": "mailbox {{runID}}|{{ticket}}|{{workflow}}|{{repo}}|{{node}}|{{nodeType}}|{{agent}}|{{nodeDescription}}|{{nextSteps}}|{{successRoutes}}|{{failureRoutes}}|{{mailbox}}|{{report}}",
|
|
21
22
|
"summaryComment": "summary {{sourceNode}}|{{targetNode}}|{{mailbox}}\n{{summaryReport}}",
|
|
22
23
|
"feedbackComment": "feedback {{sourceNode}}|{{targetNode}}|{{mailbox}}\n{{feedbackReport}}",
|
|
23
24
|
}},
|
|
@@ -31,7 +32,7 @@ func TestTaskTextTemplatesExposeValues(t *testing.T) {
|
|
|
31
32
|
Node: "review", NodeType: "hitl", Agent: "reviewer", NodeDescription: "Review changes.",
|
|
32
33
|
NextSteps: "implement; end", SuccessRoutes: "end", FailureRoutes: "implement",
|
|
33
34
|
Mailbox: "PAY-3", SourceNode: "review", TargetNode: "implement",
|
|
34
|
-
SummaryReport: "COMPLETED:\nreviewed", FeedbackReport: "REQUIRED ACTIONS:\nfix it",
|
|
35
|
+
SummaryReport: "COMPLETED:\nreviewed", FeedbackReport: "REQUIRED ACTIONS:\nfix it", Report: "STATUS: success | failure",
|
|
35
36
|
}
|
|
36
37
|
description, err := sys.RenderText(task.TextMailboxDescription, data)
|
|
37
38
|
if err != nil {
|
|
@@ -52,12 +53,47 @@ func TestTaskTextTemplatesExposeValues(t *testing.T) {
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
func TestJiraDefaultMailboxDescriptionIsCompact(t *testing.T) {
|
|
57
|
+
sys, err := newSystem(context.Background(), &fakeClient{fake: &fakeJira{}}, task.RepoSpec{
|
|
58
|
+
Name: "payments", RepoConfig: testRepoConfig(nil),
|
|
59
|
+
})
|
|
60
|
+
if err != nil {
|
|
61
|
+
t.Fatal(err)
|
|
62
|
+
}
|
|
63
|
+
data := task.TextData{
|
|
64
|
+
Ticket: "PAY-101", Node: "coder", NodeType: "agent", Agent: "coder",
|
|
65
|
+
NodeDescription: "Implement the change.", SuccessRoutes: "reviewer — when: ready",
|
|
66
|
+
FailureRoutes: "coder — when: retry", Report: workflow.ReportFormat,
|
|
67
|
+
}
|
|
68
|
+
description, err := sys.RenderText(task.TextMailboxDescription, data)
|
|
69
|
+
if err != nil {
|
|
70
|
+
t.Fatal(err)
|
|
71
|
+
}
|
|
72
|
+
want := "PAY-101 / coder — agent — coder\nImplement the change.\n\nSuccess routes:\nreviewer — when: ready\n\nFailure routes:\ncoder — when: retry\n\n" + workflow.ReportFormat
|
|
73
|
+
if description != want {
|
|
74
|
+
t.Fatalf("description = %q, want %q", description, want)
|
|
75
|
+
}
|
|
76
|
+
if strings.Contains(description, "comment history") {
|
|
77
|
+
t.Fatal("Jira description requested comment history")
|
|
78
|
+
}
|
|
79
|
+
data.NodeDescription = "Read the parent Jira ticket for this step:\nacli jira workitem view \"{{ticket}}\" --fields \"summary,description\" --json"
|
|
80
|
+
withParent, err := sys.RenderText(task.TextMailboxDescription, data)
|
|
81
|
+
if err != nil || !strings.Contains(withParent, `acli jira workitem view "PAY-101" --fields "summary,description" --json`) {
|
|
82
|
+
t.Fatalf("node-specific parent instruction = %q, %v", withParent, err)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
55
86
|
func TestCommentTemplatesRequireReportValues(t *testing.T) {
|
|
56
87
|
for _, tc := range []struct {
|
|
57
88
|
name string
|
|
58
89
|
override map[string]any
|
|
59
90
|
wantError string
|
|
60
91
|
}{
|
|
92
|
+
{name: "description", override: map[string]any{"mailboxDescription": "missing"}, wantError: "mailboxDescription must contain {{report}}"},
|
|
93
|
+
{name: "report only", override: map[string]any{"mailboxDescription": "{{report}}"}, wantError: "mailboxDescription must contain {{ticket}}"},
|
|
94
|
+
{name: "no node work", override: map[string]any{"mailboxDescription": strings.Replace(defaultMailboxDescription, "{{nodeDescription}}", "No work provided", 1)}, wantError: "mailboxDescription must contain {{nodeDescription}}"},
|
|
95
|
+
{name: "no success routes", override: map[string]any{"mailboxDescription": strings.Replace(defaultMailboxDescription, "{{successRoutes}}", "None", 1)}, wantError: "mailboxDescription must contain {{successRoutes}}"},
|
|
96
|
+
{name: "no failure routes", override: map[string]any{"mailboxDescription": strings.Replace(defaultMailboxDescription, "{{failureRoutes}}", "None", 1)}, wantError: "mailboxDescription must contain {{failureRoutes}}"},
|
|
61
97
|
{name: "summary", override: map[string]any{"summaryComment": "missing"}, wantError: "summaryComment must contain {{summaryReport}}"},
|
|
62
98
|
{name: "feedback", override: map[string]any{"feedbackComment": "missing"}, wantError: "feedbackComment must contain {{feedbackReport}}"},
|
|
63
99
|
{name: "unknown", override: map[string]any{"mailboxDescription": "{{mailboxInstructions}}"}, wantError: "unknown template variable {{mailboxInstructions}}"},
|
package/internal/task/task.go
CHANGED
|
@@ -9,6 +9,13 @@ import (
|
|
|
9
9
|
// represents an intentionally empty section.
|
|
10
10
|
const None = "None"
|
|
11
11
|
|
|
12
|
+
// ReportFormat is the canonical agent-facing contract. Runtime plugins parse
|
|
13
|
+
// these four fields into the existing structured Report wire shape.
|
|
14
|
+
const ReportFormat = `STATUS: success | failure
|
|
15
|
+
NEXT STEP: <one valid route>
|
|
16
|
+
SUMMARY: <concise result>
|
|
17
|
+
FEEDBACK: <concise handoff, or None when NEXT STEP is end>`
|
|
18
|
+
|
|
12
19
|
type Summary struct {
|
|
13
20
|
Completed string `json:"completed"`
|
|
14
21
|
Commits string `json:"commits"`
|