Skip to content

Phase 1: Introduce EngineDefinition, EngineCatalog, and ResolvedEngineTarget#20459

Merged
pelikhan merged 2 commits intomainfrom
copilot/add-engine-definition-types
Mar 11, 2026
Merged

Phase 1: Introduce EngineDefinition, EngineCatalog, and ResolvedEngineTarget#20459
pelikhan merged 2 commits intomainfrom
copilot/add-engine-definition-types

Conversation

Copy link
Contributor

Copilot AI commented Mar 11, 2026

The engine system conflates runtime adapter code with engine identity metadata, and engine lists are duplicated across multiple files. This is the foundational phase to address that by introducing a declarative catalog layer on top of the existing EngineRegistry.

New types (engine_definition.go)

  • EngineDefinition — declarative metadata (ID, DisplayName, RuntimeID, Provider, Models, Auth, Options) decoupled from the runtime adapter
  • EngineCatalog — holds EngineDefinition entries, backed by *EngineRegistry for runtime resolution
  • ResolvedEngineTarget — result of a single Resolve() call: definition + caller config + CodingAgentEngine runtime
  • Supporting types: ProviderSelection, ModelSelection, AuthBinding

Built-in registrations

NewEngineCatalog(registry) pre-registers claude, codex, copilot, and gemini. Each entry carries provider identity and auth bindings alongside the RuntimeID that maps to the existing registry adapter.

Resolution

Resolve(id, config) replaces the two-step validateEngine + getAgenticEngine pattern used in the orchestrator:

// Before
if err := c.validateEngine(engineSetting); err != nil { ... }
agenticEngine, err := c.getAgenticEngine(engineSetting)

// After
resolvedEngine, err := c.engineCatalog.Resolve(engineSetting, engineConfig)
agenticEngine := resolvedEngine.Runtime

Resolution order: exact catalog ID → prefix fallback (backward compat, e.g. codex-experimental) → formatted validation error (matching existing validateEngine message format including docs URL and "did you mean" suggestion).

Compiler wiring

  • Compiler gains an engineCatalog *EngineCatalog field, initialized in NewCompiler() alongside engineRegistry
  • compiler_orchestrator_engine.go uses a single catalog.Resolve() call; validateEngine and getAgenticEngine remain available for other call sites

Tests (engine_definition_test.go)

Six regression tests covering: all four built-ins, string vs. object-ID format parity, prefix fallback, unknown-engine error format (ID, valid engines list, docs URL, example), config passthrough, and custom definition registration.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw main -lang=go1.25 git rev-�� --show-toplevel -dwarf=false /usr/bin/git go1.25.0 -c=4 -nolocalimports git (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw -ifaceassert -nilfunc git rev-�� --show-toplevel -tests (http block)
  • https://api.github.com/repos/actions/ai-inference/git/ref/tags/v1
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha GOMODCACHE go /usr/bin/git '**/*.ts' '**/*.git GO111MODULE ache/go/1.25.0/x--show-toplevel git -C /tmp/gh-aw-test-runs/20260311-031408-41417/test-3533784361 status /usr/bin/git .github/workflowgit GO111MODULE 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha it/ref/tags/v7 git /usr/bin/git --show-toplevel x_amd64/vet /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-toplevel go /usr/bin/git git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha -json GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v3
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha -json GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE om/testorg/testrepo.git GOINSECURE GOMOD GOMODCACHE git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha --show-toplevel /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/link /usr/bin/git /tmp/go-build424git -importcfg /usr/bin/git git comm�� -m Update initial file /usr/bin/git --show-toplevel -extld=gcc /usr/bin/git git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node /hom�� --check **/*.cjs ache/go/1.25.0/x64/bin/go **/*.json --ignore-path ../../../.pretti--show-toplevel go (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v5
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha it/ref/tags/v7 go /usr/bin/git -json GO111MODULE 64/bin/go git rev-�� --show-toplevel go /usr/bin/git -json GO111MODULE 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel sh /usr/bin/git "prettier" --chegit GOPROXY 64/pkg/tool/linu--show-toplevel git rev-�� --show-toplevel 64/pkg/tool/linux_amd64/compile /usr/bin/git _.a GO111MODULE ache/go/1.25.0/x--show-toplevel git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha GOMODCACHE go /usr/bin/git -json GO111MODULE 86_64/node git conf�� --get remote.origin.url /usr/bin/git -json GO111MODULE h git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha GOMODCACHE (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --show-toplevel go /usr/bin/git -json GO111MODULE 64/bin/go git rev-�� --show-toplevel node /usr/bin/git --check **/*.cjs ache/go/1.25.0/x--show-toplevel git (http block)
  • https://api.github.com/repos/actions/download-artifact/git/ref/tags/v8
    • Triggering command: /usr/bin/gh gh api /repos/actions/download-artifact/git/ref/tags/v8 --jq .object.sha "prettier" --check 'scripts/**/*.js' --ignore-paGOSUMDB go ache/go/1.25.0/x64/bin/go -json GO111MODULE 64/bin/go go env -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/download-artifact/git/ref/tags/v8 --jq .object.sha agent-performance-analyzer.md GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE rtcfg env g/constants/constants.go g/constants/constants_test.go ache/go/1.25.0/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/x64/pkg/tool/linux_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/download-artifact/git/ref/tags/v8 --jq .object.sha lidations passed" GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json resolved$ /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v8
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE bin/sh GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha go1.25.0 -c=4 -nolocalimports -importcfg /tmp/go-build424305229/b389/importcfg -pack /tmp/go-build424305229/b389/_testmain.go env -json GO111MODULE ules/.bin/node GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha -c=4 -nolocalimports -importcfg /tmp/go-build424305229/b394/importcfg -pack /home/REDACTED/work/gh-aw/gh-aw/pkg/envutil/envutil.go /home/REDACTED/work/gh-aw/gh-aw/pkg/envutil/envutil_test.go env -json GO111MODULE bin/node GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha /tmp/gh-aw-test-runs/20260311-031408-41417/test-1192408095/.github/workflows rev-parse /usr/bin/git -json GO111MODULE 0/x64/bin/node git init�� igFiles,SwigCXXF-json go /usr/bin/git -json GO111MODULE n-dir/sh git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel go /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-toplevel x_amd64/vet /usr/bin/git git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env it/ref/tags/v7 GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha --get remote.origin.url ache/node/24.14.0/x64/bin/node -json GO111MODULE ache/go/1.25.0/x--show-toplevel git t-11�� k/gh-aw/gh-aw/.github/workflows/bot-detection.md remote.origin.url /usr/lib/git-core/git -json GO111MODULE ache/go/1.25.0/x--show-toplevel /usr/lib/git-core/git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha --show-toplevel git /usr/bin/git pload-artifact/ggit go /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git pload-artifact/ggit x_amd64/cgo /usr/bin/git git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha -json GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 305229/b363/vet.cfg GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha --show-toplevel (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v7
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha -json GO111MODULE x_amd64/link GOINSECURE GOMOD GOMODCACHE x_amd64/link /opt�� prettier --check 64/pkg/tool/linux_amd64/compile --ignore-path .prettierignore 64/bin/go oJ/BhqTCoRMGewfss9ZXZGY/X4XoDkfiiEtxJ64HjgrP (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha npx prettier --check '**/*.cjs' '**/*.ts' '**/*.GOINSECURE GOPROXY ache/go/1.25.0/x64/bin/go GOSUMDB GOWORK 64/bin/go sh -c "prettier" --check 'scripts/**/*.js' --ignore-paremote.origin.url go ache/go/1.25.0/x64/bin/go tierignore GO111MODULE 64/bin/go go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha npx prettier --check '**/*.cjs' '**/*.ts' '**/*.GOINSECURE GOPROXY 64/pkg/tool/linux_amd64/cgo GOSUMDB GOWORK 64/bin/go 64/pkg/tool/linux_amd64/cgo -c "prettier" --check 'scripts/**/*.js' --ignore-paGOSUMDB go ache/go/1.25.0/x64/bin/go tierignore GO111MODULE 64/bin/go go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/1/artifacts
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 GO111MODULE x_amd64/link GOINSECURE GOMOD GOMODCACHE x_amd64/link env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE IW/AO37iMX7KsmcgKCt3iLM/_L7oVK4BUjzHwfVA7Wgi (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12345/artifacts
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xtest@example.com stlo�� 9815135/b417/_pkgo1.25.0 GO111MODULE 64/bin/go GOINSECURE b/gh-aw/pkg/workrev-parse GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12346/artifacts
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 g/workflow/importable_tools_testGOWORK x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env 9815135/b418/_pkg_.a GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/2/artifacts
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go stlo�� -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/3/artifacts
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go stlo�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/4/artifacts
    • Triggering command: /usr/bin/gh gh run download 4 --dir test-logs/run-4 GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/5/artifacts
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go stlo�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/go/1.25.0/xGOMODCACHE GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha ck 'scripts/**/*.js' --ignore-pa-p GO111MODULE datedAt,event,headBranch,headSha,displayTitle GOINSECURE GOMOD GOMODCACHE go env 3022621880/.github/workflows GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha --show-toplevel go /usr/bin/git 1408-41417/test-git GO111MODULE eutil.test git 0/x6�� --show-toplevel eutil.test /usr/bin/git -json GO111MODULE 0/x64/bin/node git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/node GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.2.3 --jq .object.sha -json GO111MODULE 0/x64/lib/node_mGOMODCACHE GOINSECURE GOMOD GOMODCACHE go env */*.ts' '**/*.js- GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linuconfig (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v2.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE tions/setup/nodeGOMODCACHE GOINSECURE GOMOD GOMODCACHE go env 398e7e1acf3bc855GOSUMDB GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE 0/x64/bin/sh GOINSECURE GOMOD GOMODCACHE go env */*.ts' '**/*.js-errorsas GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE ndor/bin/sh GOINSECURE GOMOD GOMODCACHE go env */*.ts' '**/*.js-errorsas GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v3.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v3.0.0 --jq .object.sha -json GO111MODULE ache/go/1.25.0/xGOMODCACHE GOINSECURE GOMOD GOMODCACHE go env */*.ts' '**/*.js-errorsas GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/nonexistent/action/git/ref/tags/v999.999.999
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha ck 'scripts/**/*--detach GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (http block)
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha --show-toplevel go 0/x64/bin/node -json GO111MODULE 64/pkg/tool/linu--show-toplevel git 0/x6�� --show-toplevel 64/pkg/tool/linux_amd64/compile /usr/bin/git _.a GO111MODULE ache/go/1.25.0/x--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE tions/setup/js/nGOMODCACHE GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/nonexistent/repo/actions/runs/12345
    • Triggering command: /usr/bin/gh gh run view 12345 --repo nonexistent/repo --json status,conclusion GOINSECURE GOMOD erignore go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/owner/repo/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ode GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE n-dir/node GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/owner/repo/contents/file.md
    • Triggering command: /tmp/go-build424305229/b383/cli.test /tmp/go-build424305229/b383/cli.test -test.testlogfile=/tmp/go-build424305229/b383/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/test-owner/test-repo/actions/secrets
    • Triggering command: /usr/bin/gh gh api /repos/test-owner/test-repo/actions/secrets --jq .secrets[].name GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/go/1.25.0/xGOMODCACHE GOINSECURE GOMOD GOMODCACHE go (http block)

If you need me to access, download, or install something from one of these locations, you can either:


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…rget (Phase 1)

- Create engine_definition.go with EngineDefinition, EngineCatalog,
  ResolvedEngineTarget, ProviderSelection, ModelSelection, AuthBinding types
- NewEngineCatalog registers claude/codex/copilot/gemini as built-in entries
- EngineCatalog.Resolve() handles exact lookup, prefix fallback, and
  formatted validation errors matching the existing validateEngine style
- Add engineCatalog field to Compiler, initialized in NewCompiler()
- Update compiler_orchestrator_engine.go to use catalog.Resolve() instead
  of separate validateEngine + getAgenticEngine calls
- Add engine_definition_test.go with 6 regression tests covering all
  built-ins, legacy string/object formats, prefix fallback, unknown engine
  errors (including docs URL validation), config passthrough, custom registration

Closes #20416 Phase 1

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Introduce EngineDefinition, EngineCatalog, and ResolvedEngineTarget Phase 1: Introduce EngineDefinition, EngineCatalog, and ResolvedEngineTarget Mar 11, 2026
@pelikhan pelikhan marked this pull request as ready for review March 11, 2026 03:38
Copilot AI review requested due to automatic review settings March 11, 2026 03:38
@pelikhan pelikhan merged commit a185bcf into main Mar 11, 2026
111 checks passed
@pelikhan pelikhan deleted the copilot/add-engine-definition-types branch March 11, 2026 03:42
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces an “engine definition” layer on top of the existing runtime EngineRegistry, enabling resolution of logical engine IDs to runtime adapters with improved metadata and backward-compatible prefix fallback.

Changes:

  • Added EngineDefinition, EngineCatalog, and ResolvedEngineTarget types with built-in engine registrations and a Resolve() API.
  • Wired the compiler to use EngineCatalog.Resolve() during orchestrator engine setup instead of separate validate/get calls.
  • Added unit tests covering catalog built-ins, prefix fallback, unknown-engine errors, config passthrough, and custom registration.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
pkg/workflow/engine_definition.go New engine-definition/catalog layer and resolution logic.
pkg/workflow/engine_definition_test.go Unit tests for catalog registration and resolution behavior.
pkg/workflow/compiler_types.go Adds engineCatalog to Compiler and initializes it in NewCompiler.
pkg/workflow/compiler_orchestrator_engine.go Switches orchestrator engine setup to resolve engine via the catalog.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

}

// Register adds or replaces an EngineDefinition in the catalog.
func (c *EngineCatalog) Register(def *EngineDefinition) {
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EngineCatalog.Register will panic if called with a nil definition (def.ID dereference) and also silently accepts empty IDs. Since this is a new exported API, consider validating inputs (e.g., return an error, or at least guard and panic with a clear message) to avoid hard-to-debug nil pointer crashes or unusable catalog entries.

Suggested change
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 uses AI. Check for mistakes.
}

// Engine not found — produce a helpful validation error matching the existing format
validEngines := c.registry.GetSupportedEngines()
Copy link

Copilot AI Mar 11, 2026

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.

Suggested change
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)
}

Copilot uses AI. Check for mistakes.
Comment on lines +154 to +164
// 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
}
Copy link

Copilot AI Mar 11, 2026

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).

Suggested change
// 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 uses AI. Check for mistakes.
Comment on lines 138 to 141
jobManager: NewJobManager(),
engineRegistry: GetGlobalEngineRegistry(),
engineCatalog: NewEngineCatalog(GetGlobalEngineRegistry()),
stepOrderTracker: NewStepOrderTracker(),
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NewCompiler initializes engineRegistry and engineCatalog by calling GetGlobalEngineRegistry() twice. It’s a singleton today, but wiring catalog off c.engineRegistry avoids accidental divergence if initialization changes (or if a future option swaps engineRegistry). Suggest: set engineCatalog based on the already-assigned engineRegistry (or initialize it after options are applied).

Copilot uses AI. Check for mistakes.
Comment on lines +206 to 214
orchestratorEngineLog.Printf("Resolving engine setting: %s", engineSetting)
resolvedEngine, err := c.engineCatalog.Resolve(engineSetting, engineConfig)
if err != nil {
orchestratorEngineLog.Printf("Failed to get agentic engine: %v", err)
orchestratorEngineLog.Printf("Engine resolution failed: %v", err)
return nil, err
}
agenticEngine := resolvedEngine.Runtime

log.Printf("AI engine: %s (%s)", agenticEngine.GetDisplayName(), engineSetting)
Copy link

Copilot AI Mar 11, 2026

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.

Copilot uses AI. Check for mistakes.
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) {
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestEngineCatalog_Resolve_LegacyStringFormat doesn’t actually exercise different frontmatter formats—both branches call Resolve("claude", &EngineConfig{ID:"claude"}). Since EngineCatalog.Resolve only uses the string id argument (not how EngineConfig was constructed), this test will pass even if legacy/object parsing breaks elsewhere. Consider either removing this test or rewriting it to cover the real contract you want (e.g., parsing/extracting engine settings from frontmatter via ExtractEngineConfig, then calling Resolve with those outputs).

Suggested change
// 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
[...]

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[plan] Phase 1: Introduce EngineDefinition, EngineCatalog, and ResolvedEngineTarget

3 participants