package config_test import ( "fmt" "os" "path/filepath" "strings" "testing" "iop/packages/go/config" ) // validSingleRequestYAML is a baseline valid single-request preset YAML used // by multiple sub-tests. It uses an opaque workspace_ref, bounded limits, and // the approved plan→work→review stage bindings with high reasoning on selector // and plan/review, no high reasoning on work. const validSingleRequestYAML = ` server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" options: temperature: 0.2 - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-capability-ref-001" limits: wall_clock_ms: 1800000 timeout_ms: 600000 max_tool_iterations: 32 max_output_bytes: 8388608 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" options: temperature: 0.2 review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` // TestLoadEdgeSingleRequestExecutionPreset verifies that a valid single-request // preset decodes, normalizes, and survives LoadEdge alongside ordinary presets. func TestLoadEdgeSingleRequestExecutionPreset(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") t.Run("valid single-request preset loads", func(t *testing.T) { if err := os.WriteFile(f, []byte(validSingleRequestYAML), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } cfg, err := config.LoadEdge(f) if err != nil { t.Fatalf("load: %v", err) } if len(cfg.ExecutionPresets) != 1 { t.Fatalf("expected 1 preset, got %d", len(cfg.ExecutionPresets)) } p := cfg.ExecutionPresets[0] if p.ID != "fixed-single-request" { t.Errorf("preset id = %q, want %q", p.ID, "fixed-single-request") } if p.SingleRequest == nil { t.Fatal("expected SingleRequest to be set") } sr := p.SingleRequest if sr.WorkspaceRef != "ws-capability-ref-001" { t.Errorf("workspace_ref = %q, want ws-capability-ref-001", sr.WorkspaceRef) } if sr.Limits.WallClockMS != 1800000 { t.Errorf("wall_clock_ms = %d, want 1800000", sr.Limits.WallClockMS) } if sr.Limits.StageTimeoutMS != 600000 { t.Errorf("timeout_ms = %d, want 600000", sr.Limits.StageTimeoutMS) } if sr.Limits.MaxToolIterations != 32 { t.Errorf("max_tool_iterations = %d, want 32", sr.Limits.MaxToolIterations) } if sr.Limits.MaxOutputBytes != 8388608 { t.Errorf("max_output_bytes = %d, want 8388608", sr.Limits.MaxOutputBytes) } if sr.Stages.Plan.Model != "gemini-plan" { t.Errorf("plan model = %q, want gemini-plan", sr.Stages.Plan.Model) } if sr.Stages.Work.Model != "ornith-fast" { t.Errorf("work model = %q, want ornith-fast", sr.Stages.Work.Model) } if sr.Stages.Review.Model != "gemini-review" { t.Errorf("review model = %q, want gemini-review", sr.Stages.Review.Model) } // Allowed modes must be exactly ["light"]. if len(p.AllowedModes) != 1 || p.AllowedModes[0] != "light" { t.Errorf("allowed_modes = %v, want [light]", p.AllowedModes) } // Workspace tools must be nil (rejected for single-request). if p.WorkspaceTools != nil { t.Errorf("workspace_tools = %v, want nil", p.WorkspaceTools) } // Route must have exactly plan→work→review. route := p.Routes["light"] if len(route.Stages) != 3 { t.Fatalf("expected 3 route stages, got %d", len(route.Stages)) } if route.Stages[0].Role != "plan" || route.Stages[1].Role != "work" || route.Stages[2].Role != "review" { t.Errorf("route stages roles = %v, want [plan, work, review]", routeStagesRoles(route.Stages)) } }) t.Run("exact cap values load", func(t *testing.T) { yaml := singleRequestYAMLWithLimits( config.MaxSingleRequestWallClockMS, config.MaxSingleRequestStageTimeoutMS, config.MaxSingleRequestToolIterations, config.MaxSingleRequestOutputBytes, ) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } cfg, err := config.LoadEdge(f) if err != nil { t.Fatalf("load: %v", err) } p := cfg.ExecutionPresets[0] sr := p.SingleRequest if sr.Limits.WallClockMS != config.MaxSingleRequestWallClockMS { t.Errorf("wall_clock_ms = %d, want %d", sr.Limits.WallClockMS, config.MaxSingleRequestWallClockMS) } if sr.Limits.StageTimeoutMS != config.MaxSingleRequestStageTimeoutMS { t.Errorf("timeout_ms = %d, want %d", sr.Limits.StageTimeoutMS, config.MaxSingleRequestStageTimeoutMS) } if sr.Limits.MaxToolIterations != config.MaxSingleRequestToolIterations { t.Errorf("max_tool_iterations = %d, want %d", sr.Limits.MaxToolIterations, config.MaxSingleRequestToolIterations) } if sr.Limits.MaxOutputBytes != config.MaxSingleRequestOutputBytes { t.Errorf("max_output_bytes = %d, want %d", sr.Limits.MaxOutputBytes, config.MaxSingleRequestOutputBytes) } }) t.Run("minimum limit values load", func(t *testing.T) { yaml := singleRequestYAMLWithLimits(1, 1, 1, 1) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } cfg, err := config.LoadEdge(f) if err != nil { t.Fatalf("load: %v", err) } p := cfg.ExecutionPresets[0] sr := p.SingleRequest if sr.Limits.WallClockMS != 1 || sr.Limits.StageTimeoutMS != 1 || sr.Limits.MaxToolIterations != 1 || sr.Limits.MaxOutputBytes != 1 { t.Errorf("limits = %+v, want all 1", sr.Limits) } }) t.Run("single-request preset coexists with ordinary presets", func(t *testing.T) { yaml := ` server: listen: "0.0.0.0:9090" models: - id: "model-a" providers: prov-a: "model-a" - id: "model-b" providers: prov-a: "model-b" - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "direct-default" selector: model: "model-a" allowed_modes: - "direct" routes: direct: stages: [] - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-capability-ref-002" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["model-a", "model-b", "gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } cfg, err := config.LoadEdge(f) if err != nil { t.Fatalf("load: %v", err) } if len(cfg.ExecutionPresets) != 2 { t.Fatalf("expected 2 presets, got %d", len(cfg.ExecutionPresets)) } byID := map[string]config.ExecutionPreset{} for _, p := range cfg.ExecutionPresets { byID[p.ID] = p } if _, ok := byID["direct-default"]; !ok { t.Fatal("expected direct-default preset") } if _, ok := byID["fixed-single-request"]; !ok { t.Fatal("expected fixed-single-request preset") } // Ordinary preset must not have SingleRequest set. if byID["direct-default"].SingleRequest != nil { t.Error("direct-default should not have SingleRequest set") } // Single-request preset must not have workspace_tools. if byID["fixed-single-request"].WorkspaceTools != nil { t.Error("fixed-single-request should not have workspace_tools") } }) } // TestLoadEdgeSingleRequestExecutionPresetRejectsDivergentEffectiveBindings verifies that every // single-request role (plan/work/review) fails closed when route and single_request bindings diverge. func TestLoadEdgeSingleRequestExecutionPresetRejectsDivergentEffectiveBindings(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") const ( planOptions = "\n options:\n reasoning_effort: \"high\"" workOptions = "" reviewOptions = "\n options:\n reasoning_effort: \"high\"" planLeakOptions = "\n options:\n reasoning_effort: \"high\"\n temperature: 0.8" workRouteLeakModel = "\n options:\n temperature: 0.8" workRouteLeakMatch = "\n options:\n temperature: 0.2" workReasoningLeak = "\n options:\n reasoning_effort: \"high\"" ) for _, tc := range []struct { name string yaml string wantErrors []string }{ { name: "plan route stage model must match single_request plan stage", yaml: singleRequestYAMLWithRouteBindingDiff("gemini-review", "ornith-fast", "gemini-review", planOptions, workOptions, reviewOptions, ""), wantErrors: []string{"single_request preset light route stage[0] role=\"plan\" binding must match single_request stage"}, }, { name: "work route stage model must match single_request work stage", yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "gemini-review", "gemini-review", planOptions, workOptions, reviewOptions, ""), wantErrors: []string{"single_request preset light route stage[1] role=\"work\" binding must match single_request stage"}, }, { name: "review route stage model must match single_request review stage", yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "ornith-fast", "gemini-plan", planOptions, workOptions, reviewOptions, ""), wantErrors: []string{"single_request preset light route stage[2] role=\"review\" binding must match single_request stage"}, }, { name: "plan route option mismatch must be rejected", yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "ornith-fast", "gemini-review", planLeakOptions, workOptions, reviewOptions, ""), wantErrors: []string{"single_request preset light route stage[0] role=\"plan\" binding must match single_request stage"}, }, { name: "work route option mismatch must be rejected", yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "ornith-fast", "gemini-review", planOptions, workRouteLeakModel, reviewOptions, workRouteLeakMatch), wantErrors: []string{"single_request preset light route stage[1] role=\"work\" binding must match single_request stage"}, }, { name: "review route option mismatch must be rejected", yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "ornith-fast", "gemini-review", planOptions, workOptions, planLeakOptions, ""), wantErrors: []string{"single_request preset light route stage[2] role=\"review\" binding must match single_request stage"}, }, { name: "route-only dangling model must be rejected", yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "dangling-model-id", "gemini-review", planOptions, workOptions, reviewOptions, ""), wantErrors: []string{"single_request preset light route stage[1] role=\"work\" model", "not found in models catalog"}, }, { name: "route-only work reasoning_effort leakage must be rejected", yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "ornith-fast", "gemini-review", planOptions, workReasoningLeak, reviewOptions, ""), wantErrors: []string{"single_request preset light route stage[1] role=\"work\" binding must match single_request stage"}, }, } { t.Run(tc.name, func(t *testing.T) { requireSingleRequestLoadError(t, f, tc.yaml, tc.wantErrors...) }) } } // TestLoadEdgeSingleRequestExecutionPresetRejectsInvalidShape verifies that // invalid single-request shapes fail closed with descriptive errors. func TestLoadEdgeSingleRequestExecutionPresetRejectsInvalidShape(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") t.Run("empty workspace_ref rejected", func(t *testing.T) { yaml := srYAMLWithWorkspaceRef("", 300000, 120000, 16, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for empty workspace_ref") } if !strings.Contains(err.Error(), "workspace_ref must not be empty") { t.Fatalf("expected error mentioning workspace_ref must not be empty, got %v", err) } }) t.Run("zero wall_clock_ms rejected", func(t *testing.T) { yaml := srYAMLWithWorkspaceRef("ws-ref", 0, 120000, 16, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for zero wall_clock_ms") } if !strings.Contains(err.Error(), "wall_clock_ms must be in") { t.Fatalf("expected error mentioning wall_clock_ms range, got %v", err) } }) t.Run("wall_clock_ms over cap rejected", func(t *testing.T) { yaml := srYAMLWithWorkspaceRef("ws-ref", config.MaxSingleRequestWallClockMS+1, 120000, 16, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for wall_clock_ms over cap") } if !strings.Contains(err.Error(), "wall_clock_ms must be in") { t.Fatalf("expected error mentioning wall_clock_ms range, got %v", err) } }) t.Run("zero timeout_ms rejected", func(t *testing.T) { yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, 0, 16, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for zero timeout_ms") } if !strings.Contains(err.Error(), "timeout_ms must be in") { t.Fatalf("expected error mentioning timeout_ms range, got %v", err) } }) t.Run("timeout_ms over cap rejected", func(t *testing.T) { yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, config.MaxSingleRequestStageTimeoutMS+1, 16, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for timeout_ms over cap") } if !strings.Contains(err.Error(), "timeout_ms must be in") { t.Fatalf("expected error mentioning timeout_ms range, got %v", err) } }) t.Run("timeout_ms greater than wall_clock_ms rejected", func(t *testing.T) { yaml := srYAMLWithWorkspaceRef("ws-ref", 100000, 200000, 16, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for timeout_ms > wall_clock_ms") } if !strings.Contains(err.Error(), "must not exceed wall_clock_ms") { t.Fatalf("expected error mentioning must not exceed wall_clock_ms, got %v", err) } }) t.Run("legacy wall_clock_sec key rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_sec: 1800 timeout_ms: 600000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for legacy wall_clock_sec key") } if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") { t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err) } }) t.Run("legacy stage_timeout_sec key rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 stage_timeout_sec: 1800 timeout_ms: 600000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for legacy stage_timeout_sec key") } if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") { t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err) } if !strings.Contains(err.Error(), "stage_timeout_sec") { t.Fatalf("expected error mentioning stage_timeout_sec, got %v", err) } }) t.Run("zero max_tool_iterations rejected", func(t *testing.T) { yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, 120000, 0, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for zero max_tool_iterations") } if !strings.Contains(err.Error(), "max_tool_iterations must be in") { t.Fatalf("expected error mentioning max_tool_iterations range, got %v", err) } }) t.Run("max_tool_iterations over cap rejected", func(t *testing.T) { yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, 120000, config.MaxSingleRequestToolIterations+1, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for max_tool_iterations over cap") } if !strings.Contains(err.Error(), "max_tool_iterations must be in") { t.Fatalf("expected error mentioning max_tool_iterations range, got %v", err) } }) t.Run("zero max_output_bytes rejected", func(t *testing.T) { yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, 120000, 16, 0) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for zero max_output_bytes") } if !strings.Contains(err.Error(), "max_output_bytes must be in") { t.Fatalf("expected error mentioning max_output_bytes range, got %v", err) } }) t.Run("max_output_bytes over cap rejected", func(t *testing.T) { yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, 120000, 16, config.MaxSingleRequestOutputBytes+1) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for max_output_bytes over cap") } if !strings.Contains(err.Error(), "max_output_bytes must be in") { t.Fatalf("expected error mentioning max_output_bytes range, got %v", err) } }) t.Run("empty plan model rejected", func(t *testing.T) { yaml := srYAMLWithPlanModel("", 300000, 120000, 16, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for empty plan model") } if !strings.Contains(err.Error(), "plan.model must not be empty") { t.Fatalf("expected error mentioning plan.model must not be empty, got %v", err) } }) t.Run("empty work model rejected", func(t *testing.T) { yaml := srYAMLWithWorkModel("", 300000, 120000, 16, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for empty work model") } if !strings.Contains(err.Error(), "work.model must not be empty") { t.Fatalf("expected error mentioning work.model must not be empty, got %v", err) } }) t.Run("empty review model rejected", func(t *testing.T) { yaml := srYAMLWithReviewModel("", 300000, 120000, 16, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for empty review model") } if !strings.Contains(err.Error(), "review.model must not be empty") { t.Fatalf("expected error mentioning review.model must not be empty, got %v", err) } }) t.Run("dangling stage model rejected", func(t *testing.T) { yaml := srYAMLWithWorkModel("non-existent-model", 300000, 120000, 16, 4194304) if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for dangling stage model") } if !strings.Contains(err.Error(), "not found in models catalog") { t.Fatalf("expected error mentioning not found in models catalog, got %v", err) } }) t.Run("divergent route stage model rejected", func(t *testing.T) { // Route work stage model ("gemini-plan") differs from single_request work stage model ("ornith-fast"). yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "gemini-plan" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for divergent route stage model") } if !strings.Contains(err.Error(), "binding must match single_request stage") { t.Fatalf("expected error mentioning binding must match single_request stage, got %v", err) } }) t.Run("divergent route stage options rejected", func(t *testing.T) { // Route work stage has temperature 0.8, single_request work stage has temperature 0.2. yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" options: temperature: 0.8 - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" options: temperature: 0.2 review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for divergent route stage options") } if !strings.Contains(err.Error(), "binding must match single_request stage") { t.Fatalf("expected error mentioning binding must match single_request stage, got %v", err) } }) t.Run("dangling route stage model rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "dangling-model-id" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "dangling-model-id" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for dangling route stage model") } if !strings.Contains(err.Error(), "not found in models catalog") { t.Fatalf("expected error mentioning not found in models catalog, got %v", err) } }) t.Run("divergent selector model rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "ornith-fast" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for divergent selector model") } if !strings.Contains(err.Error(), "selector binding must match single_request plan stage") { t.Fatalf("expected error mentioning selector binding must match single_request plan stage, got %v", err) } }) t.Run("missing high reasoning on selector rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for missing high reasoning on selector") } if !strings.Contains(err.Error(), "selector.options must have reasoning_effort") { t.Fatalf("expected error mentioning selector.options reasoning_effort, got %v", err) } }) t.Run("high reasoning on work stage rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" options: reasoning_effort: "high" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" options: reasoning_effort: "high" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for high reasoning on work stage") } if !strings.Contains(err.Error(), "work.options must not declare reasoning_effort") { t.Fatalf("expected error mentioning work.options must not declare reasoning_effort, got %v", err) } }) t.Run("medium reasoning on work stage rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" options: reasoning_effort: "medium" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" options: reasoning_effort: "medium" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for medium reasoning on work stage") } if !strings.Contains(err.Error(), "work.options must not declare reasoning_effort") { t.Fatalf("expected error mentioning work.options must not declare reasoning_effort, got %v", err) } }) t.Run("non-string reasoning on work stage rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" options: reasoning_effort: 10 - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" options: reasoning_effort: 10 review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for non-string reasoning on work stage") } if !strings.Contains(err.Error(), "work.options must not declare reasoning_effort") { t.Fatalf("expected error mentioning work.options must not declare reasoning_effort, got %v", err) } }) t.Run("missing high reasoning on review rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for missing high reasoning on review") } if !strings.Contains(err.Error(), "review.options must have reasoning_effort") { t.Fatalf("expected error mentioning review.options reasoning_effort, got %v", err) } }) t.Run("non-light allowed mode rejected", func(t *testing.T) { // Use allowed_modes=["direct"] with a direct route; single_request validation // rejects non-light allowed_modes before route checks complete. yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "direct" routes: direct: stages: [] single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for non-light allowed mode") } if !strings.Contains(err.Error(), "allowed_modes must be exactly") { t.Fatalf("expected error mentioning allowed_modes must be exactly, got %v", err) } }) t.Run("direct+light allowed modes rejected", func(t *testing.T) { // allowed_modes=["direct", "light"] with both routes; single_request rejects // because allowed_modes is not exactly ["light"]. yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "direct" - "light" routes: direct: stages: [] light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for direct+light allowed modes") } if !strings.Contains(err.Error(), "allowed_modes must be exactly") { t.Fatalf("expected error mentioning allowed_modes must be exactly, got %v", err) } }) t.Run("workspace_tools with single_request rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" workspace_tools: - name: "ws1" operations: read: tool_name: "cat" schema_matcher: type: "object" argument_map: path: "path" result_matcher: status: "ok" write: tool_name: "tee" creates_parents: true schema_matcher: type: "object" argument_map: path: "path" result_matcher: status: "ok" delete: tool_name: "rm" schema_matcher: type: "object" argument_map: path: "path" result_matcher: status: "ok" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for workspace_tools with single_request") } if !strings.Contains(err.Error(), "workspace_tools") && !strings.Contains(err.Error(), "single_request") { t.Fatalf("expected error mentioning workspace_tools or single_request, got %v", err) } }) t.Run("wrong route stage order rejected", func(t *testing.T) { // Swap plan and review roles in the light route stages only. // The single_request stages still have correct roles, but the route is wrong. yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "review" model: "gemini-review" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for wrong route stage order") } if !strings.Contains(err.Error(), "stage[0] role is") { t.Fatalf("expected error mentioning stage role mismatch, got %v", err) } }) t.Run("extra route stage rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" - role: "extra" model: "gemini-plan" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for extra route stage") } if !strings.Contains(err.Error(), "exactly 3 stages") { t.Fatalf("expected error mentioning exactly 3 stages, got %v", err) } }) t.Run("missing light route rejected", func(t *testing.T) { // No routes section at all; single_request requires a light route. yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: {} single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for missing light route") } if !strings.Contains(err.Error(), "missing route for allowed mode") && !strings.Contains(err.Error(), "must declare a route for mode") { t.Fatalf("expected error mentioning missing route or must declare a route for mode, got %v", err) } }) } // TestLoadEdgeSingleRequestExecutionPresetRejectsUnknownNestedFields verifies // that unknown fields fail strict decode at every new policy boundary (R3). func TestLoadEdgeSingleRequestExecutionPresetRejectsUnknownNestedFields(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") t.Run("unknown field on single_request root rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" unknown_root_field: "unexpected" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for unknown field on single_request root") } if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") { t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err) } if !strings.Contains(err.Error(), "unknown_root_field") { t.Fatalf("expected error mentioning unknown_root_field, got %v", err) } }) t.Run("unknown field on single_request.limits rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 unknown_limit_field: 123 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for unknown field on limits") } if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") { t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err) } if !strings.Contains(err.Error(), "unknown_limit_field") { t.Fatalf("expected error mentioning unknown_limit_field, got %v", err) } }) t.Run("unknown field on single_request.stages rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" unknown_stage_field: {} nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for unknown field on stages") } if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") { t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err) } if !strings.Contains(err.Error(), "unknown_stage_field") { t.Fatalf("expected error mentioning unknown_stage_field, got %v", err) } }) t.Run("unknown field on single_request stage binding rejected", func(t *testing.T) { yaml := `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" unknown_stage_config_field: "invalid" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(f) if err == nil { t.Fatal("expected error for unknown field on stage config") } if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") { t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err) } if !strings.Contains(err.Error(), "unknown_stage_config_field") { t.Fatalf("expected error mentioning unknown_stage_config_field, got %v", err) } }) } // TestCloneExecutionPresetSingleRequestIsolation verifies that deep-cloning a // single-request preset produces an isolated copy: mutating the clone must not // affect the source. func TestCloneExecutionPresetSingleRequestIsolation(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") if err := os.WriteFile(f, []byte(validSingleRequestYAML), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } cfg, err := config.LoadEdge(f) if err != nil { t.Fatalf("load: %v", err) } original := cfg.ExecutionPresets[0] if original.SingleRequest == nil { t.Fatal("expected SingleRequest to be set") } t.Run("clone isolates workspace_ref", func(t *testing.T) { clone := original.Clone() if clone.SingleRequest == nil { t.Fatal("clone: SingleRequest is nil") } clone.SingleRequest.WorkspaceRef = "mutated" if original.SingleRequest.WorkspaceRef == "mutated" { t.Error("source SingleRequest.WorkspaceRef was mutated by clone") } if clone.SingleRequest.WorkspaceRef != "mutated" { t.Error("clone SingleRequest.WorkspaceRef was not set") } }) t.Run("clone isolates limits", func(t *testing.T) { clone := original.Clone() if clone.SingleRequest == nil { t.Fatal("clone: SingleRequest is nil") } clone.SingleRequest.Limits.WallClockMS = 999999 if original.SingleRequest.Limits.WallClockMS == 999999 { t.Error("source SingleRequest.Limits.WallClockMS was mutated by clone") } }) t.Run("clone isolates stage plan options", func(t *testing.T) { clone := original.Clone() if clone.SingleRequest == nil { t.Fatal("clone: SingleRequest is nil") } clone.SingleRequest.Stages.Plan.Options["reasoning_effort"] = "mutated" origVal, _ := original.SingleRequest.Stages.Plan.Options["reasoning_effort"] if origVal == "mutated" { t.Error("source plan options were mutated by clone") } cloneVal, _ := clone.SingleRequest.Stages.Plan.Options["reasoning_effort"] if cloneVal != "mutated" { t.Error("clone plan options were not set") } }) t.Run("clone isolates stage work options", func(t *testing.T) { clone := original.Clone() if clone.SingleRequest == nil { t.Fatal("clone: SingleRequest is nil") } clone.SingleRequest.Stages.Work.Options["temperature"] = 99.0 origVal, hasOrig := original.SingleRequest.Stages.Work.Options["temperature"] if hasOrig && origVal == 99.0 { t.Error("source work options were mutated by clone") } cloneVal, hasClone := clone.SingleRequest.Stages.Work.Options["temperature"] if !hasClone || cloneVal != 99.0 { t.Error("clone work options were not set") } }) t.Run("clone isolates stage review options", func(t *testing.T) { clone := original.Clone() if clone.SingleRequest == nil { t.Fatal("clone: SingleRequest is nil") } clone.SingleRequest.Stages.Review.Options["max_retries"] = 99 origVal, hasOrig := original.SingleRequest.Stages.Review.Options["max_retries"] if hasOrig && origVal == 99 { t.Error("source review options were mutated by clone") } cloneVal, hasClone := clone.SingleRequest.Stages.Review.Options["max_retries"] if !hasClone || cloneVal != 99 { t.Error("clone review options were not set") } }) t.Run("clone isolates route stages", func(t *testing.T) { clone := original.Clone() if len(clone.Routes["light"].Stages) != 3 { t.Fatalf("clone route stages = %d, want 3", len(clone.Routes["light"].Stages)) } clone.Routes["light"].Stages[0].Model = "mutated-model" if original.Routes["light"].Stages[0].Model == "mutated-model" { t.Error("source route stages were mutated by clone") } if clone.Routes["light"].Stages[0].Model != "mutated-model" { t.Error("clone route stages were not set") } }) t.Run("clone isolates allowed_modes", func(t *testing.T) { clone := original.Clone() clone.AllowedModes[0] = "mutated" if original.AllowedModes[0] == "mutated" { t.Error("source AllowedModes was mutated by clone") } }) t.Run("nil SingleRequest clone returns nil", func(t *testing.T) { noSR := original noSR.SingleRequest = nil clone := noSR.Clone() if clone.SingleRequest != nil { t.Error("expected nil SingleRequest on clone of preset without SingleRequest") } }) t.Run("CloneExecutionPresetCatalog isolates single-request presets", func(t *testing.T) { catalog := config.CloneExecutionPresetCatalog(cfg.ExecutionPresets) if len(catalog) != 1 { t.Fatalf("expected 1 preset in catalog, got %d", len(catalog)) } clone := catalog[0] if clone.SingleRequest == nil { t.Fatal("clone catalog: SingleRequest is nil") } clone.SingleRequest.Limits.MaxToolIterations = 0 if cfg.ExecutionPresets[0].SingleRequest.Limits.MaxToolIterations == 0 { t.Error("source catalog preset was mutated by clone") } }) } // routeStagesRoles extracts role names from a slice of ExecutionRouteStage. func routeStagesRoles(stages []config.ExecutionRouteStage) []string { roles := make([]string, len(stages)) for i, s := range stages { roles[i] = s.Role } return roles } func requireSingleRequestLoadError(t *testing.T, path, yaml string, wantFragments ...string) { t.Helper() if err := os.WriteFile(path, []byte(yaml), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } _, err := config.LoadEdge(path) if err == nil { t.Fatal("expected error for malformed single-request binding") } for _, want := range wantFragments { if !strings.Contains(err.Error(), want) { t.Fatalf("expected error mentioning %q, got %v", want, err) } } } func singleRequestYAMLWithRouteBindingDiff( routePlanModel, routeWorkModel, routeReviewModel string, routePlanOptions, routeWorkOptions, routeReviewOptions, singleWorkOptions string, ) string { return `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "` + routePlanModel + `"` + routePlanOptions + ` - role: "work" model: "` + routeWorkModel + `"` + routeWorkOptions + ` - role: "review" model: "` + routeReviewModel + `"` + routeReviewOptions + ` single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: 300000 timeout_ms: 120000 max_tool_iterations: 16 max_output_bytes: 4194304 stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast"` + singleWorkOptions + ` review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` } // srYAMLWithWorkspaceRef produces a valid single-request preset YAML with the // given workspace_ref and limits, using standard plan/work/review stage bindings. func srYAMLWithWorkspaceRef(workspaceRef string, wallClock, stageTimeout, toolIters, outputBytes int) string { return `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "` + workspaceRef + `" limits: wall_clock_ms: ` + itoa(wallClock) + ` timeout_ms: ` + itoa(stageTimeout) + ` max_tool_iterations: ` + itoa(toolIters) + ` max_output_bytes: ` + itoa(outputBytes) + ` stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` } // srYAMLWithPlanModel produces a valid single-request preset YAML with the // given plan stage model and standard limits. func srYAMLWithPlanModel(planModel string, wallClock, stageTimeout, toolIters, outputBytes int) string { return `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: ` + itoa(wallClock) + ` timeout_ms: ` + itoa(stageTimeout) + ` max_tool_iterations: ` + itoa(toolIters) + ` max_output_bytes: ` + itoa(outputBytes) + ` stages: plan: model: "` + planModel + `" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` } // srYAMLWithWorkModel produces a valid single-request preset YAML with the // given work stage model and standard limits. func srYAMLWithWorkModel(workModel string, wallClock, stageTimeout, toolIters, outputBytes int) string { return `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "` + workModel + `" - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: ` + itoa(wallClock) + ` timeout_ms: ` + itoa(stageTimeout) + ` max_tool_iterations: ` + itoa(toolIters) + ` max_output_bytes: ` + itoa(outputBytes) + ` stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "` + workModel + `" review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` } // srYAMLWithReviewModel produces a valid single-request preset YAML with the // given review stage model and standard limits. func srYAMLWithReviewModel(reviewModel string, wallClock, stageTimeout, toolIters, outputBytes int) string { return `server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" - role: "review" model: "` + reviewModel + `" options: reasoning_effort: "high" single_request: workspace_ref: "ws-ref" limits: wall_clock_ms: ` + itoa(wallClock) + ` timeout_ms: ` + itoa(stageTimeout) + ` max_tool_iterations: ` + itoa(toolIters) + ` max_output_bytes: ` + itoa(outputBytes) + ` stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" review: model: "` + reviewModel + `" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` } // singleRequestYAMLWithLimits produces a valid single-request preset YAML with // the given limits and the standard plan/work/review stage bindings. func singleRequestYAMLWithLimits(wallClock, stageTimeout, toolIters, outputBytes int) string { return ` server: listen: "0.0.0.0:9090" models: - id: "gemini-plan" providers: prov-a: "gemini-plan" - id: "ornith-fast" providers: prov-a: "ornith-fast" - id: "gemini-review" providers: prov-a: "gemini-review" execution_presets: - id: "fixed-single-request" selector: model: "gemini-plan" options: reasoning_effort: "high" allowed_modes: - "light" routes: light: stages: - role: "plan" model: "gemini-plan" options: reasoning_effort: "high" - role: "work" model: "ornith-fast" options: temperature: 0.2 - role: "review" model: "gemini-review" options: reasoning_effort: "high" single_request: workspace_ref: "ws-capability-ref-001" limits: wall_clock_ms: ` + itoa(wallClock) + ` timeout_ms: ` + itoa(stageTimeout) + ` max_tool_iterations: ` + itoa(toolIters) + ` max_output_bytes: ` + itoa(outputBytes) + ` stages: plan: model: "gemini-plan" options: reasoning_effort: "high" work: model: "ornith-fast" options: temperature: 0.2 review: model: "gemini-review" options: reasoning_effort: "high" nodes: - id: "node-01" providers: - id: "prov-a" type: "ollama" category: "local_inference" models: ["gemini-plan", "ornith-fast", "gemini-review"] capacity: 2 ` } func itoa(v int) string { return fmt.Sprintf("%d", v) }