You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Re-files #46, which was closed on 2026-07-03 with no fix commit, no closing PR, and no comment (verified via the issue timeline — the closed event has a null commit_id). Both halves of the original finding are still present on main (e37b326):
Restore aborts on the first failed install.internal/packages/snapshot.go:171-179 — installPackagesreturns on the first npm install -g error, so one broken/renamed/yanked package kills the migration of every package after it. A user upgrading with 30 globals where feat(detector): implement Volta detection with mocked tests #3 fails loses 27 packages silently.
Migration report is never written.internal/packages/report.go (MigrationReport, PackageResult, Save()) has zero call sites anywhere in the repo — the entire file is dead code. platform.DataDir()/reports/ exists as a documented data dir but nothing ever writes to it.
How to fix
Part 1 — continue past failures, collect results
Rework installPackages to loop all packages, recording a PackageResult per package instead of returning early:
Context cancellation must still abort the loop (the ctx.Err() check) — only per-package npm failures continue.
Part 2 — wire up the migration report
Restore / RestoreFromSnapshot return the []PackageResult (or accept a callback) so the caller can build the report.
In internal/cli/upgrade.go's restore step (~line 309-317), construct a MigrationReport{Timestamp, Manager, FromVersion (oldVersion), ToVersion (new default), Results} and Save() it to platform.DataDir()/reports/ — on both success and partial failure.
Print the report path in the post-restore summary, especially when some packages failed, so the user knows what to re-install by hand.
Sentinel semantics (decide + document)
upgrade.go:100-103 removes the sentinel only when restoreSucceeded. With partial failures now possible, define: partial failure keeps the sentinel (restoreSucceeded = false), so the interrupted-upgrade replay path stays available — but the report still gets written either way. Document this in the sentinel arming comment.
Tests
Table-driven test with the runShell seam failing on package N of M → asserts packages N+1..M were still attempted and the returned results carry the right statuses.
Test that a report file lands in reports/ with correct Results on partial failure.
Test that context cancellation aborts mid-loop (results truncated, error is ctx.Err()).
Acceptance criteria
One failing global package no longer prevents the rest from migrating.
Every nodeup upgrade migration writes a report file to platform.DataDir()/reports/; path surfaced to the user on partial failure.
internal/packages/report.go has real call sites (no longer dead code).
Sentinel behavior on partial failure is deliberate and documented.
Summary
Re-files #46, which was closed on 2026-07-03 with no fix commit, no closing PR, and no comment (verified via the issue timeline — the
closedevent has a nullcommit_id). Both halves of the original finding are still present onmain(e37b326):Restore aborts on the first failed install.
internal/packages/snapshot.go:171-179—installPackagesreturns on the firstnpm install -gerror, so one broken/renamed/yanked package kills the migration of every package after it. A user upgrading with 30 globals where feat(detector): implement Volta detection with mocked tests #3 fails loses 27 packages silently.Migration report is never written.
internal/packages/report.go(MigrationReport,PackageResult,Save()) has zero call sites anywhere in the repo — the entire file is dead code.platform.DataDir()/reports/exists as a documented data dir but nothing ever writes to it.How to fix
Part 1 — continue past failures, collect results
Rework
installPackagesto loop all packages, recording aPackageResultper package instead of returning early:Context cancellation must still abort the loop (the
ctx.Err()check) — only per-package npm failures continue.Part 2 — wire up the migration report
Restore/RestoreFromSnapshotreturn the[]PackageResult(or accept a callback) so the caller can build the report.internal/cli/upgrade.go's restore step (~line 309-317), construct aMigrationReport{Timestamp, Manager, FromVersion (oldVersion), ToVersion (new default), Results}andSave()it toplatform.DataDir()/reports/— on both success and partial failure.Sentinel semantics (decide + document)
upgrade.go:100-103removes the sentinel only whenrestoreSucceeded. With partial failures now possible, define: partial failure keeps the sentinel (restoreSucceeded = false), so the interrupted-upgrade replay path stays available — but the report still gets written either way. Document this in the sentinel arming comment.Tests
runShellseam failing on package N of M → asserts packages N+1..M were still attempted and the returned results carry the right statuses.reports/with correctResultson partial failure.ctx.Err()).Acceptance criteria
nodeup upgrademigration writes a report file toplatform.DataDir()/reports/; path surfaced to the user on partial failure.internal/packages/report.gohas real call sites (no longer dead code).