Refactor setup commands and enhance version management features - #83
Conversation
…nfirm utility Breaking change: --remove, --use-path, --use-existing are replaced by subcommands (remove, path, use <path>). Hidden backward-compat flags emit deprecation warnings (remove in v0.3.0). - Add src/util/confirm.rs: confirm_or_auto / confirm_or_bail (non-TTY auto-confirms) - Replace 16 inline TTY-check sites across 8 files - Normalize all --yes help text to 'Skip confirmation prompts' - Redesign NuSetupArgs with Option<NuAction> subcommand + positional VERSION - Add NuSetupArgs constructors for internal callers (doctor, nu_pin_offer) - Add 8 CLI-parse tests + update all affected test assertions - Update README, CHANGELOG, docs/numan-doctor.md, AGENTS.md
- Prevent PATH subcommand from deleting active managed Nu - Guard loader overwrites with ownership verification - Reject incompatible legacy Nu setup flags
Fix remaining --version references in resolve.rs, tighten doctor_test assertion, add skip_path guard to legacy compat path, add negative tests for version+subcommand and legacy use_existing+skip_path.
Change 'your PATH Nu is not touched' to 'your existing Nu is not replaced' since setup nu does modify PATH by default.
Introduce a new `numan use <version>` command path (`cli`, `main`, and `cmd::use_cmd`) as a post-1.0 placeholder that currently fails with a clear guidance message to use `numan setup nu <version>`. Update roadmap/docs to reflect the consolidated plan filename, add post-1.0 side-by-side Nu management notes, and register the new command module in AGENTS metadata.
Fixed 4 file(s) based on 5 unresolved review comments. Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
- Add src/nu/version_manager.rs for active version tracking - Active version stored in <root>/nu_state/active-version.json - Installed versions discovered from <root>/tools/nushell/<version>/ - Helpers: read/write active version, list installed, check if installed - Migration logic for legacy single-binary installs - Update bootstrap.rs to install to versioned subdirectories - install_from_archive() now writes to <root>/tools/nushell/<version>/ - managed_nu_binary() delegates to version_manager for active version - Automatically sets newly installed version as active - Implement numan use command - numan use <version> — switch to specific installed version - numan use latest — switch to newest installed version - numan use list — show all installed versions with active marker - Validates version is installed before switching - Provides helpful hints when version not found - Auto-migrates legacy single-binary installs on first run - Remove Commands::Use from root init exclusion (now needs root) All 425 tests pass, clippy clean, fmt applied.
- Validate and normalize Nu versions - Propagate legacy migration errors - Handle invalid active markers gracefully - Avoid parent path panic - Update Nu setup guidance
…ompilation errors
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
…master for setup.rs, snapshot.rs, cli.rs, bootstrap.rs; keep full use_cmd.rs implementation
wip: puts numan use + confirm-gate UX + migration journal + reconcile into a single baseline commit so the pr-migrate-legacy-installs split can branch off a known starting point. This commit does not represent either final PR; the next step surgically extracts migration into its own branch and reverts the migration hunks on feature/numan-use.
- Pull 251 lines of migration fs (Legacy*Detector type alias, LegacyPostCreateHook type alias, detect_legacy_version, migrate_legacy_install, migrate_legacy_install_with_detector, parse_nu_version_from_output) plus 8 migrate_legacy_* tests + create_legacy_binary helper + production_detector_prefers_version_metadata_file + test_parse_nu_version_from_output into a new crate::nu::migrate_legacy module. - Add 'pub(crate)' to write_active_marker (now consumed by crate::nu::migrate_legacy). - Retarget use_cmd.rs to call crate::nu::migrate_legacy::migrate_legacy_install. - Re-register the module in src/nu/mod.rs. This is the pr-migrate-legacy-installs half of the split: phase 1 cleanup + journaled transaction for legacy single-binary -> versioned installation with self-healing and numan doctor --fix reconciliation. Co-authored-by: Codebuff <noreply@codebuff.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Threads addressed (changed in code):
* chatgpt PR69 S1A src/cmd/doctor.rs
doctor --fix auto-repair branch reacquires the root mutation lock
before invoking migration_journal::reconcile, so a concurrent numan use
cannot race the same Prepared -> Renamed transition.
* copilot PR69 VwSra src/state/migration_journal.rs
PendingMigration::load now hard-fails on unknown schema_version
(no longer silently coerces); a future journal variant cannot be
misinterpreted as the current one.
* cubic PR69 UzV src/cmd/use_cmd.rs
numan use latest preserves an existing off-tree binary_path when the
selected version matches the existing marker (the previous write
dropped binary_path, breaking resolution of 'setup nu use <path>'
picks).
* chatgpt PR69 S08 src/cmd/setup.rs
Both setup nu use <path> callsites now write_active_version_with_binary
after register_existing_nu succeeds, so numan use list reports the
registered off-tree binary as the active selection.
* chatgpt PR69 S09 src/cmd/setup.rs
remove_managed_nu and remove_managed_nu_if_present clear the
active-version marker at the top of the function, before deleting the
managed tree, so the marker cannot dangle at a binary just removed.
* cubic PR69 UzU src/nu/migrate_legacy.rs
parse_nu_version_from_output delegates to core::nu_version::NuVersion
::parse, which already strips build-hash suffixes ('0.113.1 (abc123)').
Manually-placed legacy binaries whose --version includes a build hash
migrate cleanly; bare '0.113.1' falls through to the semver-only path.
* cubic PR69 UzM src/nu/paths.rs
find_nu_executable_with_root now propagates read_active_version errors
instead of silently treating them as 'no marker'. A present-but-malformed
active-version marker surfaces loudly so numan init / setup loader
cannot silently fall back to PATH Nu.
* cubic PR69 UzG src/nu/migrate_legacy.rs
migrate_legacy_install_with_detector refuses to scan / mutate under a
symlinked managed directory; the rename or filesystem-truth cleanup
cannot redirect outside $NUMAN_ROOT.
Build / clippy / fmt / lib tests (cargo test --lib -> 468 passed; 0
failed).
Deferred (kept as Discussion for the user):
* qodo S26 — anyhow -> thiserror migration in public APIs (large refactor)
* chatgpt S05 — refresh cached NuPaths when switching versions (architectural)
* chatgpt S06 — preserve ~/.local/bin/nu symlink through migration (UX)
* cubic UzO — HOME-mutating test seam isolation (test infra refactor)
* chatgpt VwS04 — install pinned releases to versioned layout (overlaps
PR67 VpJrb on the other branch; left for cross-PR consolidation)
* outdated=True threads: VwSrT, VwSrk, VwS1C (skip per skill rules)
Per user instruction: push to PR branch only; do NOT merge the PR.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…ate cfg(test) read_active_version After the rebase of pr-migrate-legacy-installs onto current master, the detection function was missing its closing bracket (rustc reported an 'unclosed delimiter' at detect_legacy_version that reached EOF), and cargo clippy -- -D warnings surfaced three warnings on lib build: - src/nu/bootstrap.rs:5 — std::io::IsTerminal imported but unused now that the IsTerminal check moved to src/util/confirm.rs. - src/nu/migrate_legacy.rs:19 — read_active_version imported at module scope but only used inside #[cfg(test)]. Restrict to #[cfg(test)] so the lib build is clean and tests still reach it via a sibling import inside the tests module. - src/cmd/doctor.rs:1342 — value assigned to lock was never read; the reacquire from the chatgpt PR69 S1A path is the canonical call. Drop the redundant 'lock = Some(...)' line. Also re-runs cargo fmt to absorb any drift carried in by the rebase. Validation: cargo build OK; cargo clippy -- -D warnings clean; cargo test --lib 468 passed / 0 failed on pr-migrate-legacy-installs.
Co-authored-by: Anthony Thompson <github@trackdub.com>
|
Addressed remaining still-valid review findings on this branch (pushed through Fixed: bootstrap selective Skipped:
Validation: |
Guard migration/reconcile against symlinked tools ancestors and version dirs via assert_managed_nushell_layout + version_dir checks. Reconcile migration journals before off-PATH Nu repair in doctor --fix so one pass can clear Prepared orphans. Add use latest dangling off-tree self-heal coverage. Co-authored-by: Anthony Thompson <github@trackdub.com>
|
Verified the Codex/Qodo/Cubic/Greptile findings against current HEAD and fixed only what was still valid ( Already fixed (skipped):
Fixed now:
Validation: |
Resolve conflicts between landed #69/#71 on master and PR83 review hardening. Prefer PR83 safety gates (layout containment, exact latest dest, off-tree marker preserve, list read-only) while adopting master's DoctorArgs --scan API, confirm alias, and snapshot-before-setup wiring. Co-authored-by: Anthony Thompson <github@trackdub.com>
|
Merged Simple (auto-combined): Complicated (resolved with explicit choices):
Validation: |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
src/nu/bootstrap.rs (2)
758-792: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winPinned short-circuit can persist one version while pointing PATH at another.
Inside this branch
dest.is_file()is already true, so the requested version exists on disk. Theeffectivechain still prefers the active marker, thenlatest_installed_version, and only falls back todest. When the requested version differs from the active or latest version, the two writes disagree:
- Line 797 and Line 799 prepend and persist
effective, which is the other version's binary.- Line 805 writes
normalized_version, which is the requested version.Example:
numan setup nu 0.113.0 --yeswith0.113.0installed and0.114.0active leaves the marker at0.113.0while~/.local/bin/nuresolves to0.114.0.Since
execute_nu_setupnow converts the unpinned flow into a pinned one,destis always the intended target here. Usedestdirectly for PATH handling whennormalized_versionisSome, and keep the active/latest fallback only for theNone(legacy placeholder) case. Add a test covering the mismatch case.🤖 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/nu/bootstrap.rs` around lines 758 - 792, The already-installed short-circuit in the setup flow uses the active/latest binary for pinned installs, which can disagree with the requested version recorded by normalized_version. Update the effective-path selection to use dest directly whenever normalized_version is Some, retaining the active/latest fallback only when it is None; ensure PATH prepend/persistence and the installed marker all target the requested binary. Add a test covering an installed requested version that differs from the active version.
1271-1325: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winThis inline unit test spawns a real
nuprocess and readsPATHwithout the guard.Two problems:
validate_nushell_binary(p)at Line 1277 and insideregister_existing_nuexecute the copied host Nushell binary. Inline unit tests must not spawn a realnu. Move this case totests/with the#[ignore = "requires real Nu binary on $PATH — run in platform acceptance job"]marker used intests/setup_nu_test.rs, or inject a validation seam.- The test snapshots
PATHat Line 1297 and compares at Line 1314 without holdingPathRestoreGuard.src/util/test_paths.rsserializesPATHaccess throughPATH_MUTEX. A sibling test that mutatesPATHin parallel makes this assertion flaky.As per coding guidelines: "Unit tests must use
FakeCandidateRunneror injectable registrars and must not spawn a realnuprocess." and "Real-Nu acceptance tests must be marked#[ignore]".🤖 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/nu/bootstrap.rs` around lines 1271 - 1325, Move register_existing_nu_refuses_non_tty_without_yes_before_path_mutation out of the inline unit tests into the real-Nu acceptance tests, marking it #[ignore = "requires real Nu binary on $PATH — run in platform acceptance job"] like tests/setup_nu_test.rs; alternatively inject a validation seam using FakeCandidateRunner so no real nu process runs. If the test remains inline, guard all PATH snapshot and comparison access with PathRestoreGuard/PATH_MUTEX to prevent concurrent PATH mutations.Source: Coding guidelines
src/cmd/setup.rs (2)
1019-1035: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winTests that drive
execute_use_existingedit the developer shell profile. The shared root cause isexecute_use_existingat Line 513 ofsrc/cmd/setup.rs, which hardcodesskip_path: false.register_existing_nuthen callspersist_path_dir, andPathRestoreGuardrestores only the processPATH, not the profile edit.
src/cmd/setup.rs#L1019-L1035: run these inline unit tests through a seam that setsskip_path: true, and apply the same change to the cases at Line 1062 and Line 1083.tests/setup_nu_test.rs#L141-L195: confirm this test carries the#[ignore = "requires real Nu binary on $PATH — run in platform acceptance job"]marker used at Line 368 and Line 430, so the profile write happens only in the acceptance job.🤖 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/cmd/setup.rs` around lines 1019 - 1035, Update the inline tests invoking execute_use_existing in src/cmd/setup.rs ranges 1019-1035, 1062, and 1083 to use a seam that sets skip_path: true, preventing profile edits while preserving test behavior. In tests/setup_nu_test.rs range 141-195, ensure the test has the same ignore marker used by the acceptance-only cases at lines 368 and 430, so profile writes occur only in the platform acceptance job.Source: Coding guidelines
563-566: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the duplicate non-TTY guard.
Line 532 already calls
require_tty_or_yes(yes, "managed Nushell removal")at function entry. Line 566 repeats the same call with the same label. The second call is unreachable as a distinct gate and it emits a second audit line for one operation.♻️ Proposed cleanup
- // Fail closed in non-interactive sessions without `--yes`. Removal wipes - // the managed tree; `confirm_or_bail`'s non-TTY auto-confirm must not - // silently proceed. - require_tty_or_yes(yes, "managed Nushell removal")?; confirm_or_bail(🤖 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/cmd/setup.rs` around lines 563 - 566, Remove the duplicate require_tty_or_yes call near the managed Nushell removal logic, keeping the existing guard at the function entry. Preserve the single initial validation and avoid emitting a second audit record for the same operation.AGENTS.md (1)
152-160: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDocument the new destructive-operation confirmation convention.
remove.rsandsnapshot.rsdelete/rollback now require interactive confirmation or--yesbefore destructive work. "Key Conventions" and "Architecture Rules" do not mention this. Add a bullet describing whenconfirm_or_bail/require_tty_or_yes_with_seamgates apply, alongside the existing snapshot-before-mutation rule.As per coding guidelines: "Update documentation and
AGENTS.mdwhen project structure or conventions change."🤖 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 `@AGENTS.md` around lines 152 - 160, Update AGENTS.md’s Key Conventions and Architecture Rules documentation to describe the destructive-operation confirmation convention: remove and snapshot delete/rollback flows must gate destructive work with confirm_or_bail or require_tty_or_yes_with_seam, accepting interactive confirmation or --yes, alongside the existing snapshot-before-mutation rule.Source: Coding guidelines
src/cmd/doctor.rs (1)
1474-1507: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winDelete the leftover duplicate migration-repair block.
The new migration-repair block at lines 1188-1216 already reconciles the pending migration journal, with correct handling of
Ok(Some(_)),Ok(None), andErr(e). The old block at lines 1474-1507 checks the exact samejournal.migration_pendingcondition again, later in the same function. This block is unchanged from before this PR and was never removed.This causes two problems:
- When the first block's
reconcile()call fails but leaves the journal file intact (for example, a symlink guard or an orphan-directory removal failure), this second block runsmigration_journal::reconcile(root)a second time and pushes a secondRepairRecordwith the same id"journal.migration_repaired". The JSONrepairsarray then contains two records for the same repair, which misleads any automation parsing that report.- At Line 1484,
PendingMigration::load(root)?uses?instead of pushing aFailedrecord. Every other branch inapply_repairsaccumulates a record on error. Ifloaditself errors here, this line discards everyRepairRecordalready collected inrecords(layout repairs, active-version-marker repair, registry repair) and aborts the whole function.A prior review already flagged this exact early-return/
Ok(_)pattern and it was marked addressed. The fix, however, appears to have been implemented as a new block placed earlier in the function, and this old block was left in place. Delete it; block 1188-1216 already covers this repair correctly.🐛 Proposed fix: remove the dead block
- // The migration journal path is self-healing in normal use (top of - // `migrate_legacy_install_with_detector`); the doctor repair is the - // catch-up for users who ran `numan doctor --fix` without ever calling - // `numan use`. Gating on `PendingMigration::load(...).is_some()` keeps - // the Applied record honest — re-runs of `doctor --fix` produce no - // second repair. - if findings - .iter() - .any(|f| f.id == "journal.migration_pending" && f.severity == Severity::Warn) - { - if PendingMigration::load(root)?.is_none() { - return Ok(records); - } - let id = "journal.migration_repaired".to_string(); - // chatgpt PR69 S1A: reacquire the root mutation lock before the - // self-healing reconcile so concurrent `numan use` cannot race the - // journal stage advance + directory rename the same way AGENTS.md - // requires install/remove/activate/deactivate/numan-use to. - let _migration_repair_lock = acquire_mutation_lock(root)?; - match migration_journal::reconcile(root) { - Ok(_) => records.push(RepairRecord { - id, - status: RepairStatus::Applied, - reason: None, - }), - Err(e) => records.push(RepairRecord { - id, - status: RepairStatus::Failed, - reason: Some(e.to_string()), - }), - } - } - Ok(records) }🤖 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/cmd/doctor.rs` around lines 1474 - 1507, Delete the duplicate migration-repair block guarded by the `journal.migration_pending` warning check near the end of `apply_repairs`. Keep the earlier migration-repair logic that handles `reconcile` outcomes and records failures, so this function performs the repair only once and preserves previously accumulated `RepairRecord` values.
♻️ Duplicate comments (1)
src/state/migration_journal.rs (1)
194-199: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFinish the
nu_binary_name()consolidation and use one import style.Two of the four binary-name sites now call the shared helper, but the consolidation stopped short:
- Line 195 uses a function-local
use.- Line 260 uses the fully qualified path.
- Line 323 (
Renamedbranch) still hand-rollsif cfg!(windows) { "nu.exe" } else { "nu" }.- The test helper
bin_name()at lines 355-361 hand-rolls it again.Add
nu_binary_nameto the existingcrate::nu::version_managerimport list and call it at all four sites. A future change to the binary name then has one edit site.♻️ Proposed consolidation
fn versioned_binary_present(root: &Path, version: &str) -> bool { - use crate::nu::version_manager::nu_binary_name; version_install_dir(root, version) .join(nu_binary_name()) .is_file() }Extend the existing import (near line 39):
use crate::nu::version_manager::{ nu_binary_name, read_active_version, version_install_dir, versioned_nu_dir, write_active_version, };Then replace line 260 with
let bin_name = nu_binary_name();, replace the conditional at line 323 withlet bin_name = nu_binary_name();, and delete the test helperbin_name()in favor ofnu_binary_name().🤖 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/state/migration_journal.rs` around lines 194 - 199, Consolidate all binary-name lookups in migration_journal.rs by adding nu_binary_name to the existing crate::nu::version_manager import list, removing the function-local and fully qualified imports, and calling the shared helper in versioned_binary_present, the line-260 usage, the Renamed branch, and the tests. Delete the hand-rolled test helper bin_name() and use nu_binary_name() directly at its call sites.
🤖 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 `@src/cmd/setup.rs`:
- Around line 536-555: Update the active-version cleanup flow around
read_active_version so malformed or unreadable markers are reported and returned
as an anyhow error instead of being treated as clearable. Preserve the existing
handling for absent markers and invalid managed-tree selections, but when
clearing is requested, propagate clear_active_version(root) failures with
context rather than discarding them; ensure the enclosing setup operation
returns anyhow::Result.
In `@src/nu/bootstrap.rs`:
- Around line 717-729: Update the release-install flow around
`fetch_latest_release`, `execute_nu_setup_with_installer`, and `install_version`
to pass the already-fetched `GitHubRelease` through to installation, avoiding a
second `fetch_release_by_tag` request. Add descriptive anyhow context to both
the `Client::builder().build()` and `fetch_latest_release(&client)` error paths
while preserving the existing pinned version behavior.
- Around line 712-729: Defer latest-release resolution in execute_nu_setup until
after the consent and already-installed gates, so non-interactive callers
without --yes and already-installed unpinned setups do not access the network.
Keep the unpinned destination gate behavior intact, then resolve the tag and
pass it into the installer closure used by execute_nu_setup_with_installer. Add
coverage matching
execute_nu_setup_refuses_non_tty_without_yes_and_skips_installer for an unpinned
setup that verifies no network request occurs.
---
Outside diff comments:
In `@AGENTS.md`:
- Around line 152-160: Update AGENTS.md’s Key Conventions and Architecture Rules
documentation to describe the destructive-operation confirmation convention:
remove and snapshot delete/rollback flows must gate destructive work with
confirm_or_bail or require_tty_or_yes_with_seam, accepting interactive
confirmation or --yes, alongside the existing snapshot-before-mutation rule.
In `@src/cmd/doctor.rs`:
- Around line 1474-1507: Delete the duplicate migration-repair block guarded by
the `journal.migration_pending` warning check near the end of `apply_repairs`.
Keep the earlier migration-repair logic that handles `reconcile` outcomes and
records failures, so this function performs the repair only once and preserves
previously accumulated `RepairRecord` values.
In `@src/cmd/setup.rs`:
- Around line 1019-1035: Update the inline tests invoking execute_use_existing
in src/cmd/setup.rs ranges 1019-1035, 1062, and 1083 to use a seam that sets
skip_path: true, preventing profile edits while preserving test behavior. In
tests/setup_nu_test.rs range 141-195, ensure the test has the same ignore marker
used by the acceptance-only cases at lines 368 and 430, so profile writes occur
only in the platform acceptance job.
- Around line 563-566: Remove the duplicate require_tty_or_yes call near the
managed Nushell removal logic, keeping the existing guard at the function entry.
Preserve the single initial validation and avoid emitting a second audit record
for the same operation.
In `@src/nu/bootstrap.rs`:
- Around line 758-792: The already-installed short-circuit in the setup flow
uses the active/latest binary for pinned installs, which can disagree with the
requested version recorded by normalized_version. Update the effective-path
selection to use dest directly whenever normalized_version is Some, retaining
the active/latest fallback only when it is None; ensure PATH prepend/persistence
and the installed marker all target the requested binary. Add a test covering an
installed requested version that differs from the active version.
- Around line 1271-1325: Move
register_existing_nu_refuses_non_tty_without_yes_before_path_mutation out of the
inline unit tests into the real-Nu acceptance tests, marking it #[ignore =
"requires real Nu binary on $PATH — run in platform acceptance job"] like
tests/setup_nu_test.rs; alternatively inject a validation seam using
FakeCandidateRunner so no real nu process runs. If the test remains inline,
guard all PATH snapshot and comparison access with PathRestoreGuard/PATH_MUTEX
to prevent concurrent PATH mutations.
---
Duplicate comments:
In `@src/state/migration_journal.rs`:
- Around line 194-199: Consolidate all binary-name lookups in
migration_journal.rs by adding nu_binary_name to the existing
crate::nu::version_manager import list, removing the function-local and fully
qualified imports, and calling the shared helper in versioned_binary_present,
the line-260 usage, the Renamed branch, and the tests. Delete the hand-rolled
test helper bin_name() and use nu_binary_name() directly at its call sites.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: e9a36d98-907c-451e-83ea-bdbdd3f38863
📒 Files selected for processing (13)
AGENTS.mdsrc/cmd/doctor.rssrc/cmd/remove.rssrc/cmd/setup.rssrc/cmd/snapshot.rssrc/cmd/use_cmd.rssrc/nu/bootstrap.rssrc/nu/migrate_legacy.rssrc/nu/version_manager.rssrc/state/migration_journal.rssrc/util/fs_safety.rstests/doctor_test.rstests/setup_nu_test.rs
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
tonythethompson/QuickShell(manual)tonythethompson/numan(manual)tonythethompson/dependency-chain-substrate(manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: Greptile Review
- GitHub Check: Real-Nu acceptance (windows-latest)
- GitHub Check: Analyze (rust)
🧰 Additional context used
📓 Path-based instructions (18)
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
**/*: Use Serena's semantic, symbol-aware tools as the primary tools for reading and editing code; use built-in Read, Glob, Grep, and Edit only under the stated exceptions.
Before editing a code file, inspect its symbol overview, read the specific symbols being changed, and edit them with Serena's symbol-aware tools.
Understand the existing code before changing it and make the smallest change that satisfies the request; avoid unrelated cleanup, premature abstractions, unnecessary error handling, feature flags, and compatibility shims.
Prefer editing existing files over creating new ones, and never create Markdown or README files unless explicitly requested.
For exploratory questions, provide a 2–3 sentence recommendation with the main tradeoff and do not implement until the user agrees.
For UI or frontend changes that cannot be tested in a browser, explicitly state that browser testing was not performed rather than claiming success.
Address security issues when discovered, including injection, XSS, SQL injection, path traversal, and secret leaks.
Pause and obtain confirmation before destructive, hard-to-reverse, externally visible, or third-party-upload actions, including deleting files or branches, dropping tables, force-pushing, modifying CI/CD, posting externally, or uploading content.
When blocked, investigate the root cause instead of bypassing it with --no-verify, --force, or deletion; investigate unfamiliar files, branches, and configuration before deleting them.
Only commit when explicitly asked; do not proactively update git configuration or push changes.
Do not skip Git hooks unless explicitly asked; if a pre-commit hook fails, fix the issue, re-stage, and create a new commit rather than amending.
Stage files by name rather than using git add -A or git add ., to avoid accidentally including secrets or large binaries.
Use a HEREDOC for commit messages, and add co-author attribution only when the user explicitly requests the exact trailer.
Do not force-pus...
Files:
tests/doctor_test.rsAGENTS.mdsrc/util/fs_safety.rssrc/cmd/snapshot.rssrc/cmd/use_cmd.rstests/setup_nu_test.rssrc/cmd/remove.rssrc/nu/version_manager.rssrc/state/migration_journal.rssrc/cmd/doctor.rssrc/cmd/setup.rssrc/nu/bootstrap.rssrc/nu/migrate_legacy.rs
**/*.{js,jsx,ts,tsx,py,java,go,rs,rb,php,c,cpp,h,hpp,cs,swift,kt,kts}
📄 CodeRabbit inference engine (CLAUDE.md)
Add comments only when the WHY is non-obvious; do not narrate what the code does, reference the current task, or reference the PR in comments.
Files:
tests/doctor_test.rssrc/util/fs_safety.rssrc/cmd/snapshot.rssrc/cmd/use_cmd.rstests/setup_nu_test.rssrc/cmd/remove.rssrc/nu/version_manager.rssrc/state/migration_journal.rssrc/cmd/doctor.rssrc/cmd/setup.rssrc/nu/bootstrap.rssrc/nu/migrate_legacy.rs
!**/.env,!**/credentials.json,!**/*.pem
📄 CodeRabbit inference engine (CLAUDE.md)
Do not commit files that appear to contain secrets, including .env, credentials.json, and PEM files; warn before doing so even if explicitly requested.
Files:
tests/doctor_test.rsAGENTS.mdsrc/util/fs_safety.rssrc/cmd/snapshot.rssrc/cmd/use_cmd.rstests/setup_nu_test.rssrc/cmd/remove.rssrc/nu/version_manager.rssrc/state/migration_journal.rssrc/cmd/doctor.rssrc/cmd/setup.rssrc/nu/bootstrap.rssrc/nu/migrate_legacy.rs
**/*.rs
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*.rs: Use the Rust 2021 edition.
Useanyhow::Resultwith.context(...)in application code; usethiserrorfor library error types that callers match on.
Useclapderive macros for CLI definitions.
Useserdewithserde_jsonortomlfor serialization.
Function parameters must use&Path, not&PathBuf.
Library code must not panic; error paths should returnanyhow::Resultwith context where appropriate.
Add or update tests for behavior changes, including relevant failure paths.
New mutating code paths must acquire the mutation lock viaacquire_mutation_lock(root)and snapshot the lockfile before writes.
Lockfile, journal, and state-file JSON writes must usewrite_json_atomic.
numan installmust write only to$NUMAN_ROOT; it must not invoke Nu or register plugins/autoloads.
Onlyactivateanddeactivatemay modify Nu integration state.
Treat the lockfile as the authoritative source of truth; derived projections such as autoload state must not be authoritative.
Install payloads under versioned, content-addressed paths and never overwrite them in place.
Never overwrite foreign autoload files; respectOWNERSHIP_MARKER.
Pass plugin paths through environment variables only; do not use runtime interpolation in Nu program strings.
**/*.rs: All CI gates must pass:cargo test,cargo clippy -- -D warnings, andcargo fmt --check.
Every mutating command—includinginstall,remove,update,gc, and futurenupm import—must callacquire_mutation_lock(root).
Lockfiles, journals, and state files must usewrite_json_atomic; partial writes are not allowed.
Pending activation, autoload, and lifecycle journals must be stored under$NUMAN_ROOT/state/.
Module autoload identity must match all four fields: Nu executable hash, Nu version, vendor autoload directory, and managed file path; the lockfilemodule_activationvalue is authoritative.
Never overwrite foreign autoload files; respectOWNERSHIP_MARKER.
Pass paths to Nu only throu...
Files:
tests/doctor_test.rssrc/util/fs_safety.rssrc/cmd/snapshot.rssrc/cmd/use_cmd.rstests/setup_nu_test.rssrc/cmd/remove.rssrc/nu/version_manager.rssrc/state/migration_journal.rssrc/cmd/doctor.rssrc/cmd/setup.rssrc/nu/bootstrap.rssrc/nu/migrate_legacy.rs
**/*.{rs,toml}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Run and keep
cargo fmt/rustfmtclean, and ensurecargo clippy -- -D warningspasses.Use
serde/serde_jsonfor JSON serialization andtomlfor configuration serialization.
Files:
tests/doctor_test.rssrc/util/fs_safety.rssrc/cmd/snapshot.rssrc/cmd/use_cmd.rstests/setup_nu_test.rssrc/cmd/remove.rssrc/nu/version_manager.rssrc/state/migration_journal.rssrc/cmd/doctor.rssrc/cmd/setup.rssrc/nu/bootstrap.rssrc/nu/migrate_legacy.rs
**/*.{rs,nu}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*.{rs,nu}: Real-Nu acceptance tests must be marked#[ignore]and should be run when changes affect activation or nupm import; unit tests must not spawn realnuand should use injectable seams such asFakeCandidateRunneror registrars.
The nupm integration must be read-only towardNUPM_HOME, must not executebuild.nu, and must not perform bidirectional synchronization.Unit tests must use
FakeCandidateRunneror injectable registrars and must not spawn a realnuprocess.
Files:
tests/doctor_test.rssrc/util/fs_safety.rssrc/cmd/snapshot.rssrc/cmd/use_cmd.rstests/setup_nu_test.rssrc/cmd/remove.rssrc/nu/version_manager.rssrc/state/migration_journal.rssrc/cmd/doctor.rssrc/cmd/setup.rssrc/nu/bootstrap.rssrc/nu/migrate_legacy.rs
**/*.{rs,md}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Match existing naming, module layout, and documentation level in the file being edited; update
AGENTS.md,docs/, or command help when structure, conventions, or user-visible behavior changes.Tests must cover failure modes, not only successful execution.
Files:
tests/doctor_test.rsAGENTS.mdsrc/util/fs_safety.rssrc/cmd/snapshot.rssrc/cmd/use_cmd.rstests/setup_nu_test.rssrc/cmd/remove.rssrc/nu/version_manager.rssrc/state/migration_journal.rssrc/cmd/doctor.rssrc/cmd/setup.rssrc/nu/bootstrap.rssrc/nu/migrate_legacy.rs
**/*.{rs,md,toml}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Use the repository's established serialization and module conventions rather than introducing unrelated refactors.
Files:
tests/doctor_test.rsAGENTS.mdsrc/util/fs_safety.rssrc/cmd/snapshot.rssrc/cmd/use_cmd.rstests/setup_nu_test.rssrc/cmd/remove.rssrc/nu/version_manager.rssrc/state/migration_journal.rssrc/cmd/doctor.rssrc/cmd/setup.rssrc/nu/bootstrap.rssrc/nu/migrate_legacy.rs
tests/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Keep integration tests under
tests/, place unit tests inline with source modules, and test platform-specific code with mock platforms.
Files:
tests/doctor_test.rstests/setup_nu_test.rs
**/*.md
📄 CodeRabbit inference engine (REVIEW.md)
Update documentation and
AGENTS.mdwhen project structure or conventions change.
Files:
AGENTS.md
src/util/fs_safety.rs
📄 CodeRabbit inference engine (AGENTS.md)
src/util/fs_safety.rs: Managed files must containOWNERSHIP_MARKER; useassert_managed_file_ownedto prevent overwriting foreign files.
Serialize mutations withacquire_mutation_lock(root); the second acquisition for the same root must fail immediately.
Files:
src/util/fs_safety.rs
src/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Use
cargo fmtformatting and keep Clippy clean undercargo clippy -- -D warnings.
Files:
src/util/fs_safety.rssrc/cmd/snapshot.rssrc/cmd/use_cmd.rssrc/cmd/remove.rssrc/nu/version_manager.rssrc/state/migration_journal.rssrc/cmd/doctor.rssrc/cmd/setup.rssrc/nu/bootstrap.rssrc/nu/migrate_legacy.rs
src/cmd/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Use
clapderive-based CLI definitions and preserve the lowercasenumanbinary name.
Files:
src/cmd/snapshot.rssrc/cmd/use_cmd.rssrc/cmd/remove.rssrc/cmd/doctor.rssrc/cmd/setup.rs
src/cmd/{activate,deactivate,update,remove,init,doctor,nupm}.rs
📄 CodeRabbit inference engine (AGENTS.md)
Create a state snapshot before install, update, remove, activate, deactivate,
init --refresh, nupm import, or doctor repair mutations.
Files:
src/cmd/remove.rssrc/cmd/doctor.rs
src/nu/version_manager.rs
📄 CodeRabbit inference engine (AGENTS.md)
nu_state/active-version.jsonis the sole authority for the selected managed Nu version; writes occur through managed setup/use operations.
Files:
src/nu/version_manager.rs
src/state/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
src/state/**/*.rs: Write JSON state files atomically usingwrite_json_atomic(a same-directory tempfile followed by persist) to prevent partial-write corruption.
The lockfile is authoritative for module activation;autoload-state.jsonis only a derived fast-check projection.
Files:
src/state/migration_journal.rs
src/state/{journal,plugin_deactivate_journal,migration_journal,lifecycle_journal,autoload_journal,autoload_recovery}.rs
📄 CodeRabbit inference engine (AGENTS.md)
Journal state transitions must be written atomically and reconciled after interruption according to their defined recovery stages.
Files:
src/state/migration_journal.rs
src/nu/migrate_legacy.rs
📄 CodeRabbit inference engine (AGENTS.md)
Migration reconciliation must refuse to act when
tools/nushellis a symlink or reparse point, retain unreconciled journals on failure, and let filesystem truth take precedence over journal stage.
Files:
src/nu/migrate_legacy.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: tonythethompson/numan
Timestamp: 2026-08-03T18:49:02.290Z
Learning: Source builds require explicit user consent before cloning/building, with separate consent scopes.
Learnt from: CR
Repo: tonythethompson/numan
Timestamp: 2026-08-03T18:49:02.290Z
Learning: Lockfiles must pin immutable artifact paths, and cached artifacts must remain while referenced.
Learnt from: CR
Repo: tonythethompson/numan
Timestamp: 2026-08-03T18:49:02.290Z
Learning: Use imperative commit subjects shorter than 72 characters, branches named `feature/description` or `fix/description`, never force-push `master`, and squash feature merges.
Learnt from: CR
Repo: tonythethompson/numan
Timestamp: 2026-08-03T18:49:02.290Z
Learning: Write tests first, run the full test suite, and update `AGENTS.md` when project structure or conventions change.
🔍 Remote MCP GitHub Copilot
Additional review context
- PR
#82is open and unmerged withmergeable_state: blocked; its current head is7f487d1. - All reported CI checks currently pass, including Clippy, Format, MSRV, platform tests, Real-Nu acceptance, CodeQL, and package checks.
- Review history identified important invariants around:
numan uselocking and snapshot behavior.- Bare
setup nufetching and selecting the latest release. - Atomicity of managed-tree deletion and active-marker cleanup.
- Migration probe timeout/deadlock avoidance.
- Actionable doctor repair hints.
- Preservation and validation of off-tree binary markers.
- Several earlier findings were marked resolved/outdated, but the review history still documents these as areas requiring verification against the final diff—especially latest-install marker persistence, mutation locking, migration reconciliation failure handling, and interactive removal confirmation.
- The final diff adds a regression test ensuring doctor skips off-PATH repair when a managed tree exists, moves snapshot confirmation before lock acquisition, and adds legacy-version discovery tests.
🔇 Additional comments (43)
src/nu/version_manager.rs (5)
48-81: LGTM!
350-363: LGTM!
404-410: LGTM!
467-483: LGTM!
776-867: LGTM!src/state/migration_journal.rs (5)
40-40: LGTM!Also applies to: 150-157
234-245: LGTM!
279-279: LGTM!
660-664: LGTM!
691-807: LGTM!src/util/fs_safety.rs (2)
248-269: LGTM!
464-487: LGTM!src/nu/migrate_legacy.rs (7)
79-99: LGTM!
122-138: LGTM!
147-155: LGTM!Also applies to: 168-186
208-212: LGTM!Also applies to: 236-241, 254-260
288-306: LGTM!
343-354: LGTM!
494-527: LGTM!Also applies to: 595-604, 626-725
src/nu/bootstrap.rs (6)
24-35: LGTM!
231-267: LGTM!
468-508: LGTM!
680-698: LGTM!
826-862: LGTM!
1109-1269: LGTM!src/cmd/setup.rs (6)
12-23: LGTM!
198-236: LGTM!
418-435: LGTM!Also applies to: 508-525
576-586: LGTM!Also applies to: 620-620
942-962: LGTM!
1061-1118: Good coverage of the off-tree marker contract.The assertions pin
binary_path,active_nu_binaryresolution, andlist_installed_versionsinclusion. That matches the three-way contract insrc/nu/version_manager.rs. Theskip_pathconcern is tracked at Line 1019.tests/setup_nu_test.rs (4)
13-13: LGTM!Also applies to: 56-95
101-116: LGTM!
235-241: LGTM!Also applies to: 368-372, 430-434
462-500: LGTM!src/cmd/doctor.rs (2)
143-152: LGTM!Also applies to: 325-354, 1128-1150, 1188-1216, 1257-1266, 2418-2447, 2458-2502
346-353: 🗄️ Data Integrity & IntegrationNo stale
--fixhint remains.src/cmd/use_cmd.rs (1)
79-115: LGTM!Also applies to: 118-154, 163-194, 296-400, 475-523
tests/doctor_test.rs (2)
33-117: LGTM!
2458-2502: 🎯 Functional CorrectnessNo change needed.
MigrationStageuses#[serde(rename_all = "snake_case")], so"stage": "prepared"deserializes correctly and can reach the schema-version mismatch check.src/cmd/remove.rs (1)
38-45: LGTM!src/cmd/snapshot.rs (1)
287-334: LGTM!AGENTS.md (1)
67-68: LGTM!Also applies to: 147-147
assert_contained builds non-existent paths from canonical root so macOS /var vs /private/var no longer fails migrate_legacy. Unpinned setup nu requires TTY/--yes before GitHub fetch and reuses the release. setup nu path|use force-gates only when a real managed install exists so doctor --fix off-PATH works with empty/partial managed dirs.
When setup nu remove finds no managed tree, treat read_active_version errors as diagnostic failures (leave the marker for numan doctor --fix) instead of silently deleting MalformedMarker/ReadMarker state. Propagate clear_active_version failures for legitimate stale-marker cleanup.
This pull request introduces robust support for migration journal handling during the legacy Nushell (Nu) single-binary to versioned layout transition. It ensures that migration journals are properly surfaced, reconciled, and repaired by
numan doctor --fix, addressing both pending and malformed journal states. Additionally, the tests and documentation are updated to reflect and verify these behaviors.Migration journal handling and recovery:
src/cmd/doctor.rs: Surfaces unreadable or malformedmigration-journal.jsonfiles asjournal.migration_invalidfindings with error severity and a manual repair tier, ensuring users are notified and guided to delete stale journals. Pending migration journals are now reported asjournal.migration_pendingwith auto repair vianuman useordoctor --fix. The repair logic reacquires the root mutation lock and reconciles the migration state, cleaning up both the journal and any empty subdirectories. [1] [2]src/cmd/doctor.rs: Adds comprehensive tests for migration journal states, including detection of pending journals, successful reconciliation withdoctor --fix, and error reporting for malformed journals.Documentation updates:
src/AGENTS.md,docs/plans/consolidated-multi-repo-roadmap.md,tests/AGENTS.md: Documents the migration journal’s role, its reconciliation process, and the associated repair hints and error handling. Updates clarify thatnuman usenow triggers legacy migration when needed and that backfill waves will be driven by this mechanism. [1] [2] [3] [4]Test and codebase improvements:
src/cmd/doctor.rs: Refactors test utilities for restoring thePATHenvironment variable, replacing a custom mutex and guard with a shared utility. [1] [2] [3]Minor fixes and consistency:
src/cmd/doctor.rs: Updates function signatures and repair function references for clarity and correctness, such as usingsetup::execute_nu_repairas the default for repair operations. [1] [2]src/cmd/remove.rs: Adds a missing import (std::io::IsTerminal).These changes significantly improve the reliability and user experience of migration state handling and recovery during Nu version transitions.