github jdx/mise v2026.11.0
v2026.11.0: Task locks for shared resources, safer lockfiles, and `.agents/skills` as the default skills directory

4 hours ago

Tasks can now declare a lock, so runs that share a resource take turns, even across worktrees and terminals. This release also adds opt-in npm provenance verification, dotfile dependencies, and mise bootstrap --update for global config checkouts. Lockfiles get stricter: --locked now requires checksums, and safe mode no longer trusts project lockfiles. The planned removal of minimum-version reading from go.mod and CMakeLists.txt also lands here. See Breaking Changes before you upgrade.

Added

  • Task lock. When several agents or terminals run tasks at the same time, they can collide on resources that exist only once on the machine, such as a shared test database, a simulator or a GPU. A task can now name the locks it needs, and a run that needs a lock someone else holds waits for it. #15242 (@jdx)

    • A string name is shared by every task on the machine that uses it. lock = true locks the task within its repository, and all worktrees of that repository share the lock.
    • mise takes the lock just before the first command, after dependencies, skip_if, sources and cache checks, so a skipped task never waits. A task waiting for a lock gives up its --jobs slot, so other tasks keep running. Nested mise run calls inherit their parent's locks and don't deadlock on them.
    • mise run --lock-timeout 5m, or the task.lock_timeout setting, makes a task fail after waiting that long. The error names the run holding the lock. mise tasks info and --dry-run show the locks a task takes.
    • Locks are OS file locks (flock, or LockFileEx on Windows), so they are released when the holder exits, even if it is killed. This feature is not experimental.
    [tasks.test]
    run = "pytest"
    lock = "test-db"
    
    [tasks."e2e:ios"]
    run = "./scripts/e2e-ios.sh"
    lock = ["ios-simulator", "test-db"]
    [test] waiting for lock test-db, held by test in ~/src/shop-pr-42 (pid 4121, for 3.0s)
    
  • npm provenance verification (opt-in). With npm.provenance = true, or the provenance category of paranoid, mise install verifies the Sigstore provenance an npm: tool's package publishes, against the sha512 integrity that the install locks. Packages that publish no provenance install as before. This only works with the embedded aube package manager, and only the tool's own package is checked, not its dependencies. #15240 (@jdx, @kndoshn)

    [settings.npm]
    provenance = true
  • Dotfile depends. A [dotfiles] entry or a [dotfile_groups] group can list the groups or entry targets it depends on. mise dot apply writes those first, in rounds. This lets an entry such as a mode = "fnox" file or a template that reads a file use files created in the same apply, so a new machine needs only one apply. mise reports an error for a cycle or an unknown name. If a round fails, mise stops and lists the entries it didn't apply. #15277 (@jdx, @owpac)

    [dotfiles]
    "~/.workrc" = { mode = "fnox", depends = ["secrets"] }
    "~/.npmrc" = { mode = "fnox", depends = ["~/.ssh/encryption_ed25519"] }
  • mise bootstrap --update pulls the global config checkout. Without --from or --adopt, --update fast-forwards the Git checkout that holds ~/.config/mise. This works whether the directory is linked into a checkout by --config-dir or is a checkout itself from --adopt. Bootstrap then runs again from the new commit. mise accepts only fast-forwards, so a diverged checkout stops the run. --dry-run prints the git pull --ff-only instead of running it. #15286 (@jdx, @owpac)

  • Skills default to .agents/skills. When skills.dir is unset, mise skills sync and skills.auto_sync use .agents/skills if it exists, or else an existing .claude/skills. If neither exists, mise creates .agents/skills and makes .claude/skills a symlink to it, so Claude Code finds the skills too. Projects that already sync into .claude/skills keep doing so. #15278 (@jdx, @canac)

  • mise use -g syncs skills into your home directory. With skills.auto_sync on, mise use -g links the skills of global tools into the skills directory under your home directory. mise install and mise upgrade keep those links current from any directory. mise skills sync --global now links only tools from the global and system config, not every tool active in the current directory. #15276 (@jdx, @canac)

  • Registry: kotlin-toolchain (JetBrains' Kotlin Toolchain, formerly Amper) #15234 (@ggoggam), headscale #15282 (@i-api), and tailscale #15298 (@i-api).

Fixed

Tasks

  • On Windows, a multi-line run or run_windows script under cmd.exe ran only its first line and exited 0. It now runs every line, and a later exit /b fails the task. Each line still runs whatever the previous line's exit code was, and %VAR% is expanded once for the whole script. #15238 (@jdx)
  • With jobs > 1, mise run could hang forever after a task failed while other tasks were still queued. #15268 (@jdx)
  • Monorepo tasks run from the root, such as mise run //subproject:task, now get the variables the subproject's daemons export, such as API_PORT. An explicit [env] value for the same key still wins. #15217 (@jdx, @dotellie)
  • mise tasks validate no longer reports a run_windows-only task as having no command when you run it on macOS or Linux. #15258 (@jdx, @ryan-pip)
  • A mistyped command such as mise lsit now suggests mise list. Hidden internal commands are never suggested. #15260 (@jdx)

Tools and backends

  • A tool requested as latest with prerelease = true now runs the newest installed version, prereleases included. Before, it kept running the stable release behind the latest symlink. This affects mise x, PATH, which, where and bin-paths. #15259 (@jdx, @ywenhao)
  • pypi: tools now install when mise has no Python interpreter. mise warns and installs from the version alone. --locked installs and tools that select a specific python still fail. #15287 (@jdx)
  • pypi: tools from an SSH GitHub source with only Git tags and no releases now list versions when a GitHub token is configured. #15291 (@jdx)
  • mise daemons status, stop, logs, prune and tui no longer fail with "No version is set for shim: pitchfork" when the project doesn't pin pitchfork in [tools]. #15237 (@jdx, @brodienguyen)

Lockfiles

  • mise lock no longer fails when a Java vendor publishes no build for one of the lockfile's platforms, such as Oracle GraalVM on linux-arm64-musl. It skips that platform. #15269 (@jdx)
  • mise lock now prints "Pruned …" only after it writes the lockfile, so a run that fails partway no longer claims a prune that didn't happen. #15267 (@jdx)

Config

  • A .config/, mise/ or .mise/ directory that is its own Git repository, such as a dotfiles checkout, loads again as config for the directory around it. Before, its config was dropped without any message. #15224 (@jdx, @mdt3r)

Dotfiles and bootstrap

  • Several Windows dotfiles fixes: #15248, #15249, #15252, #15254 (@jdx)
    • mise dot unapply now removes junctions and directory symlinks instead of failing with "Access is denied".
    • symlink-each entries now prune dangling links even after their state file is lost.
    • A symlink entry that fell back to a copy is reported as copied, both in apply output and in mise dot status.
    • Changing the letter case of a target path no longer creates a second state file.
  • After mise dot apply fails partway, the next apply now continues without needing mise dot recover. Links made before the failure are recorded, and empty directories the apply created are removed. #15253 (@jdx)
  • mise bootstrap no longer records history checkpoints for runs that change nothing. This also makes no-op runs faster. #15246 (@jdx)
  • With homebrew_metadata = true, the Homebrew cask handoff now also writes INSTALL_RECEIPT.json, so brew info shows the install date and that you requested the cask. The handoff now requires Homebrew 6.0.22 or newer. With an older Homebrew, mise keeps ownership of the cask. #15274 (@jdx)
  • brew-cask upgrades no longer skip auto-updating apps whose installed version still matches the install record but has a different number of version components than the cask version, such as Firefox 158.0.1 vs 159.0. #15226 (@waynehoover)
  • mise bootstrap packages apply no longer reinstalls every mas: app while Spotlight is rebuilding its index, for example after a macOS update. If mas list comes back empty but App Store apps are installed, mise stops with an error instead. #15228 (@waynehoover)
  • mise's background services on Windows, such as the tool updater, now keep running after you close the Windows Terminal window. This needs Windows 11 24H2 or Windows Server 2025 or later, and a window can still appear briefly at startup. #15265 (@CCJK123)

Performance

  • Embedded vfox plugins such as gcloud no longer re-run their env hook on every prompt. The cached result is dropped when an environment variable the hook read changes. Plugins can also use the new mise.cache Lua module to name files their result depends on, or to turn caching off. #15227 (@jdx, @leipert)
  • mise hook-env no longer re-parses uv.lock sidecars for pypi:/pipx: tools on every run. In one report this took about 320 ms of a 395 ms prompt. #15230 (@jdx)
  • "latest" tools no longer check their install directory several extra times per prompt. #15295 (@jdx, @disrupted)

Packaging

  • apt update against the mise Debian repository should no longer fail with "File has unexpected size … Mirror sync in progress?". Repository metadata is now cached for 5 minutes instead of a day, and the index files are uploaded before the files that describe them. #15219 (@jdx)

Security

  • Locked mode requires checksums. mise install --locked now fails before downloading if a lockfile entry for the current platform has a URL but no checksum. #15285 (@jdx, @deniskern)
  • Safe mode never trusts a project lockfile. With MISE_SAFE=1, a project's mise.lock can no longer choose download URLs, even in a trusted project or in CI. mise downloads from the backend's own release and still checks the lockfile's checksum. --locked now accepts entries that have a checksum but no URL. #15235 (@jdx)
  • A project .miserc.toml can no longer drop global environments. A project file could set env = [] without being trusted and turn off environments chosen in the global or system miserc.toml, such as one that sets paranoid = true. A project's env_conf_d could do the same to global conf.d fragments. Project files can still add environments. #15281 (@jdx, @adam-moss)

Breaking Changes

  • go.mod and CMakeLists.txt minimum versions are no longer read. This removal was announced in 2026.8.10. A bare go 1.21 line in go.mod no longer selects a Go version. mise reads only a toolchain goX.Y.Z line, or an exact go X.Y.Z line when there is no toolchain line. cmake_minimum_required in CMakeLists.txt is ignored, and the hidden idiomatic_version_file_ignore_minimum_versions setting is removed. To migrate, add toolchain go1.21.4 to go.mod, or set the version in .go-version or mise.toml. For cmake, set the version in mise.toml. #15266 (@jdx)
  • --locked fails on lockfile entries without checksums. This affects lockfiles written by hand or by an older mise. Run mise lock once to fill in the checksums. Lockfiles in lockfile_mode = "merge" are exempt. To keep the old behavior, set locked_require_checksum = false, or set it to true to require checksums in merge mode too. The setting will start warning in 2027.10.0. #15285
  • Global environments now also load where a project .miserc.toml used to replace them. To leave one out for a single run, use -E or MISE_ENV. #15281

New Contributors

Full Changelog: v2026.10.25...v2026.11.0

💚 Sponsor mise

mise is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.

If mise saves you or your team time, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep mise fast, free, and independent.

Don't miss a new mise release

NewReleases is sending notifications on new releases.