Skip to content

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Nov 26, 2025

Summary

Add a fast-path check for mise hook-env that runs BEFORE expensive initialization (backend::load_tools, config loading, etc). This addresses reports of slow mise activate by optimizing the common case where nothing has changed between prompts.

Performance improvement: ~40-60ms → ~8-10ms (5x faster)

What this PR does

The fast-path check uses only the cached session data (__MISE_SESSION) to verify:

  • Session exists with valid data
  • Directory hasn't changed
  • MISE_ env vars haven't changed
  • Config file mtimes haven't changed
  • Data directory hasn't been modified (no new tool installs)

When all conditions pass, hook-env exits immediately without:

  • Initializing the logger
  • Loading all backend tools (backend::load_tools)
  • Parsing config files
  • Any other expensive initialization

Benchmark results

Scenario Before After
Nothing changed (common case) ~40-60ms ~8-10ms
Config/dir changed ~40-60ms ~40-60ms (unchanged)

Test plan

  • Verified fast-path triggers when session exists and nothing changed
  • Verified full execution happens with --force flag
  • Verified full execution happens when directory changes
  • Verified full execution happens when config files are modified
  • All existing tests pass

🤖 Generated with Claude Code


Note

Introduces a fast-path for hook-env that skips initialization when nothing changed, with supporting refactors and an e2e test validating key scenarios.

  • Performance/Behavior:
    • Add pre-init fast-path hook_env::should_exit_early_fast() and call it in Cli::run to bypass logger/config/tool loading when safe.
    • Fast-path checks: prior session exists, no --force, first precmd run, directory change, MISE_ env var changes, loaded config mtimes, data dir mtime, and ancestor config dirs mtimes.
    • Post-config early-exit should_exit_early updated to schedule hooks on dir change and to use full watch_files from config.
  • Refactors:
    • Introduce mtime_to_millis and use it in file-mod detection and session building.
    • Minor import adjustments to support new checks (file, hook_env alias in CLI).
  • Tests:
    • Add e2e/cli/test_hook_env_fast_path covering: no-change fast-path, config create/remove, directory change, and parent .config/mise config detection.

Written by Cursor Bugbot for commit f7738fc. This will update automatically on new commits. Configure here.

Copilot AI review requested due to automatic review settings November 26, 2025 18: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 adds a fast-path to skip expensive initialization in mise hook-env when the environment hasn't changed, significantly reducing execution time in the common case.

  • Introduces should_exit_early_fast() to check session, directory, env vars, and file mtimes using cached data.
  • Wires the fast-path into CLI before logger/config/tool initialization.
  • Leaves full execution path intact when changes are detected or --force is used.

Reviewed changes

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

File Description
src/hook_env.rs Adds should_exit_early_fast() performing lightweight checks using PREV_SESSION to decide early exit.
src/cli/mod.rs Calls fast-path early in CLI flow to avoid expensive initialization when appropriate.

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

return false;
}
}
} else if !config_path.exists() {
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

If metadata() fails for reasons other than non-existence (e.g., permissions), this logic continues without invalidating the fast path, potentially skipping necessary re-initialization. To be safe, any failure to read metadata should cause a full run, not an early exit.

Suggested change
} else if !config_path.exists() {
} else {

Copilot uses AI. Check for mistakes.
Comment on lines +436 to 442
// Fast-path for hook-env: exit early if nothing has changed
// This avoids expensive backend::load_tools() and config loading
if hook_env_module::should_exit_early_fast() {
return Ok(());
}
measure!("logger", { logger::init() });
check_working_directory();
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

[nitpick] The fast-path returns before check_working_directory(), suppressing the warning when the current directory is invalid. If the warning is important regardless of initialization, consider moving check_working_directory() above the fast-path or explicitly running it in the fast path.

Suggested change
// Fast-path for hook-env: exit early if nothing has changed
// This avoids expensive backend::load_tools() and config loading
if hook_env_module::should_exit_early_fast() {
return Ok(());
}
measure!("logger", { logger::init() });
check_working_directory();
check_working_directory();
// Fast-path for hook-env: exit early if nothing has changed
// This avoids expensive backend::load_tools() and config loading
if hook_env_module::should_exit_early_fast() {
return Ok(());
}
measure!("logger", { logger::init() });

Copilot uses AI. Check for mistakes.
if dir_change().is_some() {
return false;
}
// Can't exit early if MISE_ env vars changed
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

[nitpick] Please document what specific MISE_ variables are hashed and compared by have_mise_env_vars_been_modified(), and whether non-MISE_ variables that affect resolution are intentionally excluded. This clarifies the assumptions of the fast path.

Suggested change
// Can't exit early if MISE_ env vars changed
// Can't exit early if MISE_ env vars changed
// Only environment variables with the `MISE_` prefix are hashed and compared by
// `have_mise_env_vars_been_modified()`. Non-`MISE_` variables that might affect
// resolution are intentionally excluded from this check. This assumes that only
// `MISE_` variables influence the fast path's correctness.

Copilot uses AI. Check for mistakes.
use crate::{Result, backend};
use crate::{cli::args::ToolArg, path::PathExt};
use crate::{logger, migrate, shims};
use crate::{hook_env as hook_env_module, logger, migrate, shims};
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

[nitpick] Importing hook_env as hook_env_module is unconventional and can be confusing. Prefer a direct use crate::hook_env; and call hook_env::should_exit_early_fast() for clarity.

Suggested change
use crate::{hook_env as hook_env_module, logger, migrate, shims};
use crate::{hook_env, logger, migrate, shims};

Copilot uses AI. Check for mistakes.
@jdx jdx force-pushed the perf/hook-env-fast-path branch from 524b0f3 to 8630d7c Compare November 26, 2025 18:44
&& !*env::__MISE_ZSH_PRECMD_RUN
{
return false;
}
Copy link

Choose a reason for hiding this comment

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

Bug: Fast-path fails to detect space-separated reason argument

The check for --reason=precmd only matches the equals-sign format, but zsh activation scripts use --reason precmd with a space. When called with space-separated arguments, the check fails to detect the first precmd run, potentially causing the fast-path to exit early when it should run the full initialization to catch PATH modifications from shell initialization.

Fix in Cursor Fix in Web

…nged

Add a fast-path check for `mise hook-env` that runs BEFORE expensive
initialization (backend::load_tools, config loading, etc). This checks
basic conditions using only the cached session data:

- Verifies __MISE_SESSION exists with valid data
- Checks if directory changed
- Checks if MISE_ env vars changed
- Checks if config file mtimes changed
- Checks if data directory was modified

When nothing has changed (the common case), hook-env now exits in ~8-10ms
instead of ~40-60ms - a 5x speedup.

This improves shell responsiveness since hook-env runs on every prompt.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@jdx jdx force-pushed the perf/hook-env-fast-path branch from 85d8466 to b1a9171 Compare November 26, 2025 19:02
Fixes the fast-path detection to check config subdirectories like
.config/mise, .mise, and mise within each ancestor directory.
Previously, creating a config file in these subdirectories would
not be detected since parent directory mtime doesn't change.

Also adds an e2e test for the fast-path optimization.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
}
}
}
}
Copy link

Choose a reason for hiding this comment

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

Bug: Fast-path misses deleted data directory

The fast-path only checks dirs::DATA modification time when it exists, but skips the check entirely if it doesn't exist. If dirs::DATA existed in the previous session but is now deleted, the fast-path won't detect this change and may incorrectly exit early, leaving stale tool paths in the environment. The check should return false when dirs::DATA is missing to trigger a full environment update.

Fix in Cursor Fix in Web

autofix-ci bot and others added 3 commits November 26, 2025 19:14
- Use PREV_SESSION.dir instead of loaded_configs to detect valid session
  (loaded_configs can be empty when no config files exist)
- Improve e2e test to properly check fast-path behavior:
  - Verify __MISE_SESSION output when fast-path is bypassed
  - Use builtin cd to avoid _mise_hook wrapper during dir change test
  - Test directory changes, new config files, and parent .config/mise

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
if modtime > PREV_SESSION.latest_update {
return false;
}
}
Copy link

Choose a reason for hiding this comment

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

Bug: Fast-path continues when modification time unavailable

The fast-path check silently continues when metadata.modified() fails, potentially skipping necessary reloads. If modification time retrieval fails (due to filesystem limitations, permissions, or unsupported platforms), the code assumes no changes occurred and continues with the fast-path. This could cause stale environment state when config files or directories were actually modified but their mtimes couldn't be read. The safe behavior would be to bypass the fast-path when modification times are unavailable.

Additional Locations (2)

Fix in Cursor Fix in Web

jdx and others added 2 commits November 26, 2025 11:38
Previously, if dirs::DATA was deleted after being present in the
session, the fast-path would skip the check entirely and incorrectly
exit early. Now we explicitly check if the data directory is missing
and bypass fast-path to trigger a full environment update.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Reduces code duplication by extracting the SystemTime to milliseconds
conversion into a reusable helper function.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@github-actions
Copy link

github-actions bot commented Nov 26, 2025

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.11.8 x -- echo 19.3 ± 0.8 18.5 33.4 1.00
mise x -- echo 19.6 ± 0.5 18.5 21.5 1.01 ± 0.05

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.11.8 env 18.9 ± 0.7 18.0 24.8 1.00
mise env 19.1 ± 0.5 18.1 21.4 1.01 ± 0.05

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.11.8 hook-env 18.9 ± 0.5 18.0 21.4 1.00
mise hook-env 19.2 ± 0.5 18.3 22.5 1.02 ± 0.04

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.11.8 ls 16.5 ± 0.5 15.8 19.4 1.00
mise ls 16.8 ± 0.5 15.9 21.8 1.02 ± 0.04

xtasks/test/perf

Command mise-2025.11.8 mise Variance
install (cached) 108ms 109ms +0%
ls (cached) 66ms 67ms -1%
bin-paths (cached) 72ms 73ms -1%
task-ls (cached) 429ms 421ms +1%

jdx and others added 3 commits November 26, 2025 11:43
The zsh activation script uses "--reason precmd" (space-separated)
but the fast-path was only checking for "--reason=precmd" (equals).
Now handles both forms correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@jdx jdx enabled auto-merge (squash) November 26, 2025 19:57
@jdx jdx merged commit dc5a5df into main Nov 26, 2025
26 checks passed
@jdx jdx deleted the perf/hook-env-fast-path branch November 26, 2025 19:59
jdx pushed a commit that referenced this pull request Nov 27, 2025
### 📦 Registry

- add charmbracelet/crush by @ev-the-dev in
[#7075](#7075)

### 🚀 Features

- **(aqua)** add symlink_bins option to filter exposed binaries by @jdx
in [#7076](#7076)

### 🐛 Bug Fixes

- **(aqua)** skip whitespace before pipe token in template parser by
@jdx in [#7069](#7069)
- **(docs)** link github backends to github repo URLs by @SKalt in
[#7071](#7071)

### 📚 Documentation

- update node examples from 22 to 24 by @jdx in
[#7074](#7074)

### ⚡ Performance

- **(hook-env)** add fast-path to skip initialization when nothing
changed by @jdx in [#7073](#7073)

### New Contributors

- @ev-the-dev made their first contribution in
[#7075](#7075)
- @SKalt made their first contribution in
[#7071](#7071)

## 📦 Aqua Registry Updates

#### New Packages (3)

-
[`SonarSource/sonar-scanner-cli`](https://github.com/SonarSource/sonar-scanner-cli)
-
[`Stranger6667/jsonschema`](https://github.com/Stranger6667/jsonschema)
- [`peteretelej/tree`](https://github.com/peteretelej/tree)

#### Updated Packages (2)

- [`astral-sh/uv`](https://github.com/astral-sh/uv)
- [`pre-commit/pre-commit`](https://github.com/pre-commit/pre-commit)
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