From 8d3d4982457143e3476adcab7a7620ea331015ef Mon Sep 17 00:00:00 2001 From: vikingowl Date: Sun, 5 Apr 2026 23:06:23 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20coordinator=20mode=20=E2=80=94=20system?= =?UTF-8?q?=20prompt=20injection=20for=20orchestration=20tasks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/engine/coordinator_test.go | 39 +++++++++++++++++++++++++++++ internal/engine/loop.go | 26 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 internal/engine/coordinator_test.go diff --git a/internal/engine/coordinator_test.go b/internal/engine/coordinator_test.go new file mode 100644 index 0000000..57deb4f --- /dev/null +++ b/internal/engine/coordinator_test.go @@ -0,0 +1,39 @@ +package engine + +import ( + "strings" + "testing" + + "somegit.dev/Owlibou/gnoma/internal/router" +) + +func TestCoordinatorSystemPrompt_InjectedForOrchestration(t *testing.T) { + prompt := coordinatorPrompt() + if !strings.Contains(prompt, "spawn_elfs") { + t.Error("coordinator prompt must mention spawn_elfs") + } + if !strings.Contains(prompt, "list_results") { + t.Error("coordinator prompt must mention list_results") + } +} + +func TestShouldInjectCoordinatorPrompt(t *testing.T) { + cases := []struct { + prompt string + want bool + }{ + {"orchestrate the migration", true}, + {"coordinate the refactor", true}, + {"dispatch tasks to elfs", true}, + {"fix the bug in main.go", false}, + {"explain this function", false}, + {"write unit tests for auth", false}, + } + for _, c := range cases { + task := router.ClassifyTask(c.prompt) + got := task.Type == router.TaskOrchestration + if got != c.want { + t.Errorf("prompt %q: want orchestration=%v, got %v (type=%s)", c.prompt, c.want, got, task.Type) + } + } +} diff --git a/internal/engine/loop.go b/internal/engine/loop.go index 13f5466..9d78a54 100644 --- a/internal/engine/loop.go +++ b/internal/engine/loop.go @@ -301,9 +301,35 @@ func (e *Engine) buildRequest(ctx context.Context) provider.Request { ) } + // Inject coordinator guidance for orchestration tasks + if e.cfg.Router != nil { + prompt := "" + for i := len(e.history) - 1; i >= 0; i-- { + if e.history[i].Role == message.RoleUser { + prompt = e.history[i].TextContent() + break + } + } + if router.ClassifyTask(prompt).Type == router.TaskOrchestration { + req.SystemPrompt = coordinatorPrompt() + "\n\n" + req.SystemPrompt + } + } + return req } +// coordinatorPrompt returns the system prompt block injected for orchestration tasks. +func coordinatorPrompt() string { + return `You are operating in coordinator mode. Your role is to decompose complex work into parallel tasks and orchestrate elfs. + +Rules: +- Use spawn_elfs to dispatch N tasks in parallel when they don't share write state. +- Use list_results to discover outputs produced by prior tool calls in this session. +- Pass result file paths to elfs in their prompts so they can read prior outputs with read_result or fs.read. +- Writes are serial: if two elfs would write the same file, sequence them. +- Synthesize elf outputs into a coherent final answer.` +} + func (e *Engine) executeTools(ctx context.Context, calls []message.ToolCall, cb Callback) ([]message.ToolResult, error) { // Partition into read-only (parallel) and write (serial) batches type toolCallWithTool struct {