relay-flow 0.2.4-alpha → 0.2.6-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 +29 -18
- package/cmd/relay-flow/backend_selection_test.go +149 -0
- package/cmd/relay-flow/beads_composition_test.go +3 -3
- package/cmd/relay-flow/main.go +94 -13
- package/cmd/relay-flow/scenario_test.go +19 -2
- package/cmd/relay-flow/serve.go +98 -19
- package/cmd/relay-flow/serve_recovery_test.go +100 -0
- package/cmd/relay-flow/temporal_init.go +170 -0
- package/cmd/relay-flow/temporal_init_test.go +217 -0
- package/cmd/relay-flow/temporal_report_test.go +733 -0
- package/examples/beads-workflow.yaml +3 -0
- package/examples/config-reference.yaml +7 -3
- package/examples/minimal-beads-task-workflow.yaml +2 -1
- package/examples/workflow-reference.yaml +2 -1
- package/go.mod +37 -16
- package/go.sum +129 -61
- package/internal/config/machine.go +33 -1
- package/internal/config/machine_test.go +76 -0
- package/internal/execution/goworkflows/activities.go +19 -0
- package/internal/execution/goworkflows/engine.go +13 -38
- package/internal/execution/goworkflows/engine_test.go +1 -1
- package/internal/execution/goworkflows/node_runtime_test.go +113 -4
- package/internal/execution/goworkflows/projection.go +47 -464
- package/internal/execution/projection/projection.go +867 -0
- package/internal/execution/projection/projection_test.go +347 -0
- package/internal/execution/temporal/activities.go +586 -0
- package/internal/execution/temporal/engine.go +384 -0
- package/internal/execution/temporal/engine_test.go +277 -0
- package/internal/execution/temporal/interpreter.go +736 -0
- package/internal/execution/temporal/operations.go +455 -0
- package/internal/execution/temporal/operations_test.go +101 -0
- package/internal/execution/temporal/recovery.go +194 -0
- package/internal/execution/temporal/recovery_runtime.go +41 -0
- package/internal/execution/temporal/recovery_test.go +102 -0
- package/internal/execution/temporal/snapshot_restart_test.go +72 -0
- package/internal/execution/temporal/spike_test.go +934 -0
- package/internal/execution/temporal/visibility_lag_test.go +415 -0
- package/internal/harness/harness.go +5 -0
- package/internal/harness/opencode/opencode.go +14 -3
- package/internal/harness/opencode/opencode_test.go +1 -1
- package/internal/harness/opencode/repo_setup.go +1 -1
- package/internal/harness/opencode/task_env_test.go +57 -0
- package/internal/harness/pi/pi.go +58 -47
- package/internal/harness/pi/pi_test.go +26 -10
- package/internal/harness/pi/prompt_test.go +30 -1
- package/internal/harness/pi/task_env_test.go +51 -0
- package/internal/harness/pi/validation_test.go +27 -51
- package/internal/repo/service.go +18 -8
- package/internal/runner/herdr/herdr.go +14 -0
- package/internal/runner/herdr/herdr_test.go +20 -0
- package/internal/runner/orca/orca.go +33 -0
- package/internal/runner/orca/orca_test.go +33 -4
- package/internal/runner/runner.go +8 -0
- package/internal/task/beads/agent_env_test.go +52 -0
- package/internal/task/beads/beads.go +87 -7
- package/internal/task/beads/beads_test.go +78 -9
- package/internal/task/beads/repo_composition_test.go +47 -3
- package/internal/task/factory.go +31 -2
- package/internal/task/task.go +10 -0
- package/package.json +1 -1
|
@@ -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
|
|
@@ -23,6 +23,7 @@ import (
|
|
|
23
23
|
goworkflow "github.com/cschleiden/go-workflows/workflow"
|
|
24
24
|
"github.com/google/uuid"
|
|
25
25
|
|
|
26
|
+
"github.com/rajpopat27/relay-flow/internal/execution/projection"
|
|
26
27
|
"github.com/rajpopat27/relay-flow/internal/harness"
|
|
27
28
|
"github.com/rajpopat27/relay-flow/internal/identity"
|
|
28
29
|
"github.com/rajpopat27/relay-flow/internal/repo"
|
|
@@ -68,46 +69,13 @@ type Engine struct {
|
|
|
68
69
|
workerName string
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
// InitDatabase
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
func InitDatabase(path string) error {
|
|
75
|
-
db, err := sql.Open("sqlite", fmt.Sprintf("file:%s?_txlock=immediate", path))
|
|
76
|
-
if err != nil {
|
|
77
|
-
return fmt.Errorf("open %s: %w", path, err)
|
|
78
|
-
}
|
|
79
|
-
defer db.Close()
|
|
80
|
-
if _, err := db.Exec(`PRAGMA schema_version`); err != nil {
|
|
81
|
-
return fmt.Errorf("open %s: %w", path, err)
|
|
82
|
-
}
|
|
83
|
-
if _, err := db.Exec("PRAGMA journal_mode=WAL;"); err != nil {
|
|
84
|
-
return fmt.Errorf("open %s: %w", path, err)
|
|
85
|
-
}
|
|
86
|
-
if err := os.Chmod(path, 0o600); err != nil && !os.IsNotExist(err) {
|
|
87
|
-
return fmt.Errorf("chmod %s: %w", path, err)
|
|
88
|
-
}
|
|
89
|
-
proj := &RunProjection{DB: db}
|
|
90
|
-
if err := proj.migrate(); err != nil {
|
|
91
|
-
return fmt.Errorf("migrate relay_runs: %w", err)
|
|
92
|
-
}
|
|
93
|
-
return nil
|
|
94
|
-
}
|
|
72
|
+
// InitDatabase preserves the embedded-engine public helper while delegating
|
|
73
|
+
// relay-owned schema lifecycle to the shared projection package.
|
|
74
|
+
func InitDatabase(path string) error { return projection.InitDatabase(path) }
|
|
95
75
|
|
|
96
|
-
// HasNonterminalRuns inspects
|
|
97
|
-
// otherwise modifying it. It is used by init --force before config changes.
|
|
76
|
+
// HasNonterminalRuns inspects the shared projection without migrating it.
|
|
98
77
|
func HasNonterminalRuns(path string) (bool, error) {
|
|
99
|
-
|
|
100
|
-
if err != nil {
|
|
101
|
-
return false, fmt.Errorf("open %s: %w", path, err)
|
|
102
|
-
}
|
|
103
|
-
defer db.Close()
|
|
104
|
-
var active bool
|
|
105
|
-
if err := db.QueryRow(`SELECT EXISTS(
|
|
106
|
-
SELECT 1 FROM relay_runs WHERE state NOT IN ('completed', 'canceled')
|
|
107
|
-
)`).Scan(&active); err != nil {
|
|
108
|
-
return false, fmt.Errorf("inspect %s: %w", path, err)
|
|
109
|
-
}
|
|
110
|
-
return active, nil
|
|
78
|
+
return projection.HasNonterminalRuns(path)
|
|
111
79
|
}
|
|
112
80
|
|
|
113
81
|
// New opens the SQLite database at path (created with mode 0600 when
|
|
@@ -148,6 +116,13 @@ func New(path string, deps Dependencies) (*Engine, error) {
|
|
|
148
116
|
db.Close()
|
|
149
117
|
return nil, fmt.Errorf("migrate relay_runs: %w", err)
|
|
150
118
|
}
|
|
119
|
+
// A marker-less legacy database is adopted only by the embedded executor;
|
|
120
|
+
// a persisted Temporal identity fails closed rather than being combined
|
|
121
|
+
// with go-workflows state.
|
|
122
|
+
if err := (&projection.RunProjection{DB: db}).VerifyIdentity(context.Background(), projection.ExecutorIdentity{ExecutorPlugin: "goworkflows"}); err != nil {
|
|
123
|
+
db.Close()
|
|
124
|
+
return nil, fmt.Errorf("verify executor identity: %w", err)
|
|
125
|
+
}
|
|
151
126
|
activities := &Activities{
|
|
152
127
|
Repos: deps.Repos,
|
|
153
128
|
Runner: deps.Runner,
|
|
@@ -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
|
}
|
|
@@ -9,6 +9,7 @@ import (
|
|
|
9
9
|
"testing"
|
|
10
10
|
"time"
|
|
11
11
|
|
|
12
|
+
"github.com/rajpopat27/relay-flow/internal/config"
|
|
12
13
|
"github.com/rajpopat27/relay-flow/internal/harness"
|
|
13
14
|
"github.com/rajpopat27/relay-flow/internal/repo"
|
|
14
15
|
"github.com/rajpopat27/relay-flow/internal/run"
|
|
@@ -210,7 +211,7 @@ func TestEnsureNodeRuntimeUsesDirectIDsAndFallsBackFresh(t *testing.T) {
|
|
|
210
211
|
}); err != nil {
|
|
211
212
|
t.Fatal(err)
|
|
212
213
|
}
|
|
213
|
-
a := &Activities{Runner: fr, Harness: fh, Runs: p}
|
|
214
|
+
a := &Activities{Repos: runtimeTestRepos(&runtimeTestTaskSystem{}), Runner: fr, Harness: fh, Runs: p}
|
|
214
215
|
nw := run.NodeWork{Work: run.Work{RunID: id, Repo: "payments", Workflow: "basic", Parent: task.TicketRef{Key: "PAY-101"}}, Node: "implement", NodeVisitID: "visit-2"}
|
|
215
216
|
spec := harness.LaunchSpec{RunID: id, NodeVisitID: "visit-2", RepoName: "payments", Workflow: "basic", Ticket: "PAY-101", Node: "implement", Agent: "build", Title: "PAY-101:implement", Prompt: "work", NudgePrompt: "custom instructions"}
|
|
216
217
|
if err := a.EnsureNodeRuntime(ctx, nw, "/srv/payments", spec, NodeRuntime{NodeVisitID: "visit-2"}); err != nil {
|
|
@@ -259,8 +260,8 @@ func TestEnsureNodeRuntimeInitialLaunchAppendsCustomInstructions(t *testing.T) {
|
|
|
259
260
|
}
|
|
260
261
|
fh := &runtimeTestHarness{}
|
|
261
262
|
fr := &runtimeTestRunner{}
|
|
262
|
-
a := &Activities{Runner: fr, Harness: fh, Runs: p}
|
|
263
|
-
nw := run.NodeWork{Work: run.Work{RunID: id}, Node: "implement", NodeVisitID: "visit-first"}
|
|
263
|
+
a := &Activities{Repos: runtimeTestRepos(&runtimeTestTaskSystem{}), Runner: fr, Harness: fh, Runs: p}
|
|
264
|
+
nw := run.NodeWork{Work: run.Work{RunID: id, Repo: "payments"}, Node: "implement", NodeVisitID: "visit-first"}
|
|
264
265
|
spec := harness.LaunchSpec{RunID: id, NodeVisitID: "visit-first", Node: "implement", NodeType: workflow.NodeHITL, Agent: "build", Prompt: "standard prompt", NudgePrompt: "custom instructions"}
|
|
265
266
|
if err := a.EnsureNodeRuntime(ctx, nw, "", spec, NodeRuntime{}); err != nil {
|
|
266
267
|
t.Fatal(err)
|
|
@@ -273,6 +274,63 @@ func TestEnsureNodeRuntimeInitialLaunchAppendsCustomInstructions(t *testing.T) {
|
|
|
273
274
|
}
|
|
274
275
|
}
|
|
275
276
|
|
|
277
|
+
// TestEnsureNodeRuntimeSuppliesTaskSystemAgentEnv asserts the repo's task
|
|
278
|
+
// system supplies the agent workspace environment at launch time, so the
|
|
279
|
+
// harness/runner command addresses the same workspace as relay-flow. Adapters
|
|
280
|
+
// without the optional capability supply nothing.
|
|
281
|
+
func TestEnsureNodeRuntimeSuppliesTaskSystemAgentEnv(t *testing.T) {
|
|
282
|
+
ctx := context.Background()
|
|
283
|
+
db := openProjectionDB(t, filepath.Join(t.TempDir(), "state.db"))
|
|
284
|
+
defer db.Close()
|
|
285
|
+
p := &RunProjection{DB: db}
|
|
286
|
+
if err := p.migrate(); err != nil {
|
|
287
|
+
t.Fatal(err)
|
|
288
|
+
}
|
|
289
|
+
id := run.ID("payments/basic/PAY-105")
|
|
290
|
+
if err := p.insertStart(ctx, run.Start{ID: id, Repo: "payments", Workflow: workflow.Workflow{Name: "basic"}, Ticket: task.TicketRef{ID: "5", Key: "PAY-105"}}, time.Now().UTC()); err != nil {
|
|
291
|
+
t.Fatal(err)
|
|
292
|
+
}
|
|
293
|
+
if err := p.updateNodeRuntimeVisit(ctx, id, "implement", "visit-first"); err != nil {
|
|
294
|
+
t.Fatal(err)
|
|
295
|
+
}
|
|
296
|
+
want := map[string]string{"BEADS_DIR": "/var/lib/beads/payments/.beads", "BEADS_DB": "", "BD_DB": ""}
|
|
297
|
+
fh := &runtimeTestHarness{}
|
|
298
|
+
a := &Activities{
|
|
299
|
+
Repos: runtimeTestRepos(&runtimeTestTaskSystem{agentEnv: want}),
|
|
300
|
+
Runner: &runtimeTestRunner{},
|
|
301
|
+
Harness: fh,
|
|
302
|
+
Runs: p,
|
|
303
|
+
}
|
|
304
|
+
nw := run.NodeWork{Work: run.Work{RunID: id, Repo: "payments", Workflow: "basic", Parent: task.TicketRef{Key: "PAY-105"}}, Node: "implement", NodeVisitID: "visit-first"}
|
|
305
|
+
spec := harness.LaunchSpec{RunID: id, NodeVisitID: "visit-first", RepoName: "payments", Node: "implement", Agent: "build", Title: "PAY-105:implement"}
|
|
306
|
+
if err := a.EnsureNodeRuntime(ctx, nw, "/srv/payments", spec, NodeRuntime{}); err != nil {
|
|
307
|
+
t.Fatal(err)
|
|
308
|
+
}
|
|
309
|
+
if len(fh.taskEnvs) != 1 || !reflect.DeepEqual(fh.taskEnvs[0], want) {
|
|
310
|
+
t.Fatalf("launch TaskEnv = %#v, want [%#v]", fh.taskEnvs, want)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// An adapter without the optional capability supplies no environment.
|
|
314
|
+
plain := &runtimeTestHarness{}
|
|
315
|
+
b := &Activities{
|
|
316
|
+
Repos: runtimeTestRepos(&runtimeTestPlainTaskSystem{System: &runtimeTestTaskSystem{}}),
|
|
317
|
+
Runner: &runtimeTestRunner{},
|
|
318
|
+
Harness: plain,
|
|
319
|
+
Runs: p,
|
|
320
|
+
}
|
|
321
|
+
if err := p.updateNodeRuntimeVisit(ctx, id, "review", "visit-review"); err != nil {
|
|
322
|
+
t.Fatal(err)
|
|
323
|
+
}
|
|
324
|
+
nw.Node, nw.NodeVisitID = "review", "visit-review"
|
|
325
|
+
spec.Node, spec.NodeVisitID, spec.Title = "review", "visit-review", "PAY-105:review"
|
|
326
|
+
if err := b.EnsureNodeRuntime(ctx, nw, "/srv/payments", spec, NodeRuntime{}); err != nil {
|
|
327
|
+
t.Fatal(err)
|
|
328
|
+
}
|
|
329
|
+
if len(plain.taskEnvs) != 1 || plain.taskEnvs[0] != nil {
|
|
330
|
+
t.Fatalf("TaskEnv without a configured workspace = %#v, want [nil]", plain.taskEnvs)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
276
334
|
func TestEnsureNodeRuntimeSendFailureClosesLiveTerminal(t *testing.T) {
|
|
277
335
|
ctx := context.Background()
|
|
278
336
|
db := openProjectionDB(t, filepath.Join(t.TempDir(), "state.db"))
|
|
@@ -293,7 +351,7 @@ func TestEnsureNodeRuntimeSendFailureClosesLiveTerminal(t *testing.T) {
|
|
|
293
351
|
}
|
|
294
352
|
fr := &runtimeTestRunner{live: true, sendErr: errors.New("send failed")}
|
|
295
353
|
fh := &runtimeTestHarness{}
|
|
296
|
-
a := &Activities{Runner: fr, Harness: fh, Runs: p}
|
|
354
|
+
a := &Activities{Repos: runtimeTestRepos(&runtimeTestTaskSystem{}), Runner: fr, Harness: fh, Runs: p}
|
|
297
355
|
nw := run.NodeWork{Work: run.Work{RunID: id, Repo: "payments", Workflow: "basic", Parent: task.TicketRef{Key: "PAY-102"}}, Node: "implement", NodeVisitID: "visit-new", Mailbox: task.Mailbox{Key: "PAY-234", Node: "implement"}}
|
|
298
356
|
spec := harness.LaunchSpec{RunID: id, NodeVisitID: "visit-new", Node: "implement", Agent: "build", Title: "PAY-102:implement", Prompt: "work", NudgePrompt: "Read the latest review feedback."}
|
|
299
357
|
if err := a.EnsureNodeRuntime(ctx, nw, "/srv/payments", spec, NodeRuntime{RunID: id, Node: "implement", TerminalID: "live-old", SessionID: "session-old", NodeVisitID: "visit-old"}); err != nil {
|
|
@@ -499,6 +557,7 @@ type runtimeTestHarness struct {
|
|
|
499
557
|
prompts []string
|
|
500
558
|
resumeIDs []string
|
|
501
559
|
rendered []harness.PromptKind
|
|
560
|
+
taskEnvs []map[string]string
|
|
502
561
|
}
|
|
503
562
|
|
|
504
563
|
func (h *runtimeTestHarness) RenderPrompt(kind harness.PromptKind, _ harness.PromptData, nudge string) (string, error) {
|
|
@@ -522,9 +581,59 @@ func (h *runtimeTestHarness) BuildCommand(spec harness.LaunchSpec) (runner.Comma
|
|
|
522
581
|
h.buildCalls++
|
|
523
582
|
h.prompts = append(h.prompts, spec.Prompt)
|
|
524
583
|
h.resumeIDs = append(h.resumeIDs, spec.ResumeID)
|
|
584
|
+
h.taskEnvs = append(h.taskEnvs, spec.TaskEnv)
|
|
525
585
|
return runner.Command{Executable: "opencode", Args: []string{spec.ResumeID}}, nil
|
|
526
586
|
}
|
|
527
587
|
|
|
588
|
+
// runtimeTestTaskSystem is a repo-bound task system stub. Only the optional
|
|
589
|
+
// task.AgentEnvironment capability is exercised here; every other method is an
|
|
590
|
+
// unused stub for the node runtime tests.
|
|
591
|
+
type runtimeTestTaskSystem struct {
|
|
592
|
+
agentEnv map[string]string
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
func (s *runtimeTestTaskSystem) AgentEnv() map[string]string { return s.agentEnv }
|
|
596
|
+
|
|
597
|
+
func (*runtimeTestTaskSystem) Poll(context.Context) ([]task.Ticket, error) { return nil, nil }
|
|
598
|
+
func (*runtimeTestTaskSystem) CompileFilter(config.RawValues) (func(task.Ticket) bool, error) {
|
|
599
|
+
return func(task.Ticket) bool { return true }, nil
|
|
600
|
+
}
|
|
601
|
+
func (*runtimeTestTaskSystem) Claim(context.Context, task.TicketRef, string) error { return nil }
|
|
602
|
+
func (*runtimeTestTaskSystem) ValidateConfig(context.Context, config.RawValues, map[string]config.RawValues) error {
|
|
603
|
+
return nil
|
|
604
|
+
}
|
|
605
|
+
func (*runtimeTestTaskSystem) RenderText(task.TextKind, task.TextData) (string, error) {
|
|
606
|
+
return "", nil
|
|
607
|
+
}
|
|
608
|
+
func (*runtimeTestTaskSystem) EnsureMailboxes(context.Context, task.TicketRef, string, []task.MailboxSpec) (map[string]task.Mailbox, error) {
|
|
609
|
+
return map[string]task.Mailbox{}, nil
|
|
610
|
+
}
|
|
611
|
+
func (*runtimeTestTaskSystem) ApplyTaskConfig(context.Context, task.Target, config.RawValues) error {
|
|
612
|
+
return nil
|
|
613
|
+
}
|
|
614
|
+
func (*runtimeTestTaskSystem) CompleteMailbox(context.Context, task.Mailbox) error { return nil }
|
|
615
|
+
func (*runtimeTestTaskSystem) HasComment(context.Context, task.Target, string) (bool, error) {
|
|
616
|
+
return false, nil
|
|
617
|
+
}
|
|
618
|
+
func (*runtimeTestTaskSystem) Comment(context.Context, task.Target, string, string) error {
|
|
619
|
+
return nil
|
|
620
|
+
}
|
|
621
|
+
func (*runtimeTestTaskSystem) ResetForRecovery(context.Context, task.TicketRef, []task.Mailbox, config.RawValues) error {
|
|
622
|
+
return nil
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// runtimeTestPlainTaskSystem forwards the task.System contract without
|
|
626
|
+
// exposing the optional AgentEnvironment capability.
|
|
627
|
+
type runtimeTestPlainTaskSystem struct {
|
|
628
|
+
task.System
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
func runtimeTestRepos(sys task.System) *repo.Registry {
|
|
632
|
+
reg := &repo.Registry{}
|
|
633
|
+
reg.Replace(&repo.Repo{Name: "payments", Path: "/srv/payments", TaskSystem: sys})
|
|
634
|
+
return reg
|
|
635
|
+
}
|
|
636
|
+
|
|
528
637
|
func openProjectionDB(t *testing.T, path string) *sql.DB {
|
|
529
638
|
t.Helper()
|
|
530
639
|
db, err := sql.Open("sqlite", path)
|