Skip to content

fix(lockfile): merge platform data when a tool's options newly diverge from an empty on-disk entry - #10710

Merged
jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:fix-lockfile-options-divergence
Jul 2, 2026
Merged

jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:fix-lockfile-options-divergence

Conversation

@JamBalaya56562

@JamBalaya56562 JamBalaya56562 commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes the lockfile data loss reported in discussion #10564: when a backend starts attaching deterministic lockfile options to a version that previously round-tripped with no options (e.g. java's shorthand_vendor, always attached for a shorthand request like java = "26"), mise lock fragments the tool into duplicate [[tools.java]] siblings and a subsequent run drops the entry holding all the platform checksums/URLs — leaving core:java pinned with zero platform info, so mise install --locked fails on every platform.

Root cause

Both lockfile writers key/match entries by exact (version, options) equality:

  • set_platform_info (the mise lock path): the freshly-resolved (26.0.1, {shorthand_vendor}) no longer matches the on-disk (26.0.1, {}) entry, so a brand-new sibling entry is pushed and the old entry's checksums/URLs become unreferenced.
  • merge_tool_entries (the mise install/mise use lockfile-update path): worse — an existing entry whose key no longer matches anything freshly resolved is silently dropped from the merged result.

Fix

When there is no exact (version, options) match and the freshly-resolved options are non-empty, adopt the existing entry for the same version that still has empty options: merge its platforms in and update its options in place, instead of creating (or losing) a sibling.

  • The adoption target is picked by smallest key so the output stays deterministic even if several optioned entries share a version (HashMap iteration order is not).
  • preserve_absent_tool_entries (monorepo root lockfiles) skips resurrecting an adopted empty-options entry as a duplicate sibling. The skip is snapshot-based so fragmented entries belonging to tools not resolved in the current run are still preserved verbatim (no new data-loss path).
  • Scoped narrowly to the empty→non-empty transition from the report. Options that differ in other ways (e.g. a real release_type change) continue to produce separate entries — those genuinely describe different downloadable artifacts. The reverse transition (an entry losing its options, e.g. after a mise downgrade) is intentionally not handled: resurrecting options the current mise no longer reports would be wrong.

This matches suggested direction 2 from the report ("merge platforms from the existing entry into the new one rather than spawning a sibling").

Tests

Three regression tests reproducing the reported java shorthand_vendor scenario:

  • test_set_platform_info_adopts_empty_options_entry_on_new_options — an entry with existing platform checksums gains options on a later mise lock; all platforms must end up on a single entry.
  • test_merge_tool_entries_adopts_empty_options_entry_on_new_options — same via the install-path merge; the existing entry's platforms must be adopted, not dropped.
  • test_preserve_absent_does_not_resurrect_adopted_empty_options_entry — monorepo preserve pass must not duplicate an adopted entry, while still preserving fragmented entries for tools not resolved in the current run.

cargo fmt --all -- --check passes locally; cargo check / test run deferred to CI.

Fixes the issue reported in discussion #10564.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed lockfile tool-entry merging to preserve existing platform-specific URLs/checksums when a tool version later starts emitting deterministic options.
    • Prevented duplicate entries and ensured platform data is adopted into the correct resolved entry instead of being dropped or orphaned.
    • Updated monorepo “preserve absent” behavior to avoid resurrecting an already-adopted empty-options sibling.
  • Tests
    • Added regression coverage for the adoption and preserve-absent scenarios.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: 31390fa1-c358-4d8b-a801-f2dd696c177f

📥 Commits

Reviewing files that changed from the base of the PR and between 45bc102 and 33369d1.

📒 Files selected for processing (1)
  • src/lockfile.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lockfile.rs

📝 Walkthrough

Walkthrough

This PR updates lockfile entry reconciliation in src/lockfile.rs so empty-options tool entries are adopted into matching non-empty-options entries instead of being duplicated or resurrected. It changes set_platform_info, merge_tool_entries, and preserve_absent_tool_entries, and adds regression tests.

Changes

Lockfile Empty-Options Adoption

Layer / File(s) Summary
set_platform_info adoption logic
src/lockfile.rs
When no exact (version, options) match exists and new options are non-empty, adopts an existing empty-options entry for the same version by updating it in-place instead of creating a sibling entry; adds a regression test verifying platforms are preserved on a single adopted entry.
merge_tool_entries dedup adoption
src/lockfile.rs
Adds documentation and logic so deduplication merges an existing empty-options entry into a freshly-resolved non-empty-options entry for the same version, selecting the adopt target deterministically and preserving platform fields; adds tests covering this merge.
preserve_absent_tool_entries duplicate prevention
src/lockfile.rs
Tracks versions that underwent adoption and skips resurrecting their empty-options sibling entries during the monorepo preserve pass, avoiding duplicates.

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

Possibly related PRs

  • jdx/mise#10547: Both PRs modify src/lockfile.rs lockfile merge behavior around reconciling tool entries and platform info across option variants.

Poem

A rabbit hopped through lockfile lines so neat,
Found empty siblings and made them meet.
One version, one burrow, no orphan in sight,
Platforms stayed cozy, all tucked in right.
🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s main fix: preserving lockfile platform data when an empty-options entry becomes a non-empty one.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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.

@greptile-apps

greptile-apps Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a lockfile data-loss bug (#10564) where a backend starting to emit deterministic options (e.g. java's shorthand_vendor) caused previously-collected platform checksums/URLs to be orphaned or silently dropped across both the mise lock and mise install code paths.

  • set_platform_info: when there is no exact (version, options) match and the incoming options are non-empty, the first same-version entry that still has empty options is adopted in place (its options updated, its platforms preserved) rather than spawning a stale sibling.
  • merge_tool_entries: an existing empty-options entry is merged into the smallest-keyed same-version freshly-resolved entry that now carries options, instead of being silently dropped from the HashMap.
  • preserve_absent_tool_entries: the monorepo-root preserve pass skips resurrecting the now-adopted empty-options entry as a duplicate sibling, while still faithfully preserving fragmented entries for tools not resolved in the current run.

Confidence Score: 5/5

The fix is narrowly scoped to the empty→non-empty options transition and does not touch any other merge paths; three regression tests cover the three mutually-dependent code sites.

All three changes work together correctly: the adoption logic in set_platform_info and merge_tool_entries is guarded by options.is_empty() on the existing entry and !options.is_empty() on the incoming/resolved entry, so it cannot fire for unrelated option changes. The preserve_absent_tool_entries skip is correctly snapshotted before the preservation loop, preventing false positives for tools resolved without the adoption path. The invariant underlying adopted_versions is explicitly documented and holds given the structure of the calling code.

No files require special attention; src/lockfile.rs is the single changed file and the logic is well-covered by the new tests.

Important Files Changed

Filename Overview
src/lockfile.rs Three targeted changes to set_platform_info, merge_tool_entries, and preserve_absent_tool_entries that handle the empty→non-empty options transition without orphaning existing platform data; three regression tests cover the core scenarios cleanly.

Reviews (2): Last reviewed commit: "fix(lockfile): merge platform data when ..." | Re-trigger Greptile

Comment thread src/lockfile.rs Resolved
…e from an empty on-disk entry

`set_platform_info` (used by `mise lock`) and `merge_tool_entries` (used by
the general `mise install`/`mise use` lockfile-update path) both key/match
lockfile entries by exact `(version, options)` equality. When a backend
starts reporting deterministic lockfile options for a version that
previously round-tripped with none (e.g. java's `shorthand_vendor`, which
is always attached for a shorthand version request), the freshly-resolved
entry no longer matches the existing on-disk entry's key:

- `set_platform_info` falls into its "no match" branch and pushes a brand
  new sibling entry, leaving the old one's checksums/URLs unreferenced.
- `merge_tool_entries` is worse: an existing entry with no matching key is
  silently dropped from the merged result entirely.

Either way, `mise lock`/`mise install` ends up fragmenting a single tool
version across sibling lockfile entries, and `mise install --locked` then
fails on any platform whose checksum only lives in the orphaned entry.

Fix: when no exact `(version, options)` match exists and the freshly
resolved `options` are non-empty, adopt an existing entry for the same
version that still has empty options — merge its platforms in and update
its options in place, instead of creating (or losing) a sibling. The
adoption target is chosen by smallest key so the output stays
deterministic, and `preserve_absent_tool_entries` (monorepo root
lockfiles) skips resurrecting an adopted empty-options entry as a
duplicate sibling while still preserving fragmented entries for tools not
resolved in the current run. Scoped narrowly to the empty-options ->
non-empty-options transition reported; options that differ in other ways
(e.g. a real `release_type` change) continue to produce separate entries,
since those genuinely describe different downloadable artifacts.

+ 3 regression tests reproducing the reported java shorthand_vendor
scenario across `set_platform_info`, `merge_tool_entries`, and the
monorepo preserve pass.

discussion jdx#10564

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JamBalaya56562
JamBalaya56562 force-pushed the fix-lockfile-options-divergence branch from 45bc102 to 33369d1 Compare July 2, 2026 07:11
@jdx
jdx merged commit 5749676 into jdx:main Jul 2, 2026
32 of 33 checks passed
@JamBalaya56562
JamBalaya56562 deleted the fix-lockfile-options-divergence branch July 2, 2026 09:07
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