Skip to content

Fix non-deterministic map iteration in EngineRegistry#21474

Merged
pelikhan merged 3 commits intomainfrom
copilot/plan-fix-map-iteration
Mar 18, 2026
Merged

Fix non-deterministic map iteration in EngineRegistry#21474
pelikhan merged 3 commits intomainfrom
copilot/plan-fix-map-iteration

Conversation

Copy link
Contributor

Copilot AI commented Mar 17, 2026

GetSupportedEngines() and GetEngineByPrefix() iterated r.engines map[string]CodingAgentEngine directly, producing random ordering due to Go's map iteration randomization. GetEngineByPrefix() returned the first match found, meaning the same prefix string could resolve to different engines across runs.

Changes

  • GetSupportedEngines() — sorts the result slice before returning
  • GetEngineByPrefix() — collects all matching candidates, sorts by engine ID, returns the first deterministically
func (r *EngineRegistry) GetEngineByPrefix(prefix string) (CodingAgentEngine, error) {
    type engineCandidate struct{ id string; engine CodingAgentEngine }
    var candidates []engineCandidate
    for id, engine := range r.engines {
        if strings.HasPrefix(prefix, id) {
            candidates = append(candidates, engineCandidate{id, engine})
        }
    }
    if len(candidates) == 0 {
        return nil, fmt.Errorf("no engine found matching prefix: %s", prefix)
    }
    sort.Slice(candidates, func(i, j int) bool { return candidates[i].id < candidates[j].id })
    return candidates[0].engine, nil
}

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 GO111MODULE 64/bin/go git rev-�� --show-toplevel go /usr/bin/git -json GO111MODULE 64/pkg/tool/linu--show-toplevel git (http block)
  • https://api.github.com/orgs/test-owner/actions/secrets
    • Triggering command: /usr/bin/gh gh api /orgs/test-owner/actions/secrets --jq .secrets[].name GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGO111MODULE env 7995893/b395/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (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 --show-toplevel 7995893/b404/imp-test.v=true /usr/bin/git k/gh-aw/gh-aw/pkgit k/gh-aw/gh-aw/pkrev-parse 64/bin/go git conf�� user.name Test User /usr/bin/git b/workflows -trimpath 64/bin/go git (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 -aw/git/ref/tags/v2.0.0 GO111MODULE (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 86/001/test-simple-frontmatter.md GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --git-dir go /tmp/go-build920573038/b433/workflow.test -json GO111MODULE x_amd64/asm /tmp/go-build920573038/b433/workflow.test -tes�� -test.paniconexit0 -test.v=true /usr/bin/git -test.timeout=10git -test.run=^Test -test.short=true--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel 64/pkg/tool/linux_amd64/link /usr/bin/git util.test GO111MODULE rtcfg.link git rev-�� --show-toplevel yQm7JrG_JTdGyVRlef/N6GE9dzJuLpfUe9tz4e_/urVQRthzremote.origin.url /usr/bin/git ithub/workflows GO111MODULE ngutil.test 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 user.email test@example.com ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet -json GO111MODULE 64/bin/go ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet conf�� user.name Test User /usr/bin/git &#34;prettier&#34; --chegit GOPROXY 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --get remote.origin.url /usr/bin/git -json GO111MODULE 64/bin/go git bran�� --show-current sh /usr/bin/git &#34;prettier&#34; --chegit GOPROXY 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --show-toplevel 64/pkg/tool/linux_amd64/asm /usr/bin/git -json GO111MODULE ache/go/1.25.0/x--show-toplevel git rev-�� --show-toplevel go /usr/bin/git -json GO111MODULE ache/go/1.25.0/x--show-toplevel git (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 ache/go/1.25.0/xGO111MODULE env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha ck &#39;scripts/**/*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (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 GOMODCACHE go /opt/hostedtoolcache/node/24.14.0/x64/bin/node -json GO111MODULE 64/bin/go node /tmp�� /tmp/TestHashConsistency_KeyOrdering173402863/001/test2.md sh /usr/bin/git &#34;prettier&#34; --chegit GOPROXY 64/bin/go git (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 --show-toplevel go 573038/b433/workflow.test -json GO111MODULE 64/bin/go 573038/b433/workflow.test e=/t�� t0 sh (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 dk/zW4jRbLZxVdM3rzSM3Rw/IRLg9tGLJO4bUJOqCxYt env runs/20260317-235137-28349/test-3519158934/.github/workflows GO111MODULE 710b3b2db19f40fbb457246cc51f19167f97480760eb40d5-d l GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --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/github/gh-aw-actions/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq .object.sha -json GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/node/24.14.0/x64/bin/node GOINSECURE GOMOD GOMODCACHE 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 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (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 go stlo�� -json GO111MODULE 64/bin/go Action pins syngit GOMOD 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 GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json 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 x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (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 env -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 x_amd64/link GOINSECURE GOMOD GOMODCACHE x_amd64/link env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE rk/cnjHJRsC75-1TI1_m0Vn/cSPgOA5Ap48bvsu2kY9H (http block)
  • https://api.github.com/repos/github/gh-aw/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path edcfg GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGO111MODULE env 7995893/b404/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE b/gh-aw/pkg/repo-c GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 100 /tmp/go-build920573038/b416/importcfg -pack /tmp/go-build920573038/b416/_testmain.go env ZRqv/fubwezt6gjQGOSUMDB GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE 7995893/b411/imp--json (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 6 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/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 -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (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 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (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 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (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 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (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 -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env 956104167/.github/workflows GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE ns-lock.json does not exist yet&#34;/tmp/go-build920573038/b413/_testmain.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 GOMODCACHE x_amd64/link env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE ef/N6GE9dzJuLpfUe9tz4e_/urVQRthzremote.origin.url (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 ache/go/1.25.0/xGO111MODULE env 7995893/b412/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE b/gh-aw/pkg/test-c GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go GOINSECURE GOMOD erignore ache/go/1.25.0/xGO111MODULE env 7995893/b414/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE b/gh-aw/pkg/timefor-each-ref GOMODCACHE go (http block)
  • https://api.github.com/repos/owner/repo/contents/file.md
    • Triggering command: /tmp/go-build920573038/b383/cli.test /tmp/go-build920573038/b383/cli.test -test.testlogfile=/tmp/go-build920573038/b383/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true GOINSECURE GOMOD GOMODCACHE go m/_n�� -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 ache/go/1.25.0/xGO111MODULE env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)

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


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

Copilot AI and others added 2 commits March 17, 2026 23:49
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…s and GetEngineByPrefix

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] [plan] Fix non-deterministic map iteration in EngineRegistry Fix non-deterministic map iteration in EngineRegistry Mar 17, 2026
Copilot AI requested a review from pelikhan March 17, 2026 23:55
@pelikhan pelikhan marked this pull request as ready for review March 18, 2026 00:09
Copilot AI review requested due to automatic review settings March 18, 2026 00:09
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

Fixes non-deterministic behavior in engine lookup APIs caused by Go’s randomized map iteration order, ensuring stable results across runs.

Changes:

  • Sorts the result of EngineRegistry.GetSupportedEngines() before returning.
  • Makes EngineRegistry.GetEngineByPrefix() deterministic by collecting matches, sorting, and returning a consistent winner.
  • Updates the generated changeset.lock.yml workflow file by removing app_token_minting_failed output/env wiring.

Reviewed changes

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

File Description
pkg/workflow/agentic_engine.go Adds sorting to stabilize engine ID listing and prefix-based resolution.
.github/workflows/changeset.lock.yml Removes app token minting failure-related env/outputs from the changeset workflow lockfile.
Comments suppressed due to low confidence (1)

pkg/workflow/agentic_engine.go:497

  • GetEngineByPrefix() now deterministically picks the first candidate after sorting, but there’s no test ensuring deterministic/consistent selection when multiple engines match a given string. Add a unit test that registers two engines with IDs that both match the same input (e.g., "co" and "copilot") and asserts the chosen engine is stable and follows the intended tie-break rule.
func (r *EngineRegistry) GetEngineByPrefix(prefix string) (CodingAgentEngine, error) {
	type engineCandidate struct {
		id     string
		engine CodingAgentEngine
	}
	var candidates []engineCandidate
	for id, engine := range r.engines {
		if strings.HasPrefix(prefix, id) {
			candidates = append(candidates, engineCandidate{id, engine})
		}
	}
	if len(candidates) == 0 {
		return nil, fmt.Errorf("no engine found matching prefix: %s", prefix)
	}
	sort.Slice(candidates, func(i, j int) bool { return candidates[i].id < candidates[j].id })
	return candidates[0].engine, nil

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

Comment on lines 460 to 466
func (r *EngineRegistry) GetSupportedEngines() []string {
var engines []string
for id := range r.engines {
engines = append(engines, id)
}
sort.Strings(engines)
return engines
Comment on lines 981 to 985
GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }}
GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }}
GH_AW_SAFE_OUTPUTS_APP_TOKEN_MINTING_FAILED: ${{ needs.safe_outputs.outputs.app_token_minting_failed }}
GH_AW_CONCLUSION_APP_TOKEN_MINTING_FAILED: ${{ steps.safe-outputs-app-token.outcome == 'failure' }}
GH_AW_GROUP_REPORTS: "false"
GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
@pelikhan pelikhan merged commit 4b15992 into main Mar 18, 2026
115 checks passed
@pelikhan pelikhan deleted the copilot/plan-fix-map-iteration branch March 18, 2026 00:46
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] Fix non-deterministic map iteration in EngineRegistry

3 participants