Skip to content

Stabilize import topological sort and add comprehensive test coverage#16149

Merged
pelikhan merged 3 commits intomainfrom
copilot/add-tests-for-import-graph-topologies
Feb 16, 2026
Merged

Stabilize import topological sort and add comprehensive test coverage#16149
pelikhan merged 3 commits intomainfrom
copilot/add-tests-for-import-graph-topologies

Conversation

Copy link
Contributor

Copilot AI commented Feb 16, 2026

Stable Topological Sort for Imports

Plan

  • Analyze current topological sort implementation
  • Fix stability issue: Map iteration in dependency reduction loop (line 905-916) is non-deterministic
  • Fix stability issue: Map iteration in in-degree calculation loop (line 871) is non-deterministic
  • Add more comprehensive test cases for import graph topologies:
    • Wide tree (multiple independent branches)
    • Reverse alphabetical ordering (to test stability when order matters)
    • Complex multi-level dependencies
    • Parallel branches with shared dependency
    • Mixed naming with special characters
  • Run tests to verify stability
  • Validate changes

Changes Made

1. Fixed ALL Stability Issues in Topological Sort (pkg/parser/import_processor.go)

Problem: The topological sort algorithm had TWO places where it iterated over Go maps with randomized iteration order:

  1. In-degree calculation (line 871) - when counting dependencies
  2. In-degree reduction (line 905) - when processing the queue

Both caused non-deterministic output.

Solution: Modified BOTH loops to iterate over dependencies in sorted order:

Fix #1 - In-degree calculation (lines 872-886):

// Extract keys, sort them, then iterate
sortedImportsForDegree := make([]string, 0, len(dependencies))
for imp := range dependencies {
    sortedImportsForDegree = append(sortedImportsForDegree, imp)
}
sort.Strings(sortedImportsForDegree)

for _, imp := range sortedImportsForDegree {
    deps := dependencies[imp]
    // Count dependencies...
}

Fix #2 - In-degree reduction (lines 914-932):

// Extract keys, sort them, then iterate
sortedImports := make([]string, 0, len(dependencies))
for imp := range dependencies {
    sortedImports = append(sortedImports, imp)
}
sort.Strings(sortedImports)

for _, imp := range sortedImports {
    deps := dependencies[imp]
    // Reduce in-degrees...
}

2. Added Comprehensive Test Cases (pkg/parser/import_topological_test.go)

Added 5 new test cases covering diverse import graph topologies:

  1. Wide tree with many independent branches: Tests that independent branches are processed in stable order
  2. Reverse alphabetical with dependencies: Tests stability with names that sort differently
  3. Multi-level dependency chain: Tests deeply nested linear dependencies (5 levels)
  4. Parallel branches with shared dependency: Tests diamond-like structure with convergence
  5. Mixed naming with special characters: Tests numeric prefixes and special naming patterns

Test Results

All topological sort tests pass successfully:

  • TestImportTopologicalSort (9 test cases)
  • TestImportTopologicalSortWithSections
  • TestImportTopologicalSortPreservesAlphabeticalForSameLevel
  • ✅ All pkg/parser tests pass
  • ✅ All workflow import tests pass

Technical Details

The fix ensures deterministic ordering at THREE levels:

  1. Queue sorting (existing, line 903): Sort queue before each dequeue
  2. NEW - In-degree calculation (lines 872-886): Sort keys before counting dependencies
  3. NEW - In-degree reduction (lines 914-932): Sort keys before reducing in-degrees

This guarantees that the topological sort produces the same output every time, regardless of Go's map iteration randomization, by ensuring ALL map iterations are replaced with sorted slice iterations.

Original prompt

When doing the topological sort of imports, make sure the ordering is stable. Add tests with more import graph topologies.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@pelikhan
Copy link
Contributor

@copilot Go Keys in maps are not sorted and have no guaranteed order. Review topological map that relies on maps for stability.

…ests

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Add tests for stable ordering in topological sort of imports Stabilize import topological sort and add comprehensive test coverage Feb 16, 2026
Copilot AI requested a review from pelikhan February 16, 2026 16:36
@pelikhan
Copy link
Contributor

@copilot merge main and recompile

…bility

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 16, 2026

@copilot Go Keys in maps are not sorted and have no guaranteed order. Review topological map that relies on maps for stability.

Fixed in ba27ddd. The topological sort now sorts ALL map iterations:

  1. In-degree calculation (lines 872-886): Extract map keys to slice, sort, then iterate
  2. In-degree reduction (lines 914-932): Extract map keys to slice, sort, then iterate

Both loops that iterate over the dependencies map now use sorted slices to ensure stable, deterministic output regardless of Go's map iteration randomization.

@pelikhan pelikhan marked this pull request as ready for review February 16, 2026 16:42
Copilot AI review requested due to automatic review settings February 16, 2026 16: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

This PR fixes a critical stability issue in the import topological sort algorithm by ensuring all map iterations are deterministic. The algorithm previously had non-deterministic behavior due to Go's randomized map iteration order in two critical locations: the in-degree calculation phase and the in-degree reduction phase of Kahn's algorithm. The fix extracts map keys into slices and sorts them before iteration in both locations, ensuring stable, reproducible topological ordering.

Changes:

  • Fixed non-deterministic map iteration in in-degree calculation phase by sorting keys before iteration
  • Fixed non-deterministic map iteration in in-degree reduction phase by sorting keys before iteration
  • Added 5 comprehensive test cases covering diverse import graph topologies (wide trees, reverse alphabetical ordering, multi-level chains, parallel branches with shared dependencies, and mixed naming patterns)

Reviewed changes

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

File Description
pkg/parser/import_processor.go Fixed stability issues by adding sorted iteration in both in-degree calculation (lines 872-886) and in-degree reduction (lines 914-932) phases of the topological sort algorithm
pkg/parser/import_topological_test.go Added 5 new comprehensive test cases (lines 131-297) covering wide trees, reverse alphabetical ordering, multi-level chains, parallel branches, and mixed naming patterns to ensure stability across diverse graph topologies

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

Copy link
Contributor Author

Copilot AI commented Feb 16, 2026

@copilot merge main and recompile

The branch is already up to date with origin/main. Ran make build && make recompile - no workflow changes needed (all .lock.yml files are current).

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 (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 etfqOr9/9xP04_nq-1 (http block)
  • https://api.github.com/repos/actions/ai-inference/git/ref/tags/v2
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v2 --jq .object.sha (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/11bd71901bbe5b1630ceea73d27597364c9af683
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/11bd71901bbe5b1630ceea73d27597364c9af683 --jq .object.sha (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 (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v7
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v7 --jq .object.sha (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/4dc6199c7b1a012772edbd06daecab0f50c9053c
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/4dc6199c7b1a012772edbd06daecab0f50c9053c --jq .object.sha ommit} (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/v5
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v5 --jq .object.sha (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v5 --jq .object.sha data/action_pins.json...&#34; (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v6 --jq .object.sha (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v6 --jq .object.sha cli/install.sh..-f (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq .object.sha (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq .object.sha cp .github/aw/actions-lock.json pkg/workflow/data/action_pins.json; \ echo &#34;��� Action pins syn/usr/bin/gh (http block)
  • https://api.github.com/repos/anchore/sbom-action/git/ref/tags/v0
    • Triggering command: /usr/bin/gh gh api /repos/anchore/sbom-action/git/ref/tags/v0 --jq .object.sha (http block)
    • Triggering command: /usr/bin/gh gh api /repos/anchore/sbom-action/git/ref/tags/v0 --jq .object.sha ommit} (http block)
  • https://api.github.com/repos/docker/build-push-action/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/docker/build-push-action/git/ref/tags/v6 --jq .object.sha (http block)
    • Triggering command: /usr/bin/gh gh api /repos/docker/build-push-action/git/ref/tags/v6 --jq .object.sha ommit} (http block)
  • https://api.github.com/repos/docker/login-action/git/ref/tags/v3
    • Triggering command: /usr/bin/gh gh api /repos/docker/login-action/git/ref/tags/v3 --jq .object.sha (http block)
  • https://api.github.com/repos/docker/metadata-action/git/ref/tags/v5
    • Triggering command: /usr/bin/gh gh api /repos/docker/metadata-action/git/ref/tags/v5 --jq .object.sha (http block)
  • https://api.github.com/repos/docker/setup-buildx-action/git/ref/tags/v3
    • Triggering command: /usr/bin/gh gh api /repos/docker/setup-buildx-action/git/ref/tags/v3 --jq .object.sha (http block)
    • Triggering command: /usr/bin/gh gh api /repos/docker/setup-buildx-action/git/ref/tags/v3 --jq .object.sha ommit} (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/c4e091835c7a94dc7d3acb8ed3ae145afb4995f3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/c4e091835c7a94dc7d3acb8ed3ae145afb4995f3 --jq .object.sha (http block)
  • https://api.github.com/repos/githubnext/agentics/git/ref/tags/-
    • Triggering command: /usr/bin/gh gh api /repos/githubnext/agentics/git/ref/tags/- --jq .object.sha (http block)
  • https://api.github.com/user
    • Triggering command: /usr/bin/gh gh api user --jq .login (http block)

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

@pelikhan pelikhan merged commit 2dd8fa8 into main Feb 16, 2026
92 of 93 checks passed
@pelikhan pelikhan deleted the copilot/add-tests-for-import-graph-topologies branch February 16, 2026 16:47
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.

2 participants