-
Notifications
You must be signed in to change notification settings - Fork 295
Phase 1: Introduce EngineDefinition, EngineCatalog, and ResolvedEngineTarget #20459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -98,6 +98,7 @@ type Compiler struct { | |
| actionTag string // Override action SHA or tag for actions/setup (when set, overrides actionMode to release) | ||
| jobManager *JobManager // Manages jobs and dependencies | ||
| engineRegistry *EngineRegistry // Registry of available agentic engines | ||
| engineCatalog *EngineCatalog // Catalog of engine definitions backed by the registry | ||
| fileTracker FileTracker // Optional file tracker for tracking created files | ||
| warningCount int // Number of warnings encountered during compilation | ||
| stepOrderTracker *StepOrderTracker // Tracks step ordering for validation | ||
|
|
@@ -136,6 +137,7 @@ func NewCompiler(opts ...CompilerOption) *Compiler { | |
| actionMode: DetectActionMode(version), // Auto-detect action mode based on version | ||
| jobManager: NewJobManager(), | ||
| engineRegistry: GetGlobalEngineRegistry(), | ||
| engineCatalog: NewEngineCatalog(GetGlobalEngineRegistry()), | ||
| stepOrderTracker: NewStepOrderTracker(), | ||
|
Comment on lines
138
to
141
|
||
| artifactManager: NewArtifactManager(), | ||
| actionPinWarnings: make(map[string]bool), // Initialize warning cache | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,185 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This file defines the engine definition layer: declarative metadata types for AI engines, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // a catalog of registered definitions, and a resolved target that combines definition, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // config, and runtime adapter. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // # Architecture | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The engine definition layer sits on top of the existing EngineRegistry runtime layer: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // EngineDefinition – declarative metadata for a single engine entry | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // EngineCatalog – registry of EngineDefinition entries with a Resolve() method | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ResolvedEngineTarget – result of resolving an engine ID: definition + config + runtime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The existing EngineRegistry and CodingAgentEngine interfaces are unchanged; the catalog | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // is an additional layer that maps logical engine IDs to runtime adapters. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // # Built-in Engines | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // NewEngineCatalog registers the four built-in engines: claude, codex, copilot, gemini. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Each EngineDefinition carries the engine's RuntimeID which maps to the corresponding | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // CodingAgentEngine registered in the EngineRegistry. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // # Resolve() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // EngineCatalog.Resolve() performs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1. Exact catalog ID lookup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 2. Runtime-ID prefix fallback (for backward compat, e.g. "codex-experimental") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 3. Formatted validation error when engine is unknown | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package workflow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "strings" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/github/gh-aw/pkg/constants" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/github/gh-aw/pkg/parser" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ProviderSelection identifies the AI provider for an engine (e.g. "anthropic", "openai"). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type ProviderSelection struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Name string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ModelSelection specifies the default and supported models for an engine. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type ModelSelection struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Default string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Supported []string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // AuthBinding maps a logical authentication role to a secret name. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type AuthBinding struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Role string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Secret string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // EngineDefinition holds the declarative metadata for an AI engine. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // It is separate from the runtime adapter (CodingAgentEngine) to allow the catalog | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // layer to carry identity and provider information without coupling to implementation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type EngineDefinition struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ID string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DisplayName string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Description string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RuntimeID string // maps to the CodingAgentEngine registered in EngineRegistry | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Provider ProviderSelection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Models ModelSelection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Auth []AuthBinding | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Options map[string]any | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // EngineCatalog is a collection of EngineDefinition entries backed by an EngineRegistry | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // for runtime adapter resolution. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type EngineCatalog struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| definitions map[string]*EngineDefinition | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| registry *EngineRegistry | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ResolvedEngineTarget is the result of resolving an engine ID through the catalog. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // It combines the EngineDefinition, the caller-supplied EngineConfig, and the resolved | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // CodingAgentEngine runtime adapter. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type ResolvedEngineTarget struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Definition *EngineDefinition | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Config *EngineConfig // resolved merged config supplied by the caller | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Runtime CodingAgentEngine // resolved adapter from the EngineRegistry | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // NewEngineCatalog creates an EngineCatalog that wraps the given EngineRegistry and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // pre-registers the four built-in engine definitions (claude, codex, copilot, gemini). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func NewEngineCatalog(registry *EngineRegistry) *EngineCatalog { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catalog := &EngineCatalog{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| definitions: make(map[string]*EngineDefinition), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| registry: registry, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catalog.Register(&EngineDefinition{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ID: "claude", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DisplayName: "Claude Code", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Description: "Uses Claude Code with full MCP tool support and allow-listing", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RuntimeID: "claude", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Provider: ProviderSelection{Name: "anthropic"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Auth: []AuthBinding{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {Role: "api-key", Secret: "ANTHROPIC_API_KEY"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catalog.Register(&EngineDefinition{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ID: "codex", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DisplayName: "Codex", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Description: "Uses OpenAI Codex CLI with MCP server support", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RuntimeID: "codex", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Provider: ProviderSelection{Name: "openai"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Auth: []AuthBinding{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {Role: "api-key", Secret: "CODEX_API_KEY"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catalog.Register(&EngineDefinition{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ID: "copilot", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DisplayName: "GitHub Copilot CLI", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Description: "Uses GitHub Copilot CLI with MCP server support", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RuntimeID: "copilot", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Provider: ProviderSelection{Name: "github"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catalog.Register(&EngineDefinition{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ID: "gemini", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DisplayName: "Google Gemini CLI", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Description: "Google Gemini CLI with headless mode and LLM gateway support", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RuntimeID: "gemini", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Provider: ProviderSelection{Name: "google"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return catalog | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Register adds or replaces an EngineDefinition in the catalog. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (c *EngineCatalog) Register(def *EngineDefinition) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (c *EngineCatalog) Register(def *EngineDefinition) { | |
| func (c *EngineCatalog) Register(def *EngineDefinition) { | |
| if def == nil { | |
| panic("workflow: cannot register nil EngineDefinition") | |
| } | |
| if strings.TrimSpace(def.ID) == "" { | |
| panic("workflow: cannot register EngineDefinition with empty ID") | |
| } |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The prefix fallback delegates to EngineRegistry.GetEngineByPrefix(), which iterates a map and returns the first matching engine. This makes resolution nondeterministic for ambiguous inputs (e.g. "c" matches claude/codex/copilot depending on map iteration order) and can accidentally accept unintended prefixes. Consider making fallback deterministic and stricter (e.g. only allow IDs of the form "-..." and/or prefer the longest matching runtime ID after sorting).
| // Fall back to runtime-ID prefix lookup for backward compat (e.g. "codex-experimental") | |
| runtime, err := c.registry.GetEngineByPrefix(id) | |
| if err == nil { | |
| def := &EngineDefinition{ | |
| ID: id, | |
| DisplayName: runtime.GetDisplayName(), | |
| Description: runtime.GetDescription(), | |
| RuntimeID: runtime.GetID(), | |
| } | |
| return &ResolvedEngineTarget{Definition: def, Config: config, Runtime: runtime}, nil | |
| } | |
| // Fall back to deterministic runtime-ID prefix lookup for backward compat (e.g. "codex-experimental"). | |
| // We only accept IDs of the form "<known-runtime-id>-..." and prefer the longest matching runtime ID. | |
| supported := c.registry.GetSupportedEngines() | |
| longestMatch := "" | |
| for _, runtimeID := range supported { | |
| if len(id) <= len(runtimeID) { | |
| continue | |
| } | |
| if strings.HasPrefix(id, runtimeID) && id[len(runtimeID)] == '-' { | |
| if len(runtimeID) > len(longestMatch) { | |
| longestMatch = runtimeID | |
| } | |
| } | |
| } | |
| if longestMatch != "" { | |
| runtime, err := c.registry.GetEngine(longestMatch) | |
| if err == nil { | |
| def := &EngineDefinition{ | |
| ID: id, | |
| DisplayName: runtime.GetDisplayName(), | |
| Description: runtime.GetDescription(), | |
| RuntimeID: runtime.GetID(), | |
| } | |
| return &ResolvedEngineTarget{Definition: def, Config: config, Runtime: runtime}, nil | |
| } | |
| } |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolve() builds the “Valid engines are …” list from EngineRegistry.GetSupportedEngines() (runtime IDs). That omits catalog IDs for custom EngineDefinition entries (e.g. a user registers ID "my-custom-engine" mapping to runtime "copilot"), so the error message and closest-match suggestions will be misleading/incomplete. Build the valid engine list from catalog definition IDs (and optionally include runtime IDs used for legacy prefix fallback) so errors reflect what Resolve() actually accepts.
| validEngines := c.registry.GetSupportedEngines() | |
| // Build the list of valid engines from catalog definition IDs (what Resolve() accepts) | |
| // and optionally include runtime IDs used for legacy prefix fallback. | |
| validSet := make(map[string]struct{}) | |
| // Include all catalog definition IDs. | |
| for defID := range c.definitions { | |
| validSet[defID] = struct{}{} | |
| } | |
| // Include runtime IDs from the registry to reflect legacy/prefix-based resolution. | |
| for _, runtimeID := range c.registry.GetSupportedEngines() { | |
| validSet[runtimeID] = struct{}{} | |
| } | |
| // Flatten the set into a slice for suggestions and display. | |
| validEngines := make([]string, 0, len(validSet)) | |
| for engineID := range validSet { | |
| validEngines = append(validEngines, engineID) | |
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,130 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //go:build !integration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package workflow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "testing" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/github/gh-aw/pkg/constants" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/stretchr/testify/assert" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/stretchr/testify/require" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestNewEngineCatalog_BuiltIns checks that all four built-in engines are registered | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // and resolve to the expected runtime adapters. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestNewEngineCatalog_BuiltIns(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| registry := NewEngineRegistry() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catalog := NewEngineCatalog(registry) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tests := []struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| engineID string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| displayName string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| provider string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {"claude", "Claude Code", "anthropic"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {"codex", "Codex", "openai"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {"copilot", "GitHub Copilot CLI", "github"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {"gemini", "Google Gemini CLI", "google"}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, tt := range tests { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Run(tt.engineID, func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resolved, err := catalog.Resolve(tt.engineID, &EngineConfig{ID: tt.engineID}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require.NoError(t, err, "expected %s to resolve without error", tt.engineID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require.NotNil(t, resolved, "expected non-nil ResolvedEngineTarget for %s", tt.engineID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert.Equal(t, tt.engineID, resolved.Definition.ID, "Definition.ID should match") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert.Equal(t, tt.displayName, resolved.Definition.DisplayName, "Definition.DisplayName should match") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert.Equal(t, tt.provider, resolved.Definition.Provider.Name, "Definition.Provider.Name should match") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert.Equal(t, tt.engineID, resolved.Runtime.GetID(), "Runtime.GetID() should match engine ID") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestEngineCatalog_Resolve_LegacyStringFormat verifies that resolving via plain string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ("engine: claude") and object ID format ("engine.id: claude") produce the same runtime. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestEngineCatalog_Resolve_LegacyStringFormat(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| registry := NewEngineRegistry() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catalog := NewEngineCatalog(registry) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Simulate "engine: claude" — EngineConfig built from string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stringConfig := &EngineConfig{ID: "claude"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resolvedString, err := catalog.Resolve("claude", stringConfig) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require.NoError(t, err, "string-format engine should resolve without error") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Simulate "engine:\n id: claude" — same logical ID | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| objectConfig := &EngineConfig{ID: "claude"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resolvedObject, err := catalog.Resolve("claude", objectConfig) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require.NoError(t, err, "object-format engine should resolve without error") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert.Equal(t, resolvedString.Runtime.GetID(), resolvedObject.Runtime.GetID(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "both formats should resolve to the same runtime adapter") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert.Equal(t, resolvedString.Definition.ID, resolvedObject.Definition.ID, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "both formats should resolve to the same definition") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestEngineCatalog_Resolve_PrefixFallback verifies backward-compat prefix matching | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (e.g. "codex-experimental" should resolve to the codex runtime). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestEngineCatalog_Resolve_PrefixFallback(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+44
to
+68
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestEngineCatalog_Resolve_LegacyStringFormat verifies that resolving via plain string | |
| // ("engine: claude") and object ID format ("engine.id: claude") produce the same runtime. | |
| func TestEngineCatalog_Resolve_LegacyStringFormat(t *testing.T) { | |
| registry := NewEngineRegistry() | |
| catalog := NewEngineCatalog(registry) | |
| // Simulate "engine: claude" — EngineConfig built from string | |
| stringConfig := &EngineConfig{ID: "claude"} | |
| resolvedString, err := catalog.Resolve("claude", stringConfig) | |
| require.NoError(t, err, "string-format engine should resolve without error") | |
| // Simulate "engine:\n id: claude" — same logical ID | |
| objectConfig := &EngineConfig{ID: "claude"} | |
| resolvedObject, err := catalog.Resolve("claude", objectConfig) | |
| require.NoError(t, err, "object-format engine should resolve without error") | |
| assert.Equal(t, resolvedString.Runtime.GetID(), resolvedObject.Runtime.GetID(), | |
| "both formats should resolve to the same runtime adapter") | |
| assert.Equal(t, resolvedString.Definition.ID, resolvedObject.Definition.ID, | |
| "both formats should resolve to the same definition") | |
| } | |
| // TestEngineCatalog_Resolve_PrefixFallback verifies backward-compat prefix matching | |
| // (e.g. "codex-experimental" should resolve to the codex runtime). | |
| func TestEngineCatalog_Resolve_PrefixFallback(t *testing.T) { | |
| // TestEngineCatalog_Resolve_PrefixFallback verifies backward-compat prefix matching | |
| // (e.g. "codex-experimental" should resolve to the codex runtime). | |
| func TestEngineCatalog_Resolve_PrefixFallback(t *testing.T) { | |
| registry := NewEngineRegistry() | |
| catalog := NewEngineCatalog(registry) | |
| resolved, err := catalog.Resolve("codex-experimental", &EngineConfig{ID: "codex-experimental"}) | |
| require.NoError(t, err, "prefix-matched engine should resolve without error") | |
| require.NotNil(t, resolved, "expected non-nil ResolvedEngineTarget for prefix match") | |
| assert.Equal(t, "codex", resolved.Runtime.GetID(), "prefix match should resolve to codex runtime") | |
| } | |
| // TestEngineCatalog_Resolve_UnknownEngine verifies that unknown engine IDs return a | |
| [...] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After Resolve(), downstream logic continues to use engineSetting (the user-provided ID) for engine-specific behavior. With prefix fallback (e.g. "codex-experimental"), engineSetting won’t equal the canonical runtime ID ("codex"), and functions like enableFirewallByDefaultForCopilot/firewall.go:100-104 won’t run as intended. Consider capturing a canonicalEngineID := resolvedEngine.Runtime.GetID() (or resolvedEngine.Definition.RuntimeID) and using that for subsequent engine-ID comparisons/feature gates, while keeping engineSetting for display/logging.