Skip to content

fix(github): prefer shortest asset name as tiebreaker in auto-detection#9361

Merged
jdx merged 1 commit intomainfrom
fix/asset-picker-shortest-tiebreak
Apr 24, 2026
Merged

fix(github): prefer shortest asset name as tiebreaker in auto-detection#9361
jdx merged 1 commit intomainfrom
fix/asset-picker-shortest-tiebreak

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Apr 24, 2026

Summary

  • When a release publishes multiple binaries per platform (e.g. agent-sh/agnix ships agnix-*, agnix-lsp-*, agnix-mcp-*), the platform-scoring rules tie and the picker returned whichever asset GitHub's API listed first — varies per platform, producing inconsistent lockfile entries.
  • Use shortest name as a tiebreaker (then lexicographic for full determinism). The canonical binary's name is almost always the shortest in this pattern.
  • Also fixes the same problem for tools like bun that ship baseline/profile variants alongside the canonical build.

Fixes #9358

Test plan

  • New unit test covers the agnix case (multiple binaries per platform)
  • New unit test covers the bun case (variant suffixes)
  • Both tests confirm order-independence
  • All existing asset_matcher tests still pass (31/31)
  • All existing github backend tests still pass (24/24)

Note

Medium Risk
Changes asset auto-selection ordering, which can alter which release artifact gets installed and what gets written to lockfiles across platforms. Risk is contained to tie cases but could still pick a different binary when multiple assets score equally.

Overview
Makes AssetPicker::pick_best_asset deterministic when multiple assets have the same platform score by adding tie-breakers: prefer higher score, then shortest filename, then lexicographic order.

Adds unit tests covering multi-binary releases (e.g. agnix vs agnix-lsp/agnix-mcp) and variant builds (e.g. bun baseline/profile) to ensure the canonical, shortest-named asset is selected regardless of input ordering.

Reviewed by Cursor Bugbot for commit dc373a9. Bugbot is set up for automated code reviews on this repo. Configure here.

When a release publishes multiple binaries per platform (e.g. agent-sh/agnix
ships agnix, agnix-lsp, and agnix-mcp), the platform-scoring rules tie and
the picker would return whichever asset the GitHub API listed first — which
varies per platform and produces inconsistent lockfile entries.

Use shortest name as a tiebreaker, then lexicographic for full determinism.
The canonical binary's name is almost always the shortest in this pattern.

Fixes #9358

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 24, 2026

Greptile Summary

This PR fixes non-deterministic asset selection in the GitHub backend by replacing a sort_by_key + first approach with a fully-deterministic min_by comparator that orders candidates by highest score first, then shortest name, then lexicographic order. The change correctly addresses cases like agent-sh/agnix (multiple named binaries per platform) and bun (variant suffixes) where all matching assets score identically.

Confidence Score: 5/5

Safe to merge — the logic is correct, tests are order-independent, and all existing tests continue to pass.

The min_by comparator correctly inverts the score comparison (score_b.cmp(score_a)) to select the highest-scoring asset while adding deterministic tiebreakers. Both new tests exercise the exact failure modes described in the linked discussion and verify order-independence. No regressions in existing behavior.

No files require special attention.

Important Files Changed

Filename Overview
src/backend/asset_matcher.rs Replaces sort+first with min_by that encodes highest-score-first, then shortest-name, then lexicographic tiebreakers; adds two new unit tests covering the agnix and bun cases.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["pick_best_asset(assets)"] --> B["score_all_assets(assets)"]
    B --> C["into_iter()"]
    C --> D["filter: score > 0"]
    D --> E{"any candidates?"}
    E -- No --> F["None"]
    E -- Yes --> G["min_by comparator"]
    G --> H["1. Highest score\n(score_b.cmp(score_a))"]
    H --> I{"tie?"}
    I -- No --> J["winner by score"]
    I -- Yes --> K["2. Shortest name\n(name_a.len().cmp(&name_b.len()))"]
    K --> L{"tie?"}
    L -- No --> M["winner by length"]
    L -- Yes --> N["3. Lexicographic\n(name_a.cmp(name_b))"]
    N --> O["deterministic winner"]
    J & M & O --> P["map: return asset String"]
Loading

Reviews (1): Last reviewed commit: "fix(github): prefer shortest asset name ..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request modifies the pick_best_asset method in src/backend/asset_matcher.rs to implement a tie-breaking mechanism for assets with identical scores. When scores are equal, the logic now prefers the asset with the shortest name, which typically represents the canonical binary. Additionally, new unit tests have been added to ensure correct behavior for tools like agnix and bun. There are no review comments to address, and I have no further feedback to provide.

@jdx jdx enabled auto-merge (squash) April 24, 2026 13:55
@jdx jdx merged commit bebab7f into main Apr 24, 2026
38 checks passed
@jdx jdx deleted the fix/asset-picker-shortest-tiebreak branch April 24, 2026 14:06
@github-actions
Copy link
Copy Markdown

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.20 x -- echo 21.8 ± 0.6 20.6 23.8 1.00
mise x -- echo 22.4 ± 2.2 21.0 66.9 1.03 ± 0.10

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.20 env 21.3 ± 0.6 20.2 25.2 1.00
mise env 22.2 ± 0.7 20.9 24.0 1.04 ± 0.05

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.20 hook-env 22.5 ± 0.8 21.1 26.4 1.00
mise hook-env 23.4 ± 0.8 21.8 25.8 1.04 ± 0.05

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.20 ls 20.6 ± 0.7 19.3 22.8 1.00
mise ls 21.2 ± 0.6 20.1 23.6 1.03 ± 0.05

xtasks/test/perf

Command mise-2026.4.20 mise Variance
install (cached) 163ms 166ms -1%
ls (cached) 79ms 79ms +0%
bin-paths (cached) 84ms 84ms +0%
task-ls (cached) 833ms 817ms +1%

mise-en-dev added a commit that referenced this pull request Apr 25, 2026
### 🚀 Features

- **(registry)** add --security flag to include security info in JSON
output by @jdx in [#9364](#9364)

### 🐛 Bug Fixes

- **(config)** limit resolved backend opts to aliases by @risu729 in
[#9315](#9315)
- **(docs)** stack banner message and link on mobile by @jdx in
[#9362](#9362)
- **(github)** prefer shortest asset name as tiebreaker in
auto-detection by @jdx in [#9361](#9361)
- **(java)** newer zulu versions use a different directory structure by
@roele in [#9365](#9365)
- **(prune)** respect tracked lockfiles by @jdx in
[#9373](#9373)
- **(task)** skip tool install for missing naked tasks by @jdx in
[#9374](#9374)
- **(trust)** add untrust command by @jdx in
[#9370](#9370)
- fix - flux-operator-mcp aqua path by @monotek in
[#9357](#9357)

### 📚 Documentation

- update ruby compile msg by @fladson in
[#9338](#9338)

### 📦️ Dependency Updates

- update ubuntu docker tag to v26 by @renovate[bot] in
[#9347](#9347)
- update ghcr.io/jdx/mise:deb docker digest to 1af5a69 by @renovate[bot]
in [#9352](#9352)
- update taiki-e/install-action digest to 787505c by @renovate[bot] in
[#9354](#9354)
- update ghcr.io/jdx/mise:rpm docker digest to 7015ff3 by @renovate[bot]
in [#9353](#9353)
- update ghcr.io/jdx/mise:copr docker digest to da63a0f by
@renovate[bot] in [#9351](#9351)
- update ghcr.io/jdx/mise:alpine docker digest to 461700f by
@renovate[bot] in [#9350](#9350)
- bump communique 1.0.3 → 1.0.4 by @jdx in
[#9378](#9378)

### 📦 Registry

- remove openshift-install by @jdx in
[#9372](#9372)
- remove go-sdk by @jdx in
[#9371](#9371)

### Chore

- **(npm-publish)** use aube publish instead of npm publish by @jdx in
[#9328](#9328)

### New Contributors

- @fladson made their first contribution in
[#9338](#9338)
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.

1 participant