Skip to content

feat(config): default release age and warn on hidden versions#10279

Merged
jdx merged 13 commits into
mainfrom
feat/default-min-release-age
Jun 9, 2026
Merged

feat(config): default release age and warn on hidden versions#10279
jdx merged 13 commits into
mainfrom
feat/default-min-release-age

Conversation

@jdx

@jdx jdx commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • default minimum_release_age to 24h at runtime for backends that can use release timestamps
  • keep minimum_release_age optional internally so explicit values keep precedence over deprecated install_before
  • avoid applying the built-in default to asdf/vfox/plugin-style tools without release timestamps
  • warn when newer releases are hidden by the release-age cutoff
  • strengthen release-age e2e coverage so opt-out and warning assertions require concrete setup/output

Tests

  • mise run render:schema
  • cargo fmt --all -- --check
  • cargo test install_before
  • cargo test latest_version_tests
  • cargo test test_settings_toml_is_sorted
  • mise run test:e2e e2e/cli/test_lock_env e2e/cli/test_lock_version
  • MISE_GPG_VERIFY=false mise run test:e2e e2e/cli/test_install_before
  • MISE_GPG_VERIFY=false mise run test:e2e e2e/backend/test_npm_install_before
  • mise run lint

This PR description was generated by an AI coding assistant.


Note

High Risk
Changes default version resolution for many backends without explicit config, which can surprise upgrades and CI; behavior is mitigated by 0s, exclusions, and pinned versions bypassing the filter.

Overview
Supply-chain default: Fuzzy version resolution on timestamp-capable backends (core, aqua, github, npm, pipx, etc.) now applies a built-in 24h minimum_release_age when no global or per-tool value is set. minimum_release_age = "0s" turns that off. minimum_release_age_excludes skips both the global setting and this default. Plugin-style backends without release metadata (e.g. asdf) are unchanged.

Visibility: mise ls-remote warns how many releases were hidden; mise upgrade compares date-filtered vs unfiltered latest and warns when a newer release is ignored (e.g. newer jq release … ignored by minimum_release_age).

Resolution plumbing: install_before is normalized into minimum_release_age (explicit minimum_release_age wins). Backend::latest_version_unfiltered supports upgrade diagnostics. Pinned exact versions no longer forward release-age cutoffs into npm/pipx transitive installs. VersionInfo gains shared date-filter helpers.

Docs/schema: Settings and security docs document the 24h default, exclusions, and opt-out; e2e tests cover 0s, upgrade warnings, and ls-remote warnings.

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

Summary by CodeRabbit

  • New Features

    • Default 24-hour minimum-release-age: versions released less than 24h are skipped by default.
    • Commands now warn when newer releases are ignored due to the release-age filter and report how many versions are hidden.
  • Documentation

    • Clarified default = "24h", how to override per-tool/global, that pinned versions bypass the delay, and that "0s" disables it.
  • Tests

    • Added/updated unit and e2e tests covering release-age behavior and regressions.
  • Refactor

    • Normalized legacy/alias settings and added backend-specific fallback behavior for the release-age cutoff.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Updated minimum_release_age handling: a 24-hour built-in default applies only for supported backends, legacy setting alias is normalized, version-date helpers/counts added, CLI lists/upgrades warn when newer releases are hidden, and docs/tests updated.

Changes

Minimum Release Age Default and Notifications

Layer / File(s) Summary
Settings and parsing normalization
settings.toml, src/config/settings.rs
Updated docs to state "24h" default and "0s" disables the delay; normalize legacy install_before into minimum_release_age during parsing/try_get; add unit test for precedence.
Runtime resolution with backend-specific fallback
src/install_before.rs
Introduce DEFAULT_MINIMUM_RELEASE_AGE = "24h" and refactor resolver to apply the default only when resolving with backend context and the backend is supported; no default when resolving without backend.
Version date helpers
src/backend/mod.rs
Add VersionInfo::created_at_timestamp(), hidden_by_date(before), and count_hidden_by_date(versions, before); refactor filter_by_date to use parsed timestamps while preserving include-on-missing/unparseable behavior.
ls-remote hidden version warnings
src/cli/ls_remote.rs
Count how many matching remote versions would be hidden by minimum_release_age, filter displayed versions by date, and emit a consolidated non-JSON warning; add helper to format warnings.
upgrade ignored-release warnings
src/cli/upgrade.rs
After building the outdated list, call warn_if_newer_versions_hidden_by_minimum_release_age; add helpers latest_for_upgrade and baseline_latest_for_upgrade and the warning generator to compare eligible vs unfiltered latests and deduplicate warnings.
Toolset query visibility
src/toolset/outdated_info.rs
Made prefixed_latest_query pub(crate) for use by upgrade/lookup helpers.
Documentation and comments
docs/dev-tools/backends/npm.md, docs/dev-tools/backends/pipx.md, docs/dev-tools/mise-lock.md, docs/tips-and-tricks.md, e2e/backend/test_npm_package_manager
Clarify forwarding of minimum_release_age in backend docs; document 24h default, duration/date formats, 0s disables, and that pinned versions bypass the cutoff; update test comments.
End-to-end tests
e2e/backend/test_npm_install_before, e2e/cli/test_install_before, e2e/cli/test_ls_remote
Add regression for MISE_MINIMUM_RELEASE_AGE=0s, add upgrade --minimum-release-age dry-run assertion, and extend ls-remote tests to assert hidden-version warning output.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • jdx/mise#10277: Modifies the same resolve_before_date_with_excludes logic in src/install_before.rs—this PR adds backend-context fallback defaulting while the related PR adds tool-aware exclusion handling.

Poem

🐰 I nibble through timestamps and code,

Twenty-four hours now set the mode,
Warnings whisper of newer hides,
Pinned versions leap the tide,
Tests hop in to keep the road.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: introducing a default release age (24h) and adding warnings when versions are hidden by this constraint.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/default-min-release-age

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

@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR makes minimum_release_age default to 24h at runtime for backends that expose release timestamps (Core, npm, GitHub, etc.), without writing it into config. It also fixes the minimum_release_age_excludes bypass so excluded tools skip both the global setting and the new built-in default, adds user-visible warnings when the cutoff hides newer releases in mise ls-remote and mise upgrade, and normalizes the deprecated install_before setting through a new normalize_hidden_config_aliases helper.

  • Default 24h filter: default_minimum_release_age_applies gates the built-in default to timestamp-capable backends; asdf/vfox/plugin-style tools are unaffected; set minimum_release_age = "0s" to disable.
  • Exclude fix: resolve_before_date_with_excludes now checks !excluded before both the global setting and the built-in default, so tools in minimum_release_age_excludes are fully opted out of all automatic filtering.
  • Deprecation migration: normalize_hidden_config_aliases normalizes install_beforeminimum_release_age during settings loading (explicit minimum_release_age wins); set_hidden_configs remains as a second-pass guard.

Confidence Score: 5/5

Safe to merge; the previously flagged issues with install_before precedence and minimum_release_age_excludes bypass are both correctly addressed in this revision.

The core logic changes in resolve_before_date_with_excludes are correct — excluded tools skip both the global setting and the built-in default, and the normalize_hidden_config_aliases helper preserves the right precedence for the deprecated alias. The new warning paths are additive and fail gracefully. Remaining notes are quality/performance observations that do not affect correctness.

No files require special attention.

Important Files Changed

Filename Overview
src/install_before.rs Core release-age resolution logic refactored: both the global setting and the new 24h default are correctly gated on !excluded, so tools in minimum_release_age_excludes are fully opted out. Well-covered by new unit tests.
src/config/settings.rs New normalize_hidden_config_aliases function properly normalizes install_beforeminimum_release_age with correct precedence (explicit wins). warn_deprecated deduplicates via a global set so double-calling during two-pass load won't produce duplicate warnings.
src/cli/upgrade.rs Adds per-tool warning when date-filter hides a newer release; warning logic makes 2 extra backend calls per filtered tool during every mise upgrade, which may add latency for large toolsets.
src/cli/ls_remote.rs Counts and reports hidden versions correctly; warning is intentionally suppressed for --json output. The run_all path passes the literal string "tools" as the tool name in the warning message, producing slightly awkward phrasing.
src/backend/mod.rs Adds hidden_by_date, count_hidden_by_date, and latest_version_unfiltered; filter_by_date refactored via shared created_at_timestamp helper. Semantics of new helpers are consistent with the existing filter logic.
src/toolset/toolset_install.rs New transitive_dependency_before_date correctly bypasses the date filter only when the request is an exact pinned version that resolved to itself, preserving filtering for fuzzy and latest requests.

Fix All in Claude Code

Reviews (6): Last reviewed commit: "fix(install): preserve release age for l..." | Re-trigger Greptile

Comment thread src/config/settings.rs

@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: 2

🤖 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 `@e2e/backend/test_npm_install_before`:
- Around line 18-21: The test currently calls "mise latest npm:prettier" and
only uses assert_not_contains to check it doesn't return "2.8.8", which can
false-pass on empty/failing output; capture the command output into a variable
(e.g., output=$(mise latest npm:prettier)), then assert the output is non-empty
and additionally assert it does not contain "2.8.8" (or assert it matches a
semantic version regex like ^[0-9]+\.[0-9]+\.[0-9]+$) so that the test fails on
empty/errored results while still ensuring the 0s opt-out returns a concrete
latest version.

In `@src/config/settings.rs`:
- Around line 381-384: The deprecated alias handling currently unconditionally
copies self.install_before into self.minimum_release_age, allowing the
deprecated key to override a user-provided canonical value; update the logic so
install_before only sets minimum_release_age when the canonical setting was not
provided by the user (or apply the alias mapping earlier at the SettingsPartial
layer before defaults are applied). Concretely, either move the alias mapping
out of this post-defaults block into the SettingsPartial normalization step, or
change the if let handling around install_before to check whether
minimum_release_age is unset (or its source is not user-provided) before
assigning, using the identifiers install_before, minimum_release_age, and
SettingsPartial to locate the correct code to change.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1b629010-1bf7-4b9e-b0ca-57ef6e3989c6

📥 Commits

Reviewing files that changed from the base of the PR and between 7e0b436 and 18b6c38.

📒 Files selected for processing (10)
  • docs/dev-tools/backends/npm.md
  • docs/dev-tools/backends/pipx.md
  • docs/dev-tools/mise-lock.md
  • docs/tips-and-tricks.md
  • e2e/backend/test_npm_install_before
  • e2e/backend/test_npm_package_manager
  • schema/mise.json
  • settings.toml
  • src/config/settings.rs
  • src/install_before.rs

Comment thread e2e/backend/test_npm_install_before
Comment thread src/config/settings.rs
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.6.1 x -- echo 21.2 ± 1.5 17.7 27.9 1.00
mise x -- echo 23.3 ± 2.6 17.9 55.2 1.10 ± 0.15

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.6.1 env 21.1 ± 1.6 17.6 26.8 1.00
mise env 22.9 ± 1.9 18.2 29.4 1.08 ± 0.12

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.6.1 hook-env 22.0 ± 1.7 18.2 28.8 1.00
mise hook-env 24.0 ± 2.0 19.2 30.7 1.09 ± 0.13

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.6.1 ls 17.9 ± 1.6 14.6 22.6 1.00
mise ls 19.6 ± 1.6 15.2 24.2 1.09 ± 0.13

xtasks/test/perf

Command mise-2026.6.1 mise Variance
install (cached) 157ms 158ms +0%
ls (cached) 69ms 70ms -1%
bin-paths (cached) 78ms 81ms -3%
task-ls (cached) 147ms 148ms +0%

@jdx jdx changed the title feat(config): default minimum release age to 24h feat(config): default release age and warn on hidden versions Jun 9, 2026
Comment thread src/config/settings.rs

@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: 1

🧹 Nitpick comments (4)
src/backend/mod.rs (2)

174-183: ⚡ Quick win

Remove redundant import.

Line 175 imports parse_into_timestamp locally, but it's already imported at module scope on line 20.

♻️ Suggested cleanup
 pub fn hidden_by_date(&self, before: Timestamp) -> bool {
-    use crate::duration::parse_into_timestamp;
     match &self.created_at {
         Some(ts) => match parse_into_timestamp(ts) {
🤖 Prompt for 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.

In `@src/backend/mod.rs` around lines 174 - 183, The local use statement importing
parse_into_timestamp inside the hidden_by_date method is redundant because
parse_into_timestamp is already imported at module scope; remove the inner "use
crate::duration::parse_into_timestamp;" line from the hidden_by_date method
(leaving the rest of the function intact) so the method uses the module-scoped
parse_into_timestamp when converting created_at to a Timestamp.

191-208: ⚡ Quick win

Consider refactoring to avoid double timestamp parsing.

When a version has a valid created_at timestamp that passes the date filter (created < before), this code parses the timestamp twice:

  1. Inside hidden_by_date at line 177
  2. Again at line 199 to check if parsing fails

The second parse at line 199 will only fail if hidden_by_date returned false due to a parse error, meaning both parses will fail and the trace will log. For the common case where the timestamp is valid and passes the filter, we parse successfully twice, which is wasteful.

♻️ Potential optimization

One approach: have filter_by_date parse once and share the result:

 pub fn filter_by_date(versions: Vec<Self>, before: Timestamp) -> Vec<Self> {
     versions
         .into_iter()
         .filter(|v| match &v.created_at {
-            Some(ts) => {
-                if v.hidden_by_date(before) {
-                    false
-                } else {
-                    if crate::duration::parse_into_timestamp(ts).is_err() {
-                        trace!("Failed to parse timestamp: {}", ts);
-                    }
-                    true
-                }
-            }
+            Some(ts) => match parse_into_timestamp(ts) {
+                Ok(created) => {
+                    if created >= before {
+                        false  // hidden
+                    } else {
+                        true   // include
+                    }
+                }
+                Err(_) => {
+                    trace!("Failed to parse timestamp: {}", ts);
+                    true  // include by default
+                }
+            },
             None => true,
         })
         .collect()
 }

This preserves the exact same behavior (including the trace) but parses each timestamp only once.

🤖 Prompt for 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.

In `@src/backend/mod.rs` around lines 191 - 208, The filter_by_date implementation
currently parses each version.created_at twice (once inside hidden_by_date and
once again via parse_into_timestamp), so change filter_by_date to parse
created_at only once: for each version with Some(ts) call
crate::duration::parse_into_timestamp(ts) once, match on that Result and if
Ok(parsed_ts) compare parsed_ts to the before Timestamp to decide whether to
filter (same logic as hidden_by_date), and if Err(_) emit the same trace and
keep the version; alternatively, refactor hidden_by_date to accept a pre-parsed
timestamp (e.g., hidden_by_date_parsed) and reuse the parse result from
filter_by_date to avoid double parsing while preserving current behavior.
src/cli/ls_remote.rs (1)

126-142: ⚡ Quick win

Consider consolidating prefix filtering to avoid duplicate work.

The current implementation filters by prefix twice:

  1. Lines 130-134: Filter, clone, and collect to count hidden versions
  2. Line 141: Filter by prefix again for the final output

This could be more efficient by filtering once:

♻️ Potential optimization
 let all_versions = plugin.list_remote_versions_with_info(config).await?;
+let prefix_matched: Vec<_> = all_versions
+    .into_iter()
+    .filter(|v| matches_prefix(&v.version))
+    .collect();
 let hidden_versions = before_date
     .map(|before| {
-        VersionInfo::count_hidden_by_date(
-            &all_versions
-                .iter()
-                .filter(|v| matches_prefix(&v.version))
-                .cloned()
-                .collect::<Vec<_>>(),
-            before,
-        )
+        VersionInfo::count_hidden_by_date(&prefix_matched, before)
     })
     .unwrap_or_default();
-let versions = filter_versions_by_date(all_versions, before_date)
-    .into_iter()
-    .filter(|v| matches_prefix(&v.version))
-    .collect::<Vec<_>>();
+let versions = filter_versions_by_date(prefix_matched, before_date);
🤖 Prompt for 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.

In `@src/cli/ls_remote.rs` around lines 126 - 142, The prefix filtering is done
twice; fix by filtering all_versions once into a single Vec (e.g., create
filtered_versions = all_versions.iter().filter(|v|
matches_prefix(&v.version)).cloned().collect::<Vec<_>>()), then pass
filtered_versions.clone() (or references) into VersionInfo::count_hidden_by_date
for hidden_versions and use filtered_versions as the input to compute versions
after filter_versions_by_date; update uses of all_versions in the surrounding
block to reference filtered_versions where appropriate (symbols: all_versions,
filtered_versions, VersionInfo::count_hidden_by_date, filter_versions_by_date,
matches_prefix, versions).
src/cli/upgrade.rs (1)

631-671: 💤 Low value

Consider renaming for clarity: this method applies the global/per-tool cutoff, not "unfiltered" latest.

The method name unfiltered_latest_for_upgrade suggests it retrieves versions without any release-age filtering, but the implementation calls list_versions_matching, which resolves the cutoff from global and per-tool settings (via the default minimum_release_age = "24h" or configured values).

The current behavior is correct for the intended warning logic: comparing CLI-flag-filtered versions against default/global-filtered versions. However, the name might confuse future maintainers who expect "unfiltered" to mean "no date filtering at all."

💡 Alternative naming

Consider renaming to something like:

  • default_filtered_latest_for_upgrade
  • baseline_latest_for_upgrade
  • globally_filtered_latest_for_upgrade

Or add a comment explaining that "unfiltered" means "without the CLI override, but still respecting global/per-tool cutoffs."

🤖 Prompt for 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.

In `@src/cli/upgrade.rs` around lines 631 - 671, Rename or clarify the method
currently named unfiltered_latest_for_upgrade to avoid implying it ignores
release-age cutoffs; either rename it to a clearer identifier like
baseline_latest_for_upgrade or globally_filtered_latest_for_upgrade, and update
all callers to the new name (or alternatively add a clear doc comment above
unfiltered_latest_for_upgrade stating that it applies global/per-tool cutoff via
list_versions_matching and respects minimum_release_age). Make sure references
to symbols in the body (e.g., list_versions_matching, list_remote_versions,
split_version_prefix, prefixed_latest_query, and ToolVersion::request) remain
correct after the rename or that the new doc comment explains that “unfiltered”
means “without CLI override but still respecting global/per-tool cutoffs.”
🤖 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 `@e2e/cli/test_install_before`:
- Around line 42-47: The test asserts a "newer jq release" warning but never
ensures jq is already installed at an older version, making the check flaky;
before running the upgrade dry-run (the call to mise upgrade jq
--minimum-release-age ... --dry-run) ensure the environment has a known older jq
installed (for example by invoking mise install jq@2018-01-01 or the equivalent
mise install jq --version <old-date>) so the upgrade path will deterministically
emit the "newer jq release" warning, then run the existing output capture and
assert_contains on the upgrade output.

---

Nitpick comments:
In `@src/backend/mod.rs`:
- Around line 174-183: The local use statement importing parse_into_timestamp
inside the hidden_by_date method is redundant because parse_into_timestamp is
already imported at module scope; remove the inner "use
crate::duration::parse_into_timestamp;" line from the hidden_by_date method
(leaving the rest of the function intact) so the method uses the module-scoped
parse_into_timestamp when converting created_at to a Timestamp.
- Around line 191-208: The filter_by_date implementation currently parses each
version.created_at twice (once inside hidden_by_date and once again via
parse_into_timestamp), so change filter_by_date to parse created_at only once:
for each version with Some(ts) call crate::duration::parse_into_timestamp(ts)
once, match on that Result and if Ok(parsed_ts) compare parsed_ts to the before
Timestamp to decide whether to filter (same logic as hidden_by_date), and if
Err(_) emit the same trace and keep the version; alternatively, refactor
hidden_by_date to accept a pre-parsed timestamp (e.g., hidden_by_date_parsed)
and reuse the parse result from filter_by_date to avoid double parsing while
preserving current behavior.

In `@src/cli/ls_remote.rs`:
- Around line 126-142: The prefix filtering is done twice; fix by filtering
all_versions once into a single Vec (e.g., create filtered_versions =
all_versions.iter().filter(|v|
matches_prefix(&v.version)).cloned().collect::<Vec<_>>()), then pass
filtered_versions.clone() (or references) into VersionInfo::count_hidden_by_date
for hidden_versions and use filtered_versions as the input to compute versions
after filter_versions_by_date; update uses of all_versions in the surrounding
block to reference filtered_versions where appropriate (symbols: all_versions,
filtered_versions, VersionInfo::count_hidden_by_date, filter_versions_by_date,
matches_prefix, versions).

In `@src/cli/upgrade.rs`:
- Around line 631-671: Rename or clarify the method currently named
unfiltered_latest_for_upgrade to avoid implying it ignores release-age cutoffs;
either rename it to a clearer identifier like baseline_latest_for_upgrade or
globally_filtered_latest_for_upgrade, and update all callers to the new name (or
alternatively add a clear doc comment above unfiltered_latest_for_upgrade
stating that it applies global/per-tool cutoff via list_versions_matching and
respects minimum_release_age). Make sure references to symbols in the body
(e.g., list_versions_matching, list_remote_versions, split_version_prefix,
prefixed_latest_query, and ToolVersion::request) remain correct after the rename
or that the new doc comment explains that “unfiltered” means “without CLI
override but still respecting global/per-tool cutoffs.”
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a0d3376d-027d-4b64-b771-6b1b7f4e42fc

📥 Commits

Reviewing files that changed from the base of the PR and between 18b6c38 and c0f3593.

📒 Files selected for processing (6)
  • e2e/cli/test_install_before
  • e2e/cli/test_ls_remote
  • src/backend/mod.rs
  • src/cli/ls_remote.rs
  • src/cli/upgrade.rs
  • src/toolset/outdated_info.rs
✅ Files skipped from review due to trivial changes (1)
  • src/toolset/outdated_info.rs

Comment thread e2e/cli/test_install_before
Comment thread src/cli/upgrade.rs
Comment thread src/install_before.rs

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

♻️ Duplicate comments (1)
src/config/settings.rs (1)

289-291: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Preserve env-over-file precedence for the deprecated alias.

These builder calls only normalize install_before on the CLI partials. If a user exports MISE_INSTALL_BEFORE=7d and a config file sets minimum_release_age="3d", sb.load() will keep both fields, and set_hidden_configs() later preserves the lower-precedence canonical value because minimum_release_age is already populated. Please normalize the env alias before layering too, and add a regression for that env/file combination.

Also applies to: 305-307

🤖 Prompt for 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.

In `@src/config/settings.rs` around lines 289 - 291, The CLI env alias needs
normalization before layering so env-over-file precedence is preserved: call
normalize_hidden_config_aliases on the CLI partial (the value from
CLI_SETTINGS.lock().unwrap().clone().unwrap_or_default()) before passing it into
the builder’s preloaded() / preloaded_env() layering logic (i.e., ensure
normalize_hidden_config_aliases runs prior to sb.load()/preloaded), so that
deprecated env keys like MISE_INSTALL_BEFORE map to the canonical field
(install_before/minimum_release_age) before file values are applied; update the
code paths that currently normalize only install_before (the preloaded(...)
calls around normalize_hidden_config_aliases at lines shown and similar calls at
305-307) and add a regression test that sets the env var
(MISE_INSTALL_BEFORE=7d) and a config file with minimum_release_age="3d" and
asserts the env value wins after set_hidden_configs().
🤖 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.

Duplicate comments:
In `@src/config/settings.rs`:
- Around line 289-291: The CLI env alias needs normalization before layering so
env-over-file precedence is preserved: call normalize_hidden_config_aliases on
the CLI partial (the value from
CLI_SETTINGS.lock().unwrap().clone().unwrap_or_default()) before passing it into
the builder’s preloaded() / preloaded_env() layering logic (i.e., ensure
normalize_hidden_config_aliases runs prior to sb.load()/preloaded), so that
deprecated env keys like MISE_INSTALL_BEFORE map to the canonical field
(install_before/minimum_release_age) before file values are applied; update the
code paths that currently normalize only install_before (the preloaded(...)
calls around normalize_hidden_config_aliases at lines shown and similar calls at
305-307) and add a regression test that sets the env var
(MISE_INSTALL_BEFORE=7d) and a config file with minimum_release_age="3d" and
asserts the env value wins after set_hidden_configs().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d69e956f-010a-4644-ac8d-8246c05c694e

📥 Commits

Reviewing files that changed from the base of the PR and between bf72cd0 and d81673b.

📒 Files selected for processing (3)
  • src/backend/mod.rs
  • src/cli/ls_remote.rs
  • src/config/settings.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/cli/ls_remote.rs

Comment thread src/install_before.rs
Comment thread src/install_before.rs Outdated
@jdx jdx force-pushed the feat/default-min-release-age branch 2 times, most recently from 50d80ec to e394605 Compare June 9, 2026 02:25
Comment thread src/install_before.rs
Comment thread src/cli/upgrade.rs Outdated
Comment thread src/cli/upgrade.rs

@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 3f09da5. Configure here.

Comment thread src/toolset/toolset_install.rs
@jdx jdx enabled auto-merge (squash) June 9, 2026 03:03
@jdx jdx merged commit 103e4bc into main Jun 9, 2026
35 checks passed
@jdx jdx deleted the feat/default-min-release-age branch June 9, 2026 03:11
jdx pushed a commit that referenced this pull request Jun 11, 2026
…version resolution

The built-in 24h minimum_release_age default (#10279) set a before_date on
every resolution, which disabled the installed-version fast paths for all
non-prefer-offline commands (mise which, mise use, ...). Every invocation
then fetched remote version lists for the entire toolset, turning ~75ms
shell-startup calls into ~2.5s each (reported as ~65s shell startups in
discussion #10308).

The built-in default now only gates which versions remote resolution may
pick — its actual purpose of delaying new installs. Explicit cutoffs (the
--minimum-release-age flag, a per-tool minimum_release_age option, or the
minimum_release_age setting) keep their date-aware resolution semantics
from #9269, since opting in was a deliberate choice there.

- install_before: cutoff resolution reports a BeforeDateSource
  (Provided/Explicit/Default) alongside the timestamp
- ResolveOptions: new before_date_from_default flag, set via a shared
  apply_before_date_for_tool helper; the should_filter_installed_versions
  gates in resolve_version/resolve_prefix require an explicit cutoff
- e2e: test_which_no_remote_fetch pins the regression (fails on unfixed
  code); test_hook_env_no_remote_fetch guards the prefer-offline paths so
  hook-env can never start fetching remote versions

Fixes #10308

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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