Skip to content

fix(config): keep installed versions eligible with release age - #10973

Merged
jdx merged 4 commits into
mainfrom
codex/fix-installed-release-age-resolution
Jul 13, 2026
Merged

jdx merged 4 commits into
mainfrom
codex/fix-installed-release-age-resolution

Conversation

@jdx

@jdx jdx commented Jul 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • keep already-installed fuzzy matches eligible when minimum_release_age is configured
  • reserve date-aware filtering of installed matches for lockfile generation, which intentionally discovers versions from release metadata
  • document the installed-version semantics and add regression coverage for local resolution and prune

Root cause

An explicit minimum_release_age cutoff globally disabled installed-version fast paths. As a result, ordinary online resolution could select an older age-eligible remote release while offline/prefer-offline resolution and prune selected the latest installed release. This made the same latest request resolve inconsistently and made prune appear to remove an active version.

minimum_release_age is an installation/discovery gate: an explicitly installed version remains eligible. Commands that intentionally discover versions (upgrade, install resolution, latest, ls-remote, and date-aware lock generation) continue to apply the cutoff to remote candidates.

Addresses discussion #10944.

Validation

  • mise run lint-fix
  • mise run test:unit (1,668 passed)
  • mise run test:e2e test_minimum_release_age_installed_resolution
  • mise run test:e2e test_install_prefer_offline_no_refresh test_which_no_remote_fetch test_which_prefer_offline
  • mise run test:e2e e2e/cli/test_lock (target test passed)
  • mise run test:e2e test_npm_install_before

Note

Medium Risk
Changes central version resolution in tool_version.rs, which affects most commands that resolve tools; behavior is narrowed with explicit opt-in for lockfile discovery and regression tests, but edge cases around fuzzy specs and release age remain possible.

Overview
Fixes inconsistent behavior where an explicit minimum_release_age (or built-in default cutoff) could skip installed-version fast paths, forcing remote lookups for fuzzy specs like latest and making online resolution, mise which, and mise prune disagree about the active version.

Resolution now treats release age as a remote discovery gate: ResolveOptions adds filter_installed_versions_by_release_date (default off). Ordinary flows (install, use, upgrade, hook-env-style resolution) leave it false, so already-installed fuzzy matches stay eligible without re-checking release metadata. mise lock sets it true and may rebuild the toolset when a release-age policy is configured, so lock generation can still pick age-eligible remote versions over a newer installed match.

Docs in docs/security.md spell out that installed versions are not made inactive by the setting. E2E coverage adds test_minimum_release_age_installed_resolution and adjusts lock / which regression tests.

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

Summary by CodeRabbit

  • Bug Fixes
    • Already-installed tool versions remain eligible during resolution even when a minimum release age is configured.
    • mise install, mise use, and mise upgrade now avoid release-age filtering for installed matches and reduce unnecessary remote lookups.
    • mise lock continues to apply minimum release age filtering when selecting versions.
  • Documentation
    • Updated security documentation to clarify that minimum release age affects only remote version selection, not installed versions.
  • Tests
    • Added/updated end-to-end coverage for minimum-release-age installed-resolution behavior and offline/lookup preference scenarios.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change makes installed-version release-date filtering explicit, configures it per CLI command, updates version-resolution fast paths, and adds end-to-end coverage and documentation for minimum_release_age.

Changes

Minimum release age resolution

Layer / File(s) Summary
Resolution policy and installed-version fast paths
src/toolset/tool_version.rs
Adds explicit release-date filtering to ResolveOptions and applies it to installed-version matching for version and prefix resolution.
Command-specific resolution modes
src/cli/install.rs, src/cli/lock.rs, src/cli/upgrade.rs, src/cli/use.rs
Commands disable installed-version filtering except lock resolution, which enables it.
Regression coverage and behavior documentation
e2e/cli/test_minimum_release_age_installed_resolution, e2e/cli/test_lock, e2e/cli/test_which_*, docs/security.md
Verifies installed versions avoid remote lookup and documents their continued eligibility.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLICommand
  participant ToolVersion
  participant RemoteListing
  CLICommand->>ToolVersion: resolve requested version with filtering mode
  ToolVersion->>ToolVersion: evaluate installed-version fast path
  ToolVersion-->>CLICommand: return installed match
  ToolVersion->>RemoteListing: fetch versions when remote resolution is required
Loading

Possibly related PRs

  • jdx/mise#10315: Modifies the same minimum-release-age installed-version resolution behavior.
  • jdx/mise#10571: Changes related prefer_offline fast-path behavior in tool_version.rs.
  • jdx/mise#10595: Addresses remote fetching when an installed version satisfies a request.

Poem

A bunny hops past versions old,
No remote list need be told.
Installed tools stay in the den,
Safe from cutoff’s chilly pen.
The right tool’s already here! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: keeping installed versions eligible when minimum release age filtering is applied.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 44064e4. Configure here.

Comment thread src/cli/lock.rs Resolved

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/security.md`:
- Around line 47-48: Update the installed-version eligibility statement in the
security documentation to limit the exception to ordinary resolution. Clarify
that installed matches remain eligible during normal version selection, while
`mise lock` may re-check fuzzy installed matches against release metadata and
apply the minimum release age cutoff.

In `@e2e/cli/test_minimum_release_age_installed_resolution`:
- Around line 15-20: Update the test setup around the dummy spy plugin scripts
to replace or trap both list-all and latest-stable with marker-touching failing
scripts. Keep the existing cache reset and marker assertion behavior so any
remote metadata fetch is detected, including the latest_version_with_refresh
path.

In `@src/cli/lock.rs`:
- Around line 88-92: Update the fallback toolset construction in
config.get_toolset() to apply the same lock-specific ResolveOptions, including
filter_installed_versions_by_release_date. Build that branch through
ToolsetBuilder::new().with_resolve_options(lock_resolve_options.clone()),
preserving the existing before_date and other resolution settings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: bd5f4e20-eb24-4326-b936-6de840a3f350

📥 Commits

Reviewing files that changed from the base of the PR and between b2bdeb1 and 44064e4.

📒 Files selected for processing (9)
  • docs/security.md
  • e2e/cli/test_minimum_release_age_installed_resolution
  • e2e/cli/test_which_no_remote_fetch
  • e2e/cli/test_which_prefer_offline
  • src/cli/install.rs
  • src/cli/lock.rs
  • src/cli/upgrade.rs
  • src/cli/use.rs
  • src/toolset/tool_version.rs

Comment thread docs/security.md Resolved
Comment thread src/cli/lock.rs Resolved
@greptile-apps

greptile-apps Bot commented Jul 13, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug where minimum_release_age inadvertently disabled the installed-version fast paths for fuzzy specs (latest, version prefixes), causing the same config to resolve differently across commands and making prune appear to remove active versions. The fix gates date-aware filtering of installed versions behind a new ResolveOptions::filter_installed_versions_by_release_date flag (default false), enabled only for mise lock, while install, use, and upgrade explicitly set it to false.

  • tool_version.rs: should_filter_installed_versions now requires the new flag in addition to an active cutoff, so ordinary resolution always treats installed fuzzy matches as eligible; the redundant prefer_offline-specific fast-path branches are removed.
  • lock.rs: The toolset-rebuild path is extended to trigger when a minimum_release_age is configured (not just when --before is passed), ensuring mise lock re-checks installed fuzzy versions against release metadata as intended.
  • Tests: A new test_minimum_release_age_installed_resolution e2e test makes remote lookups fatal and verifies mise current resolves from installed versions without network access; test_lock relocates the mkdir -p setup so both lock assertions validate filtering against an already-installed newer version.

Confidence Score: 5/5

Safe to merge — the change is well-scoped, defaults the new flag to false so no call site is accidentally activated, and is covered by targeted e2e regressions.

The new filter_installed_versions_by_release_date flag defaults to false, so all existing callers are unaffected unless explicitly opted in. Only mise lock enables it, and the e2e tests verify both the lock path (date-filtering installed versions) and the ordinary resolution path (skipping remote lookups entirely). The removed prefer_offline fast-path branches are functionally subsumed by the unified !opts.latest_versions && !should_filter_installed_versions path, which behaves identically in all non-lock scenarios.

No files require special attention.

Important Files Changed

Filename Overview
src/toolset/tool_version.rs Adds filter_installed_versions_by_release_date field to ResolveOptions (default false) and rewires should_filter_installed_versions to require the flag, making installed-version fast paths unconditional for ordinary resolution; removes the now-redundant prefer_offline-specific fast-path branches.
src/cli/lock.rs Sets filter_installed_versions_by_release_date: true for lock resolve options and expands the toolset-rebuild condition from before_date.is_some() to also cover a globally or per-tool configured minimum_release_age, ensuring lock always date-filters installed fuzzy versions when a cutoff is active.
src/cli/install.rs Explicitly opts out of installed-version date filtering (filter_installed_versions_by_release_date: false) in the resolve options passed during install.
src/cli/upgrade.rs Sets filter_installed_versions_by_release_date: false at both call sites in the upgrade flow, consistent with install and use semantics.
src/cli/use.rs Sets filter_installed_versions_by_release_date: false in the resolve options for mise use, keeping installed-version fast paths active.
e2e/cli/test_minimum_release_age_installed_resolution New regression test: installs spy@1.0.0 and spy@2.0.0, makes remote lookups fatal, and verifies that mise current resolves to 2.0.0 without network access even when minimum_release_age is configured, and that prune correctly identifies 1.0.0 as unused.
e2e/cli/test_lock Moves mkdir -p jq/1.7.1 before both lock invocations that assert age-filtering still selects jq@1.6, proving mise lock correctly applies the cutoff even when the newer version is already installed.
e2e/cli/test_which_no_remote_fetch Updates comment to reflect that installed matches are now eligible regardless of implicit or explicit minimum_release_age; no logic changes.
e2e/cli/test_which_prefer_offline Simplifies comments to reflect that prefer-offline behavior no longer needs to special-case explicit cutoffs; the underlying test logic is unchanged.
docs/security.md Adds a sentence clarifying that minimum_release_age only gates remote version selection, not installed versions, and that lockfile generation may re-check installed fuzzy versions.

Reviews (2): Last reviewed commit: "perf(lock): preserve cached toolset with..." | Re-trigger Greptile

Comment thread src/cli/lock.rs Resolved
@github-actions

github-actions Bot commented Jul 13, 2026 •

Copy link
Copy Markdown
Contributor

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.5 x -- echo 19.9 ± 2.6 17.4 34.1 1.00 ± 0.15
mise x -- echo 19.9 ± 1.7 18.0 47.7 1.00

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.5 env 19.3 ± 1.0 17.3 23.5 1.00
mise env 22.4 ± 3.0 18.4 30.3 1.16 ± 0.16
⚠️ Inconclusive: env measured 16% slower, but the relative uncertainty overlaps the 10% threshold.

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.5 hook-env 19.9 ± 1.3 17.8 27.4 1.00
mise hook-env 21.4 ± 1.2 18.8 28.3 1.07 ± 0.09

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.5 ls 17.7 ± 0.8 16.0 21.5 1.00
mise ls 19.4 ± 1.2 17.0 24.0 1.10 ± 0.09

xtasks/test/perf

Command mise-2026.7.5 mise Variance
install (cached) 170ms 166ms +2%
ls (cached) 68ms 70ms -2%
bin-paths (cached) 79ms 77ms +2%
task-ls (cached) 153ms 156ms -1%

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