Skip to content

fix(installer): report orphaned sandboxes honestly across uninstall and reinstall#6539

Merged
cv merged 5 commits into
mainfrom
fix/6520-uninstall-reinstall-orphan-sandbox
Jul 9, 2026
Merged

fix(installer): report orphaned sandboxes honestly across uninstall and reinstall#6539
cv merged 5 commits into
mainfrom
fix/6520-uninstall-reinstall-orphan-sandbox

Conversation

@Dongni-Yang

@Dongni-Yang Dongni-Yang commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

nemoclaw uninstall --yes preserves sandboxes.json but removes the gateway registration, provider registrations, and Docker image its recorded sandboxes depend on; a later reinstall then reported "Installation complete" and "Existing sandboxes were recovered and upgraded" while silently stranding the user's sandbox with no remediation path. This PR makes the whole cycle honest: the CLI derives a dedicated, observation-based orphan signal (independent of version classification, so the same-version reinstall repro is also caught), the installer consumes it to report "completed with warnings" with concrete remediation instead of false success, and uninstall warns at preserve time that the kept registry is not self-sufficient.

Related Issue

Fixes #6520

Changes

  • src/lib/actions/upgrade-sandboxes.ts: new orphan classifier — a recorded sandbox absent in any phase from the selected gateway, whose persisted binding resolves to that same gateway, is reported via the exported ORPHANED_SANDBOX_MARKER line with <name> destroy / onboard remediation. Sandboxes bound to another live gateway, exonerated by the confirming second listing, or restored by prepared-backup recovery are excluded, preserving the [All Platforms][Upgrade] v0.0.55 → v0.0.76 leaves pre-existing sandboxes stuck in Provisioning/Error — user data inaccessible until manual rebuild #6114 multi-gateway exit-0 contract. Stale listings now label version regressions with a (downgrade) suffix (describeStaleUpgrade exported for tests).
  • scripts/install.sh: recover_preexisting_sandboxes_before_onboard mirrors recovery output through a trap-registered temp log (_cleanup_files), takes the CLI's own exit status via PIPESTATUS[0] so a tee write failure cannot fake the [Ubuntu][Upgrade] installer auto-rebuild destroys existing sandboxes then fails to recreate when ambient env/session does not match the original config #5735 failure path, and greps the orphan marker into _PREEXISTING_SANDBOX_ORPHANED; print_done downgrades the banner to === Installation completed with warnings === and replaces the false recovery claim with hedged wording plus remediation; the onboarding-skip line no longer claims recovery in the orphaned case.
  • src/lib/actions/uninstall/run-plan.ts: warns at preserve time that preserved sandboxes.json records cannot be recovered automatically on reinstall (non-interactive and interactive keep paths, suppressed under --destroy-user-data); provider/sandbox delete no-ops now print already removed or unreachable instead of the contradictory Deleted … skipped.
  • Tests: test/install-orphaned-sandbox-recovery.test.ts (bash harness driving the real install.sh classification and print_done; the stub output is built from the exported marker constant so wording drift on either side fails the suite), src/lib/actions/upgrade-sandboxes-display.test.ts (downgrade labeling), five orphan-classifier cases in upgrade-sandboxes-recovery.test.ts (own-gateway orphan for current- and stale-classified records; negatives for other-gateway, reconnect-race, and recovered sandboxes), src/lib/actions/uninstall/run-plan-preserved-registry.test.ts (preserve warning on non-interactive and interactive keep paths, no-op wording fix, purge suppression).
  • Docs: reference/commands.mdx, manage-sandboxes/lifecycle.mdx, get-started/quickstart.mdx, reference/host-files-and-state.mdx updated for the preserve-time warning, the warnings install outcome, the remediation path, and the (downgrade) label.

Source-of-Truth Constraints (advisor PRA-1/6/8)

  • Invalid state: preserved sandboxes.json entries whose gateway registration, provider registrations, and Docker image were removed by a prior nemoclaw uninstall.
  • Source boundary: the uninstall command's preserve-by-default user-data contract. --yes is deliberately non-destructive; existing automation depends on it never purging user data.
  • Why the root cause is not fixed here: making uninstall/reinstall symmetric means either preserving the gateway/providers/image (defeats uninstall) or purging the registry by default (breaks the preserve contract and --yes automation). Both are behavior contracts owned by uninstall, out of scope for an honesty fix. The chosen resolution matches the issue's stated "Acceptable" bar: detect, diagnose with remediation, and never report false success.
  • Regression tests: test/install-orphaned-sandbox-recovery.test.ts (real install.sh classification + print_done, stub built from the domain marker constant), src/lib/actions/upgrade-sandboxes-recovery.test.ts (orphan classifier incl. multi-gateway/reconnect/recovered negatives and check-mode parity), src/lib/domain/maintenance/orphan-detection.test.ts, src/lib/domain/uninstall/messaging.test.ts, src/lib/actions/uninstall/run-plan-preserved-registry.test.ts.
  • Removal condition: when uninstall either preserves everything its recorded sandboxes depend on, purges the registry by default, or records gateway-removal evidence in the registry so reinstall can distinguish states — the observation-based classifier and installer marker grep can then be retired.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification:
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification:
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Verification

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run check:diff passed when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — command/result or justification: npx vitest run --project cli src/lib/actions/upgrade-sandboxes-display.test.ts src/lib/actions/upgrade-sandboxes-recovery.test.ts src/lib/actions/uninstall/ → 79 passed; npx vitest run --project integration test/install-orphaned-sandbox-recovery.test.ts test/install-preexisting-sandbox-recovery.test.ts test/install-onboard-exit.test.ts → 14 passed; npm run typecheck:cli clean; shfmt -i 2 -ci -bn -d scripts/install.sh clean
  • Applicable broad gate passed — npm test for broad runtime/test-harness changes; npm run check for repo-wide validation/coverage changes — command/result:
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Dongni Yang dongniy@nvidia.com

Summary by CodeRabbit

  • New Features
    • Added orphan-detection for preserved registry sandboxes to drive more accurate “recovered with warnings” vs “not recovered” messaging.
    • Enhanced stale-sandbox labeling (downgrade/unknown-build) and refined upgrade/recovery output to reduce ambiguity.
  • Bug Fixes
    • Uninstall and recovery messaging now better distinguishes unreachable/removal no-ops from successful deletions.
    • “Installation completed with warnings” is shown when recovery exits cleanly but sandboxes can’t be recovered.
  • Documentation
    • Updated quickstart, lifecycle, command, and state/uninstall docs with remediation guidance.
  • Tests
    • Added/expanded Vitest coverage for orphan detection, installer completion honesty, and uninstall messaging.

…nd reinstall

`nemoclaw uninstall --yes` preserves sandboxes.json by design but removes
the gateway registration, provider registrations, and Docker image its
recorded sandboxes depend on. A later reinstall could not recover those
records, yet reported "Installation complete" and "Existing sandboxes were
recovered and upgraded" — false success that silently stranded the user's
sandbox with no remediation path.

The root cause is that no layer could tell a genuinely orphaned record
apart from benign skips. Staleness could not carry the signal either: a
same-version reinstall classifies the orphan "current" and never surfaces
it at all, while keying on the generic "not observed on the selected
gateway" skip line would misfire on sandboxes healthy on another live
gateway or ones reconnecting mid-run.

The CLI now derives a dedicated orphan signal from observation rather than
version state: a recorded sandbox absent from the selected gateway in any
phase, whose persisted binding resolves to that same gateway, has nowhere
else to be running. Sandboxes bound to other gateways, exonerated by the
confirming second listing, or restored by prepared-backup recovery are
excluded. The marker prints with concrete remediation (`<name> destroy`,
then `onboard`) and is exported as a shared constant so the installer
harness drives the real grep against it — drift on either side fails the
suite.

install.sh mirrors recovery output through a trap-registered temp log
(taking the CLI's own exit status via PIPESTATUS so a tee failure cannot
fake the #5735 failure path), downgrades the banner to "completed with
warnings", and replaces the false recovery claim with hedged wording and
the same remediation. Uninstall now warns at preserve time that the kept
registry is not self-sufficient, uses "already removed or unreachable"
wording for provider/sandbox delete no-ops instead of the contradictory
"Deleted X skipped", and stale listings label version regressions with a
"(downgrade)" suffix instead of framing them as routine upgrades.

Fixes #6520

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
@Dongni-Yang Dongni-Yang self-assigned this Jul 9, 2026
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces orphaned sandbox detection for recorded sandboxes missing from their recorded gateway, then threads that state through upgrade-sandboxes, install recovery, uninstall warnings, stale-upgrade labeling, and related documentation/tests.

Changes

Orphaned sandbox recovery and remediation

Layer / File(s) Summary
Domain helpers and stale labeling
src/lib/domain/maintenance/orphan-detection.ts, src/lib/domain/maintenance/orphan-detection.test.ts, src/lib/domain/maintenance/upgrade.ts, src/lib/domain/maintenance/upgrade.test.ts, src/lib/onboard/docker-driver-gateway-compat.ts
Adds orphan classification and messaging helpers, compares dotted versions for stale-upgrade labeling, and re-exports the version comparator from the onboarding compatibility module.
Upgrade sandbox orphan reporting
src/lib/actions/upgrade-sandboxes.ts, src/lib/actions/upgrade-sandboxes-recovery.test.ts
Computes orphaned registry sandboxes, filters unknown-version output, and prints orphan summaries/remediation alongside auto and --check flows.
Installer recovery status and completion messaging
scripts/install.sh, test/install-orphaned-sandbox-recovery.test.ts
Tracks orphaned pre-existing sandboxes during recovery, preserves the CLI exit code, and changes onboarding/completion messaging to report warnings when recovery was incomplete.
Uninstall preservation warnings
src/lib/domain/uninstall/messaging.ts, src/lib/domain/uninstall/messaging.test.ts, src/lib/actions/uninstall/run-plan.ts, src/lib/actions/uninstall/run-plan-preserved-registry.test.ts
Adds preserve-data warnings for unrecoverable sandboxes.json entries and updates uninstall skip messages for sandbox/provider/gateway removals.
Docs and platform references
docs/get-started/quickstart.mdx, docs/manage-sandboxes/lifecycle.mdx, docs/reference/commands.mdx, docs/reference/host-files-and-state.mdx, ci/platform-matrix.json, docs/get-started/prerequisites.mdx, docs/reference/platform-support.mdx
Updates recovery, uninstall, and platform-reference docs to describe the recorded-gateway orphan case and related line citations.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

  • NVIDIA/NemoClaw#5102: Shares the upgrade.ts stale/reason-labeling path and related version-classification logic.

Suggested labels: bug-fix, area: sandbox, area: cli

Suggested reviewers: cv, jyaunches

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: installer/uninstall handling now reports orphaned sandboxes honestly during reinstall recovery.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/6520-uninstall-reinstall-orphan-sandbox

Comment @coderabbitai help to get the list of available commands.

@github-code-quality

github-code-quality Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage remains at 96%, unchanged from the main branch.

TypeScript / code-coverage/cli

The overall coverage in the fix/6520-uninstall-r... branch remains at 76%, unchanged from the main branch.

Show a code coverage summary of the most impacted files.
File main 2b2c9f6 fix/6520-uninstall-r... 75b0708 +/-
src/lib/securit...ntial-filter.ts 99% 87% -12%
src/lib/actions...-add-restart.ts 19% 14% -5%
src/lib/runner.ts 80% 75% -5%
src/lib/adapters/http/probe.ts 89% 91% +2%
src/lib/actions...lution-probe.ts 88% 94% +6%
src/lib/actions...de-sandboxes.ts 82% 89% +7%
src/lib/actions...e-validation.ts 81% 90% +9%
src/lib/actions...x/mcp-bridge.ts 35% 44% +9%
src/lib/domain/...an-detection.ts 0% 100% +100%
src/lib/domain/...ll/messaging.ts 0% 100% +100%

Updated July 09, 2026 06:01 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

The codebase-growth guardrail requires changed test files to stay free of
if statements; express the run stub's command dispatch as a conditional
expression instead.

Refs #6520

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: cloud-onboard, onboard-repair, onboard-resume, state-backup-restore, upgrade-stale-sandbox
Optional E2E: sandbox-operations

Dispatch hint: cloud-onboard,onboard-repair,onboard-resume,state-backup-restore,upgrade-stale-sandbox

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • cloud-onboard: Required floor from the deterministic risk plan for installer/platform changes; validates the hosted installer reaches a usable onboarded assistant on a clean supported host.
  • onboard-repair: Required floor from the deterministic risk plan and onboarding resume rule for gateway/onboarding lifecycle compatibility changes; validates repair of partially broken onboarding/sandbox state.
  • onboard-resume: Required floor from the deterministic risk plan and onboarding resume rule for gateway/onboarding lifecycle compatibility changes; validates interrupted onboarding resume state handling in a live sandbox path.
  • state-backup-restore: Required floor from the deterministic risk plan for upgrade/rebuild state preservation changes; validates that sandbox state survives backup and restore flows.
  • upgrade-stale-sandbox: Required floor from the deterministic risk plan for upgrade-sandboxes and maintenance upgrade changes; validates stale sandbox detection/rebuild behavior on a live sandbox.

Optional E2E

  • sandbox-operations: Useful adjacent lifecycle confidence for registry-backed sandbox operations after uninstall/orphan-detection changes, but it does not directly cover the preserved-registry reinstall scenario.

New E2E recommendations

  • uninstall-preserved-registry-reinstall-recovery (high): This PR adds logic for the case where uninstall preserves sandboxes.json but removes the gateway registration and Docker image, and the installer later sees upgrade-sandboxes exit 0 with recorded sandboxes not found. Existing live E2E jobs cover cloud onboarding and stale upgrade paths, but no existing workflow job appears to exercise uninstall-preserve followed by reinstall/recovery-warning semantics end to end.
    • Suggested test: Add a live E2E job such as uninstall-preserved-registry-reinstall that creates a sandbox, runs nemoclaw uninstall --yes without --destroy-user-data, reruns the installer, asserts the final summary is 'Installation completed with warnings' rather than recovered, and verifies the remediation guidance plus stranded-registry cleanup path.

Dispatch hint

  • Workflow: E2E
  • jobs input: cloud-onboard,onboard-repair,onboard-resume,state-backup-restore,upgrade-stale-sandbox

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

E2E Target Recommendation

Required E2E targets: cloud-onboard, onboard-repair, onboard-resume, state-backup-restore, upgrade-stale-sandbox
Optional E2E targets: None

Dispatch required E2E targets:

  • gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=cloud-onboard
  • gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=onboard-repair
  • gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=onboard-resume
  • gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=state-backup-restore
  • gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=upgrade-stale-sandbox

Workflow run

Full E2E target advisor summary

E2E Target Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E targets

  • cloud-onboard: Installer and platform changes must work on a clean supported host with the pinned runtime dependencies.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=cloud-onboard
  • onboard-repair: Onboarding and sandbox state must converge across persisted metadata, reported status, and the live runtime.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=onboard-repair
  • onboard-resume: Onboarding and sandbox state must converge across persisted metadata, reported status, and the live runtime.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=onboard-resume
  • state-backup-restore: Upgrade, rebuild, snapshot, and restore operations must preserve user state while replacing stale runtime state.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=state-backup-restore
  • upgrade-stale-sandbox: Upgrade, rebuild, snapshot, and restore operations must preserve user state while replacing stale runtime state.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=upgrade-stale-sandbox

Optional E2E targets

  • None.

Relevant changed files

  • ci/platform-matrix.json
  • scripts/install.sh
  • src/lib/actions/uninstall/run-plan.ts
  • src/lib/actions/upgrade-sandboxes.ts
  • src/lib/domain/maintenance/orphan-detection.ts
  • src/lib/domain/maintenance/upgrade.ts
  • src/lib/domain/uninstall/messaging.ts
  • src/lib/onboard/docker-driver-gateway-compat.ts

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — Changes requested

Merge posture: Do not merge yet
Primary next action: Fix PRA-3: upgrade-sandboxes.ts exceeds 20-line monolith growth threshold (+26 net); then add or justify PRA-T1.
Open items: 3 required · 4 warnings · 1 suggestion · 8 test follow-ups
Since last review: 5 prior items resolved · 3 still apply · 4 new items found

Action checklist

  • PRA-3 Fix: upgrade-sandboxes.ts exceeds 20-line monolith growth threshold (+26 net) in src/lib/actions/upgrade-sandboxes.ts:1
  • PRA-4 Fix: run-plan.ts exceeds 20-line monolith growth threshold (+29 net) in src/lib/actions/uninstall/run-plan.ts:1
  • PRA-5 Fix: Test file grew 131 lines — split by concern per PRA-4 in src/lib/actions/upgrade-sandboxes-recovery.test.ts:1
  • PRA-1 Resolve or justify: Source-of-truth review needed: src/lib/domain/maintenance/orphan-detection.ts
  • PRA-2 Resolve or justify: Source-of-truth review needed: src/lib/domain/uninstall/messaging.ts + src/lib/domain/maintenance/orphan-detection.ts
  • PRA-6 Resolve or justify: Orphan detection workaround lacks linked tracking issue for removal condition in src/lib/domain/maintenance/orphan-detection.ts:1
  • PRA-7 Resolve or justify: Uninstall preserved-registry warning and upgrade-sandboxes orphan remediation text not locked in sync in src/lib/domain/uninstall/messaging.ts:38
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Runtime validation
  • PRA-T6 Add or justify test follow-up: Test file grew 131 lines — split by concern per PRA-4
  • PRA-T7 Add or justify test follow-up: src/lib/domain/maintenance/orphan-detection.ts
  • PRA-T8 Add or justify test follow-up: src/lib/domain/uninstall/messaging.ts + src/lib/domain/maintenance/orphan-detection.ts
  • PRA-8 In-scope improvement: compareDottedVersions should be documented as canonical version comparator in src/lib/domain/maintenance/upgrade.ts:90

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-3 Required architecture src/lib/actions/upgrade-sandboxes.ts:1 Extract the six remaining local helpers into domain/maintenance/upgrade.ts or a new upgrade-helpers.ts. Keep upgrade-sandboxes.ts as a thin orchestrator under 459 lines.
PRA-4 Required architecture src/lib/actions/uninstall/run-plan.ts:1 Extract service-stopping helpers (stopHelperServices, stopMatchingPids, stopOllamaAuthProxy, stopModelRouter, stopOrphanedOpenShell) and Docker cleanup helpers (removeDockerContainers, removeDockerImages, removeDockerVolume) into domain/uninstall/cleanup.ts. Keep run-plan.ts as plan executor only.
PRA-5 Required tests src/lib/actions/upgrade-sandboxes-recovery.test.ts:1 Split into: (1) upgrade-sandboxes-prepared-backup-recovery.test.ts (lines 171-330), (2) upgrade-sandboxes-orphan-detection.test.ts (lines 351-464), (3) upgrade-sandboxes-gateway-binding.test.ts (lines 477-603), (4) upgrade-sandboxes-downgrade-labeling.test.ts (new). Extract createRecoveryHarness to src/lib/actions/upgrade-sandboxes-test-utils.ts.
PRA-6 Resolve/justify architecture src/lib/domain/maintenance/orphan-detection.ts:1 File a follow-up issue exploring: (a) uninstall preserves gateway/providers/images, or (b) uninstall purges registry by default, or (c) uninstall records gateway-removal evidence in registry so reinstall can distinguish states. Link issue from orphan-detection.ts comment.
PRA-7 Resolve/justify correctness src/lib/domain/uninstall/messaging.ts:38 Create a shared constants file (e.g., src/lib/domain/uninstall/remediation-commands.ts) exporting DESTROY_CMD and ONBOARD_CMD template strings. Import in both messaging modules. Add a snapshot test verifying both messages contain the same command templates.
PRA-8 Improvement docs src/lib/domain/maintenance/upgrade.ts:90 Add a JSDoc comment on compareDottedVersions noting it's the canonical dotted-version comparator for NemoClaw and downgradeSuffix depends on its semantics.

🚨 Required before merge

Address these before merging unless a maintainer explicitly overrides the advisor with rationale.

PRA-3 Required — upgrade-sandboxes.ts exceeds 20-line monolith growth threshold (+26 net)

  • Location: src/lib/actions/upgrade-sandboxes.ts:1
  • Category: architecture
  • Problem: File grew from 439 to 465 lines despite extracting orphan detection and downgrade logic to domain modules. Threshold is base+20=459 lines; current exceeds by 6 lines. Six local helpers remain: checkAgentVersionForUpgrade, resolveCurrentNemoclawVersion, prepareBackupRecovery, isPreparedRecoveryCandidate, confirmAbsentRecoveryCandidates, printOrphanedRegistrySandboxes.
  • Impact: Reduces maintainability; future changes to upgrade logic require touching the orchestrator file. The monolith growth threshold exists to force decomposition before complexity accumulates.
  • Required action: Extract the six remaining local helpers into domain/maintenance/upgrade.ts or a new upgrade-helpers.ts. Keep upgrade-sandboxes.ts as a thin orchestrator under 459 lines.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/actions/upgrade-sandboxes.ts should show ≤459 lines. Run vitest src/lib/actions/upgrade-sandboxes-recovery.test.ts to confirm behavior unchanged.
  • Missing regression test: No new test needed — structural. Existing upgrade-sandboxes-recovery.test.ts (all 38 tests) must pass after extraction.
  • Done when: The required change is committed and verification passes: wc -l src/lib/actions/upgrade-sandboxes.ts should show ≤459 lines. Run vitest src/lib/actions/upgrade-sandboxes-recovery.test.ts to confirm behavior unchanged.
  • Evidence: wc -l shows 465 lines. Helpers at lines 80, 118, 142, 178, 207, 235. Threshold 459 = 439 base + 20.

PRA-4 Required — run-plan.ts exceeds 20-line monolith growth threshold (+29 net)

  • Location: src/lib/actions/uninstall/run-plan.ts:1
  • Category: architecture
  • Problem: File grew from 963 to 992 lines despite extracting messaging to domain/uninstall/messaging.ts. Threshold is base+20=983 lines; current exceeds by 9 lines. Service-stopping and Docker cleanup helpers remain in the plan executor.
  • Impact: Uninstall plan execution, service stopping, Docker cleanup, and messaging all in one file. Messaging changes require touching the full plan executor.
  • Required action: Extract service-stopping helpers (stopHelperServices, stopMatchingPids, stopOllamaAuthProxy, stopModelRouter, stopOrphanedOpenShell) and Docker cleanup helpers (removeDockerContainers, removeDockerImages, removeDockerVolume) into domain/uninstall/cleanup.ts. Keep run-plan.ts as plan executor only.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/actions/uninstall/run-plan.ts should show ≤983 lines. Run vitest src/lib/actions/uninstall/run-plan-preserved-registry.test.ts and existing uninstall tests.
  • Missing regression test: Add unit tests for extracted cleanup functions in new test file to ensure warning text stability across uninstall scenarios.
  • Done when: The required change is committed and verification passes: wc -l src/lib/actions/uninstall/run-plan.ts should show ≤983 lines. Run vitest src/lib/actions/uninstall/run-plan-preserved-registry.test.ts and existing uninstall tests.
  • Evidence: wc -l shows 992 lines. Threshold 983 = 963 base + 20. Service/Docker helpers span lines 300-550 approximately.

PRA-5 Required — Test file grew 131 lines — split by concern per PRA-4

  • Location: src/lib/actions/upgrade-sandboxes-recovery.test.ts:1
  • Category: tests
  • Problem: upgrade-sandboxes-recovery.test.ts grew from 566 to 697 lines (+131). Contains 38 tests covering prepared-backup recovery, orphan detection, gateway binding, legacy confirmation, and downgrade labeling — multiple distinct concerns in one file. createRecoveryHarness helper (line 36) not extracted to shared test utils.
  • Impact: Harder to navigate, slower to run focused tests, creates merge conflicts. Blocks clean architecture.
  • Required action: Split into: (1) upgrade-sandboxes-prepared-backup-recovery.test.ts (lines 171-330), (2) upgrade-sandboxes-orphan-detection.test.ts (lines 351-464), (3) upgrade-sandboxes-gateway-binding.test.ts (lines 477-603), (4) upgrade-sandboxes-downgrade-labeling.test.ts (new). Extract createRecoveryHarness to src/lib/actions/upgrade-sandboxes-test-utils.ts.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: After split, each new test file <300 lines. Shared helper imported from test-utils. All 38 tests pass.
  • Missing regression test: None — structural. All existing tests must pass after split.
  • Done when: The required change is committed and verification passes: After split, each new test file <300 lines. Shared helper imported from test-utils. All 38 tests pass.
  • Evidence: File grew from 566 to 697 lines. 38 named test blocks. createRecoveryHarness at line 36 used by 28+ tests.
Review findings by urgency: 3 required fixes, 4 items to resolve/justify, 1 in-scope improvement

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: src/lib/domain/maintenance/orphan-detection.ts

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: test/install-orphaned-sandbox-recovery.test.ts (real install.sh classification + print_done, stub built from the domain marker constant), src/lib/actions/upgrade-sandboxes-recovery.test.ts (orphan classifier incl. multi-gateway/reconnect/recovered negatives and check-mode parity), src/lib/domain/maintenance/orphan-detection.test.ts, src/lib/domain/uninstall/messaging.test.ts, src/lib/actions/uninstall/run-plan-preserved-registry.test.ts
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: orphan-detection.ts header comment lines 1-22 documents all 5 questions. Three test files cover the workaround. No linked tracking issue for removal condition.

PRA-2 Resolve/justify — Source-of-truth review needed: src/lib/domain/uninstall/messaging.ts + src/lib/domain/maintenance/orphan-detection.ts

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Add snapshot test verifying both message families contain identical command templates. domain/uninstall/messaging.test.ts and domain/maintenance/orphan-detection.test.ts already test each family independently.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: messaging.ts:38 exports preservedRegistryUnrecoverableWarnings. orphan-detection.ts exports orphanedRegistryRemediation. Both render destroy/onboard commands but with separate string constants. Prior PRA-5.

PRA-6 Resolve/justify — Orphan detection workaround lacks linked tracking issue for removal condition

  • Location: src/lib/domain/maintenance/orphan-detection.ts:1
  • Category: architecture
  • Problem: The orphan-detection.ts header comment documents all 5 source-of-truth questions but the removal condition ('when uninstall either preserves everything its recorded sandboxes depend on, purges the registry by default, or records gateway-removal evidence in the registry') is not linked to a tracking issue. Prior PRA-6 required linking a follow-up issue from the orphan-detection.ts comment.
  • Impact: Workaround may persist indefinitely without a concrete trigger to re-evaluate. The removal condition is documented but not actionable.
  • Recommended action: File a follow-up issue exploring: (a) uninstall preserves gateway/providers/images, or (b) uninstall purges registry by default, or (c) uninstall records gateway-removal evidence in registry so reinstall can distinguish states. Link issue from orphan-detection.ts comment.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check orphan-detection.ts header comment for a GitHub issue link (e.g., 'See #XXXX').
  • Missing regression test: orphan-detection.test.ts (classifier logic), upgrade-sandboxes-recovery.test.ts (7 orphan integration tests), install-orphaned-sandbox-recovery.test.ts (install.sh end-to-end classification) already cover the workaround behavior.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check orphan-detection.ts header comment for a GitHub issue link (e.g., 'See #XXXX').
  • Evidence: orphan-detection.ts header comment lines 1-22 documents all 5 questions. Three test files cover the workaround. No issue link present.

PRA-7 Resolve/justify — Uninstall preserved-registry warning and upgrade-sandboxes orphan remediation text not locked in sync

  • Location: src/lib/domain/uninstall/messaging.ts:38
  • Category: correctness
  • Problem: domain/uninstall/messaging.ts exports providerDeleteSkipMessage, gatewayDestroySkipMessage, preservedRegistryUnrecoverableWarnings while domain/maintenance/orphan-detection.ts exports orphanedRegistryRemediation. Both render 'destroy' and 'onboard' command templates but use different constants — no snapshot test verifies they stay in sync.
  • Impact: Wording drift between uninstall preserve warning and upgrade-sandboxes orphan remediation could confuse users with inconsistent command examples.
  • Recommended action: Create a shared constants file (e.g., src/lib/domain/uninstall/remediation-commands.ts) exporting DESTROY_CMD and ONBOARD_CMD template strings. Import in both messaging modules. Add a snapshot test verifying both messages contain the same command templates.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'destroy.*onboard' src/lib/domain/uninstall/messaging.ts src/lib/domain/maintenance/orphan-detection.ts — verify identical command templates. Run vitest to confirm snapshot test passes.
  • Missing regression test: Add snapshot test in domain/uninstall/messaging.test.ts or domain/maintenance/orphan-detection.test.ts verifying command template parity.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'destroy.*onboard' src/lib/domain/uninstall/messaging.ts src/lib/domain/maintenance/orphan-detection.ts — verify identical command templates. Run vitest to confirm snapshot test passes.
  • Evidence: messaging.ts:38 exports preservedRegistryUnrecoverableWarnings with 'destroy'/'onboard' remediation. orphan-detection.ts exports orphanedRegistryRemediation with similar but separate templates. Prior PRA-5.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

PRA-8 Improvement — compareDottedVersions should be documented as canonical version comparator

  • Location: src/lib/domain/maintenance/upgrade.ts:90
  • Category: docs
  • Problem: compareDottedVersions is used by downgradeSuffix to label downgrades explicitly. It is the canonical dotted-version comparator for NemoClaw but lacks a JSDoc noting this and that downgradeSuffix depends on its semantics.
  • Impact: Future maintainers may not realize this is the single source of truth for version comparison, risking inconsistent comparisons elsewhere.
  • Suggested action: Add a JSDoc comment on compareDottedVersions noting it's the canonical dotted-version comparator for NemoClaw and downgradeSuffix depends on its semantics.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check upgrade.ts line 90 area for JSDoc on compareDottedVersions.
  • Missing regression test: None — documentation only. Existing upgrade.test.ts (describeStaleUpgrade tests) covers downgrade labeling behavior.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: upgrade.ts exports compareDottedVersions and downgradeSuffix. Prior PRA-7.
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — Run the cloud-onboard E2E job for Installer and platform changes must work on a clean supported host with the pinned runtime dependencies. Matched files: ci/platform-matrix.json, scripts/install.sh.. Deterministic regression risks require live validation: lifecycle-state, upgrade-rebuild, platform-install. Unit/mocked layer is complete with 58 new test blocks covering all branches. Five required E2E jobs (cloud-onboard, onboard-repair, onboard-resume, state-backup-restore, upgrade-stale-sandbox) map to riskPlan invariants and must run before deploy.
  • PRA-T2 Runtime validation — Run the onboard-repair E2E job for Onboarding and sandbox state must converge across persisted metadata, reported status, and the live runtime. Matched files: src/lib/onboard/docker-driver-gateway-compat.ts.. Deterministic regression risks require live validation: lifecycle-state, upgrade-rebuild, platform-install. Unit/mocked layer is complete with 58 new test blocks covering all branches. Five required E2E jobs (cloud-onboard, onboard-repair, onboard-resume, state-backup-restore, upgrade-stale-sandbox) map to riskPlan invariants and must run before deploy.
  • PRA-T3 Runtime validation — Run the onboard-resume E2E job for Onboarding and sandbox state must converge across persisted metadata, reported status, and the live runtime. Matched files: src/lib/onboard/docker-driver-gateway-compat.ts.. Deterministic regression risks require live validation: lifecycle-state, upgrade-rebuild, platform-install. Unit/mocked layer is complete with 58 new test blocks covering all branches. Five required E2E jobs (cloud-onboard, onboard-repair, onboard-resume, state-backup-restore, upgrade-stale-sandbox) map to riskPlan invariants and must run before deploy.
  • PRA-T4 Runtime validation — Run the state-backup-restore E2E job for Upgrade, rebuild, snapshot, and restore operations must preserve user state while replacing stale runtime state. Matched files: src/lib/actions/upgrade-sandboxes.ts, src/lib/domain/maintenance/upgrade.ts.. Deterministic regression risks require live validation: lifecycle-state, upgrade-rebuild, platform-install. Unit/mocked layer is complete with 58 new test blocks covering all branches. Five required E2E jobs (cloud-onboard, onboard-repair, onboard-resume, state-backup-restore, upgrade-stale-sandbox) map to riskPlan invariants and must run before deploy.
  • PRA-T5 Runtime validation — Run the upgrade-stale-sandbox E2E job for Upgrade, rebuild, snapshot, and restore operations must preserve user state while replacing stale runtime state. Matched files: src/lib/actions/upgrade-sandboxes.ts, src/lib/domain/maintenance/upgrade.ts.. Deterministic regression risks require live validation: lifecycle-state, upgrade-rebuild, platform-install. Unit/mocked layer is complete with 58 new test blocks covering all branches. Five required E2E jobs (cloud-onboard, onboard-repair, onboard-resume, state-backup-restore, upgrade-stale-sandbox) map to riskPlan invariants and must run before deploy.
  • PRA-T6 Test file grew 131 lines — split by concern per PRA-4 — Split into: (1) upgrade-sandboxes-prepared-backup-recovery.test.ts (lines 171-330), (2) upgrade-sandboxes-orphan-detection.test.ts (lines 351-464), (3) upgrade-sandboxes-gateway-binding.test.ts (lines 477-603), (4) upgrade-sandboxes-downgrade-labeling.test.ts (new). Extract createRecoveryHarness to src/lib/actions/upgrade-sandboxes-test-utils.ts.
  • PRA-T7 src/lib/domain/maintenance/orphan-detection.ts — test/install-orphaned-sandbox-recovery.test.ts (real install.sh classification + print_done, stub built from the domain marker constant), src/lib/actions/upgrade-sandboxes-recovery.test.ts (orphan classifier incl. multi-gateway/reconnect/recovered negatives and check-mode parity), src/lib/domain/maintenance/orphan-detection.test.ts, src/lib/domain/uninstall/messaging.test.ts, src/lib/actions/uninstall/run-plan-preserved-registry.test.ts. orphan-detection.ts header comment lines 1-22 documents all 5 questions. Three test files cover the workaround. No linked tracking issue for removal condition.
  • PRA-T8 src/lib/domain/uninstall/messaging.ts + src/lib/domain/maintenance/orphan-detection.ts — Add snapshot test verifying both message families contain identical command templates. domain/uninstall/messaging.test.ts and domain/maintenance/orphan-detection.test.ts already test each family independently.. messaging.ts:38 exports preservedRegistryUnrecoverableWarnings. orphan-detection.ts exports orphanedRegistryRemediation. Both render destroy/onboard commands but with separate string constants. Prior PRA-5.
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: src/lib/domain/maintenance/orphan-detection.ts

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: test/install-orphaned-sandbox-recovery.test.ts (real install.sh classification + print_done, stub built from the domain marker constant), src/lib/actions/upgrade-sandboxes-recovery.test.ts (orphan classifier incl. multi-gateway/reconnect/recovered negatives and check-mode parity), src/lib/domain/maintenance/orphan-detection.test.ts, src/lib/domain/uninstall/messaging.test.ts, src/lib/actions/uninstall/run-plan-preserved-registry.test.ts
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: orphan-detection.ts header comment lines 1-22 documents all 5 questions. Three test files cover the workaround. No linked tracking issue for removal condition.

PRA-2 Resolve/justify — Source-of-truth review needed: src/lib/domain/uninstall/messaging.ts + src/lib/domain/maintenance/orphan-detection.ts

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Add snapshot test verifying both message families contain identical command templates. domain/uninstall/messaging.test.ts and domain/maintenance/orphan-detection.test.ts already test each family independently.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: messaging.ts:38 exports preservedRegistryUnrecoverableWarnings. orphan-detection.ts exports orphanedRegistryRemediation. Both render destroy/onboard commands but with separate string constants. Prior PRA-5.

PRA-3 Required — upgrade-sandboxes.ts exceeds 20-line monolith growth threshold (+26 net)

  • Location: src/lib/actions/upgrade-sandboxes.ts:1
  • Category: architecture
  • Problem: File grew from 439 to 465 lines despite extracting orphan detection and downgrade logic to domain modules. Threshold is base+20=459 lines; current exceeds by 6 lines. Six local helpers remain: checkAgentVersionForUpgrade, resolveCurrentNemoclawVersion, prepareBackupRecovery, isPreparedRecoveryCandidate, confirmAbsentRecoveryCandidates, printOrphanedRegistrySandboxes.
  • Impact: Reduces maintainability; future changes to upgrade logic require touching the orchestrator file. The monolith growth threshold exists to force decomposition before complexity accumulates.
  • Required action: Extract the six remaining local helpers into domain/maintenance/upgrade.ts or a new upgrade-helpers.ts. Keep upgrade-sandboxes.ts as a thin orchestrator under 459 lines.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/actions/upgrade-sandboxes.ts should show ≤459 lines. Run vitest src/lib/actions/upgrade-sandboxes-recovery.test.ts to confirm behavior unchanged.
  • Missing regression test: No new test needed — structural. Existing upgrade-sandboxes-recovery.test.ts (all 38 tests) must pass after extraction.
  • Done when: The required change is committed and verification passes: wc -l src/lib/actions/upgrade-sandboxes.ts should show ≤459 lines. Run vitest src/lib/actions/upgrade-sandboxes-recovery.test.ts to confirm behavior unchanged.
  • Evidence: wc -l shows 465 lines. Helpers at lines 80, 118, 142, 178, 207, 235. Threshold 459 = 439 base + 20.

PRA-4 Required — run-plan.ts exceeds 20-line monolith growth threshold (+29 net)

  • Location: src/lib/actions/uninstall/run-plan.ts:1
  • Category: architecture
  • Problem: File grew from 963 to 992 lines despite extracting messaging to domain/uninstall/messaging.ts. Threshold is base+20=983 lines; current exceeds by 9 lines. Service-stopping and Docker cleanup helpers remain in the plan executor.
  • Impact: Uninstall plan execution, service stopping, Docker cleanup, and messaging all in one file. Messaging changes require touching the full plan executor.
  • Required action: Extract service-stopping helpers (stopHelperServices, stopMatchingPids, stopOllamaAuthProxy, stopModelRouter, stopOrphanedOpenShell) and Docker cleanup helpers (removeDockerContainers, removeDockerImages, removeDockerVolume) into domain/uninstall/cleanup.ts. Keep run-plan.ts as plan executor only.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/actions/uninstall/run-plan.ts should show ≤983 lines. Run vitest src/lib/actions/uninstall/run-plan-preserved-registry.test.ts and existing uninstall tests.
  • Missing regression test: Add unit tests for extracted cleanup functions in new test file to ensure warning text stability across uninstall scenarios.
  • Done when: The required change is committed and verification passes: wc -l src/lib/actions/uninstall/run-plan.ts should show ≤983 lines. Run vitest src/lib/actions/uninstall/run-plan-preserved-registry.test.ts and existing uninstall tests.
  • Evidence: wc -l shows 992 lines. Threshold 983 = 963 base + 20. Service/Docker helpers span lines 300-550 approximately.

PRA-5 Required — Test file grew 131 lines — split by concern per PRA-4

  • Location: src/lib/actions/upgrade-sandboxes-recovery.test.ts:1
  • Category: tests
  • Problem: upgrade-sandboxes-recovery.test.ts grew from 566 to 697 lines (+131). Contains 38 tests covering prepared-backup recovery, orphan detection, gateway binding, legacy confirmation, and downgrade labeling — multiple distinct concerns in one file. createRecoveryHarness helper (line 36) not extracted to shared test utils.
  • Impact: Harder to navigate, slower to run focused tests, creates merge conflicts. Blocks clean architecture.
  • Required action: Split into: (1) upgrade-sandboxes-prepared-backup-recovery.test.ts (lines 171-330), (2) upgrade-sandboxes-orphan-detection.test.ts (lines 351-464), (3) upgrade-sandboxes-gateway-binding.test.ts (lines 477-603), (4) upgrade-sandboxes-downgrade-labeling.test.ts (new). Extract createRecoveryHarness to src/lib/actions/upgrade-sandboxes-test-utils.ts.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: After split, each new test file <300 lines. Shared helper imported from test-utils. All 38 tests pass.
  • Missing regression test: None — structural. All existing tests must pass after split.
  • Done when: The required change is committed and verification passes: After split, each new test file <300 lines. Shared helper imported from test-utils. All 38 tests pass.
  • Evidence: File grew from 566 to 697 lines. 38 named test blocks. createRecoveryHarness at line 36 used by 28+ tests.

PRA-6 Resolve/justify — Orphan detection workaround lacks linked tracking issue for removal condition

  • Location: src/lib/domain/maintenance/orphan-detection.ts:1
  • Category: architecture
  • Problem: The orphan-detection.ts header comment documents all 5 source-of-truth questions but the removal condition ('when uninstall either preserves everything its recorded sandboxes depend on, purges the registry by default, or records gateway-removal evidence in the registry') is not linked to a tracking issue. Prior PRA-6 required linking a follow-up issue from the orphan-detection.ts comment.
  • Impact: Workaround may persist indefinitely without a concrete trigger to re-evaluate. The removal condition is documented but not actionable.
  • Recommended action: File a follow-up issue exploring: (a) uninstall preserves gateway/providers/images, or (b) uninstall purges registry by default, or (c) uninstall records gateway-removal evidence in registry so reinstall can distinguish states. Link issue from orphan-detection.ts comment.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check orphan-detection.ts header comment for a GitHub issue link (e.g., 'See #XXXX').
  • Missing regression test: orphan-detection.test.ts (classifier logic), upgrade-sandboxes-recovery.test.ts (7 orphan integration tests), install-orphaned-sandbox-recovery.test.ts (install.sh end-to-end classification) already cover the workaround behavior.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check orphan-detection.ts header comment for a GitHub issue link (e.g., 'See #XXXX').
  • Evidence: orphan-detection.ts header comment lines 1-22 documents all 5 questions. Three test files cover the workaround. No issue link present.

PRA-7 Resolve/justify — Uninstall preserved-registry warning and upgrade-sandboxes orphan remediation text not locked in sync

  • Location: src/lib/domain/uninstall/messaging.ts:38
  • Category: correctness
  • Problem: domain/uninstall/messaging.ts exports providerDeleteSkipMessage, gatewayDestroySkipMessage, preservedRegistryUnrecoverableWarnings while domain/maintenance/orphan-detection.ts exports orphanedRegistryRemediation. Both render 'destroy' and 'onboard' command templates but use different constants — no snapshot test verifies they stay in sync.
  • Impact: Wording drift between uninstall preserve warning and upgrade-sandboxes orphan remediation could confuse users with inconsistent command examples.
  • Recommended action: Create a shared constants file (e.g., src/lib/domain/uninstall/remediation-commands.ts) exporting DESTROY_CMD and ONBOARD_CMD template strings. Import in both messaging modules. Add a snapshot test verifying both messages contain the same command templates.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'destroy.*onboard' src/lib/domain/uninstall/messaging.ts src/lib/domain/maintenance/orphan-detection.ts — verify identical command templates. Run vitest to confirm snapshot test passes.
  • Missing regression test: Add snapshot test in domain/uninstall/messaging.test.ts or domain/maintenance/orphan-detection.test.ts verifying command template parity.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'destroy.*onboard' src/lib/domain/uninstall/messaging.ts src/lib/domain/maintenance/orphan-detection.ts — verify identical command templates. Run vitest to confirm snapshot test passes.
  • Evidence: messaging.ts:38 exports preservedRegistryUnrecoverableWarnings with 'destroy'/'onboard' remediation. orphan-detection.ts exports orphanedRegistryRemediation with similar but separate templates. Prior PRA-5.

PRA-8 Improvement — compareDottedVersions should be documented as canonical version comparator

  • Location: src/lib/domain/maintenance/upgrade.ts:90
  • Category: docs
  • Problem: compareDottedVersions is used by downgradeSuffix to label downgrades explicitly. It is the canonical dotted-version comparator for NemoClaw but lacks a JSDoc noting this and that downgradeSuffix depends on its semantics.
  • Impact: Future maintainers may not realize this is the single source of truth for version comparison, risking inconsistent comparisons elsewhere.
  • Suggested action: Add a JSDoc comment on compareDottedVersions noting it's the canonical dotted-version comparator for NemoClaw and downgradeSuffix depends on its semantics.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check upgrade.ts line 90 area for JSDoc on compareDottedVersions.
  • Missing regression test: None — documentation only. Existing upgrade.test.ts (describeStaleUpgrade tests) covers downgrade labeling behavior.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: upgrade.ts exports compareDottedVersions and downgradeSuffix. Prior PRA-7.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — Changes requested

Merge posture: Do not merge yet
Primary next action: Resolve or justify PRA-1: Source-of-truth review needed: Installer consumption of `upgrade-sandboxes` orphan outcome.
Open items: 0 required · 3 warnings · 0 suggestions · 8 test follow-ups
Since last review: 0 prior items resolved · 1 still applies · 1 new item found

Action checklist

  • PRA-1 Resolve or justify: Source-of-truth review needed: Installer consumption of `upgrade-sandboxes` orphan outcome
  • PRA-2 Resolve or justify: `upgrade-sandboxes --check` still gives generic rebuild guidance for orphan-only records in src/lib/actions/upgrade-sandboxes.ts:394
  • PRA-3 Resolve or justify: Installer orphan detection can fail open when temp-log capture is unavailable in scripts/install.sh:2299
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Runtime validation
  • PRA-T6 Add or justify test follow-up: Acceptance clause
  • PRA-T7 Add or justify test follow-up: Acceptance clause
  • PRA-T8 Add or justify test follow-up: Acceptance clause

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Resolve/justify correctness src/lib/actions/upgrade-sandboxes.ts:394 In the `checkOnly` branch, print the generic rebuild instruction only when there is non-orphan stale work or prepared recovery work that `upgrade-sandboxes` can actually perform. For an orphan-only result, end the output with the destroy/onboard remediation.
PRA-3 Resolve/justify security scripts/install.sh:2299 Make marker capture fail closed: either use a structured CLI outcome side channel, capture stdout through a safe fallback while streaming it, or treat successful recovery with unavailable capture as a warning that avoids claiming existing sandboxes were recovered.
Review findings by urgency: 0 required fixes, 3 items to resolve/justify, 0 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: Installer consumption of `upgrade-sandboxes` orphan outcome

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Covered by the security finding's requested shell-harness test: force temp-log creation failure while the stub CLI emits `ORPHANED_SANDBOX_MARKER` and exits 0.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: `scripts/install.sh:2299` falls back to an empty `recovery_log`; `scripts/install.sh:2318-2319` only sets `_PREEXISTING_SANDBOX_ORPHANED` when that log exists and contains the marker.

PRA-2 Resolve/justify — `upgrade-sandboxes --check` still gives generic rebuild guidance for orphan-only records

  • Location: src/lib/actions/upgrade-sandboxes.ts:394
  • Category: correctness
  • Problem: The prior Advisor finding still applies at the current head. In check mode, the code prints `printOrphanedRegistrySandboxes(unobservedOwnGatewaySandboxes)` and then unconditionally prints `Run `nemoclaw upgrade-sandboxes` to rebuild them.`. For an own-gateway orphan, the new remediation says automatic recovery is not possible and the user should run `<name> destroy` followed by `onboard`, so the final rebuild line contradicts the actionable diagnosis.
  • Impact: Users can leave `--check` with mixed instructions and retry `nemoclaw upgrade-sandboxes`, even though the orphan path is expected to skip the stranded record rather than restore its removed gateway registration or Docker image.
  • Recommended action: In the `checkOnly` branch, print the generic rebuild instruction only when there is non-orphan stale work or prepared recovery work that `upgrade-sandboxes` can actually perform. For an orphan-only result, end the output with the destroy/onboard remediation.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `src/lib/actions/upgrade-sandboxes.ts` around the `if (checkOnly)` block and confirm whether `console.log(` Run \`${CLI_NAME} upgrade-sandboxes\` to rebuild them.`);` is gated away when all stale/unknown entries are own-gateway orphans.
  • Missing regression test: Extend `prints the orphan diagnosis in --check mode so check and auto agree ([NemoClaw][onboard][All Platforms] nemoclaw uninstall + reinstall flow leaves an orphaned sandbox that the reinstalled CLI can't recover or clean up #6520)` in `src/lib/actions/upgrade-sandboxes-recovery.test.ts` to assert an orphan-only `--check` output includes the destroy/onboard remediation and does not include the unqualified `Run `nemoclaw upgrade-sandboxes` to rebuild them.` line.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `src/lib/actions/upgrade-sandboxes.ts` around the `if (checkOnly)` block and confirm whether `console.log(` Run \`${CLI_NAME} upgrade-sandboxes\` to rebuild them.`);` is gated away when all stale/unknown entries are own-gateway orphans.
  • Evidence: `unknownWithoutOrphans` excludes unknown orphans and `printOrphanedRegistrySandboxes(...)` is called in check mode, but the next line always prints the generic rebuild command regardless of whether `stale` contains only own-gateway orphans.

PRA-3 Resolve/justify — Installer orphan detection can fail open when temp-log capture is unavailable

  • Location: scripts/install.sh:2299
  • Category: security
  • Problem: `recover_preexisting_sandboxes_before_onboard` detects the zero-exit orphan outcome by teeing CLI output into a temp file and grepping for the marker. If `mktemp` fails, the fallback still runs `upgrade-sandboxes --auto` but cannot inspect stdout, so `_PREEXISTING_SANDBOX_ORPHANED` remains false even if the CLI emitted the orphan marker.
  • Impact: A host with an unavailable or failing temp directory can still receive the false clean summary that this PR is designed to prevent. Because the installer is making lifecycle trust decisions from text output, losing the capture path should not silently downgrade an orphan warning into a recovered-and-upgraded claim.
  • Recommended action: Make marker capture fail closed: either use a structured CLI outcome side channel, capture stdout through a safe fallback while streaming it, or treat successful recovery with unavailable capture as a warning that avoids claiming existing sandboxes were recovered.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `scripts/install.sh` lines around `recovery_log="$(mktemp ...)" || recovery_log=""` and the later `if [ -n "$recovery_log" ] && grep -Fq ...`; confirm the fallback path has no way to set `_PREEXISTING_SANDBOX_ORPHANED` from CLI output.
  • Missing regression test: Add a shell-harness test in `test/install-orphaned-sandbox-recovery.test.ts` that forces `mktemp` or temp-log creation to fail while the stub CLI emits `ORPHANED_SANDBOX_MARKER` and exits 0; assert the installer does not print the clean recovered-and-upgraded summary.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `scripts/install.sh` lines around `recovery_log="$(mktemp ...)" || recovery_log=""` and the later `if [ -n "$recovery_log" ] && grep -Fq ...`; confirm the fallback path has no way to set `_PREEXISTING_SANDBOX_ORPHANED` from CLI output.
  • Evidence: `scripts/install.sh:2299` assigns an empty `recovery_log` on `mktemp` failure, and `scripts/install.sh:2318-2319` only greps for `recorded sandbox(es) were not found on their recorded gateway` when that temp file exists.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

  • None.
Simplification opportunities: 1 possible cut

These are safe simplification checks only. Do not remove validation, security controls, data-loss prevention, or required tests.

  • PRA-3 shrink (scripts/install.sh:2299): The shell-level stdout marker grep as the sole production state handoff.
    • Replacement: A small structured status from `upgrade-sandboxes --auto` or a fail-closed capture helper that streams and records output in one place.
    • Safety boundary: Do not remove the own-gateway validation, multi-gateway exclusion, reconnect exclusion, credential redaction, or non-destructive uninstall preservation behavior.
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — Static regression: orphan-only `nemoclaw upgrade-sandboxes --check` output includes the destroy/onboard remediation and omits the unqualified `Run `nemoclaw upgrade-sandboxes` to rebuild them.` instruction.. The PR has strong unit and shell-harness coverage for classification and messaging, but changed deterministic risk families include installer/bootstrap, lifecycle-state, upgrade-rebuild, and platform-install paths that cross Docker/OpenShell runtime state.
  • PRA-T2 Runtime validation — Static shell regression: when temp-log creation fails and the stub CLI emits the orphan marker with exit 0, `install.sh` does not print a clean recovered-and-upgraded summary.. The PR has strong unit and shell-harness coverage for classification and messaging, but changed deterministic risk families include installer/bootstrap, lifecycle-state, upgrade-rebuild, and platform-install paths that cross Docker/OpenShell runtime state.
  • PRA-T3 Runtime validation — Runtime validation: uninstall with `nemoclaw uninstall --yes`, reinstall the same tag with a preserved stopped sandbox, and verify the final installer summary says completed with warnings, does not claim recovered/upgraded, and prints `<name> destroy` plus `onboard` remediation.. The PR has strong unit and shell-harness coverage for classification and messaging, but changed deterministic risk families include installer/bootstrap, lifecycle-state, upgrade-rebuild, and platform-install paths that cross Docker/OpenShell runtime state.
  • PRA-T4 Runtime validation — Runtime validation: reinstall with an older `NEMOCLAW_INSTALL_TAG` against a preserved row whose recorded image build is newer, and verify the stale line includes `(downgrade)` plus own-gateway orphan remediation.. The PR has strong unit and shell-harness coverage for classification and messaging, but changed deterministic risk families include installer/bootstrap, lifecycle-state, upgrade-rebuild, and platform-install paths that cross Docker/OpenShell runtime state.
  • PRA-T5 Runtime validation — Runtime validation: uninstall with `--destroy-user-data` or `NEMOCLAW_UNINSTALL_DESTROY_USER_DATA=1`, reinstall, and verify there is no preserved-registry orphan warning because `sandboxes.json` was purged.. The PR has strong unit and shell-harness coverage for classification and messaging, but changed deterministic risk families include installer/bootstrap, lifecycle-state, upgrade-rebuild, and platform-install paths that cross Docker/OpenShell runtime state.
  • PRA-T6 Acceptance clause — Running <code>nemoclaw uninstall --yes</code> (default flags, no <code>--destroy-user-data</code>) followed immediately by a fresh install of the same tag leaves the environment in an inconsistent state: — add test evidence or identify existing coverage. The PR adds uninstall preserve warnings, orphan classification, and installer warning summaries. The same-tag current-classification path is covered by `flags an own-gateway orphan with the dedicated marker...`, but installer temp-log capture failure can still report cleanly.
  • PRA-T7 Acceptance clause — On reinstall, the installer reads the preserved <code>sandboxes.json</code>, tries to "recover and upgrade pre-existing sandboxes," and immediately hits <code>Error: × No gateway metadata found for 'nemoclaw'</code> because uninstall deleted the gateway registration that the recorded sandbox referenced. — add test evidence or identify existing coverage. `upgrade-sandboxes.ts` now derives an observation-based own-gateway orphan signal after gateway recreation/listing, and installer consumes the marker when captured. The PR does not directly make the earlier gateway-metadata error structured, and capture failure can still lose the signal.
  • PRA-T8 Acceptance clause — The installer reports <code>=== Installation complete ===</code> and <code>Existing sandboxes were recovered and upgraded</code> — but nothing was actually recovered. — add test evidence or identify existing coverage. `print_done` now prints `Installation completed with warnings` and avoids the recovered-and-upgraded line when `_PREEXISTING_SANDBOX_ORPHANED=true`; `test/install-orphaned-sandbox-recovery.test.ts` covers this when the marker is captured. The mktemp fallback can still leave `_PREEXISTING_SANDBOX_ORPHANED=false`.
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: Installer consumption of `upgrade-sandboxes` orphan outcome

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Covered by the security finding's requested shell-harness test: force temp-log creation failure while the stub CLI emits `ORPHANED_SANDBOX_MARKER` and exits 0.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: `scripts/install.sh:2299` falls back to an empty `recovery_log`; `scripts/install.sh:2318-2319` only sets `_PREEXISTING_SANDBOX_ORPHANED` when that log exists and contains the marker.

PRA-2 Resolve/justify — `upgrade-sandboxes --check` still gives generic rebuild guidance for orphan-only records

  • Location: src/lib/actions/upgrade-sandboxes.ts:394
  • Category: correctness
  • Problem: The prior Advisor finding still applies at the current head. In check mode, the code prints `printOrphanedRegistrySandboxes(unobservedOwnGatewaySandboxes)` and then unconditionally prints `Run `nemoclaw upgrade-sandboxes` to rebuild them.`. For an own-gateway orphan, the new remediation says automatic recovery is not possible and the user should run `<name> destroy` followed by `onboard`, so the final rebuild line contradicts the actionable diagnosis.
  • Impact: Users can leave `--check` with mixed instructions and retry `nemoclaw upgrade-sandboxes`, even though the orphan path is expected to skip the stranded record rather than restore its removed gateway registration or Docker image.
  • Recommended action: In the `checkOnly` branch, print the generic rebuild instruction only when there is non-orphan stale work or prepared recovery work that `upgrade-sandboxes` can actually perform. For an orphan-only result, end the output with the destroy/onboard remediation.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `src/lib/actions/upgrade-sandboxes.ts` around the `if (checkOnly)` block and confirm whether `console.log(` Run \`${CLI_NAME} upgrade-sandboxes\` to rebuild them.`);` is gated away when all stale/unknown entries are own-gateway orphans.
  • Missing regression test: Extend `prints the orphan diagnosis in --check mode so check and auto agree ([NemoClaw][onboard][All Platforms] nemoclaw uninstall + reinstall flow leaves an orphaned sandbox that the reinstalled CLI can't recover or clean up #6520)` in `src/lib/actions/upgrade-sandboxes-recovery.test.ts` to assert an orphan-only `--check` output includes the destroy/onboard remediation and does not include the unqualified `Run `nemoclaw upgrade-sandboxes` to rebuild them.` line.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `src/lib/actions/upgrade-sandboxes.ts` around the `if (checkOnly)` block and confirm whether `console.log(` Run \`${CLI_NAME} upgrade-sandboxes\` to rebuild them.`);` is gated away when all stale/unknown entries are own-gateway orphans.
  • Evidence: `unknownWithoutOrphans` excludes unknown orphans and `printOrphanedRegistrySandboxes(...)` is called in check mode, but the next line always prints the generic rebuild command regardless of whether `stale` contains only own-gateway orphans.

PRA-3 Resolve/justify — Installer orphan detection can fail open when temp-log capture is unavailable

  • Location: scripts/install.sh:2299
  • Category: security
  • Problem: `recover_preexisting_sandboxes_before_onboard` detects the zero-exit orphan outcome by teeing CLI output into a temp file and grepping for the marker. If `mktemp` fails, the fallback still runs `upgrade-sandboxes --auto` but cannot inspect stdout, so `_PREEXISTING_SANDBOX_ORPHANED` remains false even if the CLI emitted the orphan marker.
  • Impact: A host with an unavailable or failing temp directory can still receive the false clean summary that this PR is designed to prevent. Because the installer is making lifecycle trust decisions from text output, losing the capture path should not silently downgrade an orphan warning into a recovered-and-upgraded claim.
  • Recommended action: Make marker capture fail closed: either use a structured CLI outcome side channel, capture stdout through a safe fallback while streaming it, or treat successful recovery with unavailable capture as a warning that avoids claiming existing sandboxes were recovered.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `scripts/install.sh` lines around `recovery_log="$(mktemp ...)" || recovery_log=""` and the later `if [ -n "$recovery_log" ] && grep -Fq ...`; confirm the fallback path has no way to set `_PREEXISTING_SANDBOX_ORPHANED` from CLI output.
  • Missing regression test: Add a shell-harness test in `test/install-orphaned-sandbox-recovery.test.ts` that forces `mktemp` or temp-log creation to fail while the stub CLI emits `ORPHANED_SANDBOX_MARKER` and exits 0; assert the installer does not print the clean recovered-and-upgraded summary.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `scripts/install.sh` lines around `recovery_log="$(mktemp ...)" || recovery_log=""` and the later `if [ -n "$recovery_log" ] && grep -Fq ...`; confirm the fallback path has no way to set `_PREEXISTING_SANDBOX_ORPHANED` from CLI output.
  • Evidence: `scripts/install.sh:2299` assigns an empty `recovery_log` on `mktemp` failure, and `scripts/install.sh:2318-2319` only greps for `recorded sandbox(es) were not found on their recorded gateway` when that temp file exists.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
docs/reference/commands.mdx (1)

2415-2418: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the em dash.

Line 2418 uses an em dash ("— this typically means..."). As per path instructions, docs should "Avoid filler, hype, rhetorical questions, emoji, em-dashes, and unnecessary bold text."

✏️ Proposed fix
-A recorded sandbox that is not observed in any phase on its own recorded gateway is reported as not found there, with remediation guidance — this typically means its gateway registration or Docker image was removed (for example by `$$nemoclaw uninstall`, which preserves `sandboxes.json` but removes both).
+A recorded sandbox that is not observed in any phase on its own recorded gateway is reported as not found there, with remediation guidance. This typically means its gateway registration or Docker image was removed (for example by `$$nemoclaw uninstall`, which preserves `sandboxes.json` but removes both).
🤖 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 `@docs/reference/commands.mdx` around lines 2415 - 2418, Update the prose in
the relevant docs entry under the commands reference to remove the em dash in
the sentence describing a recorded sandbox not found on its recorded gateway;
rewrite that clause as a plain sentence or use a period/semicolon instead. Keep
the meaning and remediation guidance intact, and make sure the edited text stays
consistent with the surrounding command documentation style in the same section.

Source: Path instructions

🤖 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/lib/actions/uninstall/run-plan-preserved-registry.test.ts`:
- Around line 70-74: The test mock in run-plan-preserved-registry.test.ts is
using branching inside the run helper to dispatch behavior based on command and
args, which hides incorrect calls instead of asserting them. Refactor the mock
around the run function in the test setup to avoid if-based logic: use separate
scenario-specific deps/mocks or a simple command-to-result lookup so each case
is explicit and observable. Keep the behavior coverage the same, but remove the
conditional dispatch from the mock and assert directly on the expected command
invocation and returned value.

In `@src/lib/actions/upgrade-sandboxes.ts`:
- Around line 352-384: The orphan detection in upgrade-sandboxes is overlapping
with the version classification, so the same sandbox can be printed twice.
Update the logic around `checkAgentVersionForUpgrade`, `unknown`, and
`unobservedOwnGatewaySandboxes` so orphaned records are excluded from the
version-based lists or the orphan print path is skipped when they already appear
there. Keep the deduping focused on the `printOrphanedRegistrySandboxes` branch
and the `unknown` classification flow.

---

Nitpick comments:
In `@docs/reference/commands.mdx`:
- Around line 2415-2418: Update the prose in the relevant docs entry under the
commands reference to remove the em dash in the sentence describing a recorded
sandbox not found on its recorded gateway; rewrite that clause as a plain
sentence or use a period/semicolon instead. Keep the meaning and remediation
guidance intact, and make sure the edited text stays consistent with the
surrounding command documentation style in the same section.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e767ab8c-d790-422a-a9f8-18acb11c04c7

📥 Commits

Reviewing files that changed from the base of the PR and between e962d05 and 6836af3.

📒 Files selected for processing (11)
  • docs/get-started/quickstart.mdx
  • docs/manage-sandboxes/lifecycle.mdx
  • docs/reference/commands.mdx
  • docs/reference/host-files-and-state.mdx
  • scripts/install.sh
  • src/lib/actions/uninstall/run-plan-preserved-registry.test.ts
  • src/lib/actions/uninstall/run-plan.ts
  • src/lib/actions/upgrade-sandboxes-display.test.ts
  • src/lib/actions/upgrade-sandboxes-recovery.test.ts
  • src/lib/actions/upgrade-sandboxes.ts
  • test/install-orphaned-sandbox-recovery.test.ts

Comment thread src/lib/actions/uninstall/run-plan-preserved-registry.test.ts Outdated
Comment thread src/lib/actions/upgrade-sandboxes.ts Outdated
An orphaned record with no cached or probeable version landed in both the
"Unknown version" bucket (start-and-rerun guidance) and the orphan block
(destroy/onboard remediation) — conflicting advice for the same sandbox.
Its version is unknown because the sandbox is gone, so only the orphan
block reports it. Stale orphans keep their stale listing: version drift
there is real information.

Refs #6520

Signed-off-by: Dongni Yang <dongniy@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/lib/actions/upgrade-sandboxes-recovery.test.ts (1)

372-394: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test correctly validates the unknown-version orphan de-duplication fix.

Title follows behavior-oriented + (#1234) suffix convention, and the assertions target the public console-output boundary rather than internals.

One optional gap: unlike the sibling orphan tests in this suite, this test doesn't assert rebuildSpy was not called, even though NEMOCLAW_RESTORE_LATEST_BACKUP_ON_RECREATE is set to "0". Adding that assertion would tighten confidence that no rebuild attempt is triggered for this orphan.

✅ Optional strengthening
     expect(console.log).toHaveBeenCalledWith(
       expect.stringContaining("were not found on their recorded gateway: my-assistant"),
     );
     expect(console.log).not.toHaveBeenCalledWith(expect.stringContaining("Unknown version"));
+    expect(harness.rebuildSpy).not.toHaveBeenCalled();
   });
🤖 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/lib/actions/upgrade-sandboxes-recovery.test.ts` around lines 372 - 394,
The new orphan de-duplication test in upgradeSandboxes should also verify that
no rebuild is attempted when NEMOCLAW_RESTORE_LATEST_BACKUP_ON_RECREATE is "0".
Update the test around createRecoveryHarness and upgradeSandboxes to assert
rebuildSpy was not called, matching the sibling orphan cases and strengthening
the public-behavior coverage without changing the existing console-output
assertions.
🤖 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.

Nitpick comments:
In `@src/lib/actions/upgrade-sandboxes-recovery.test.ts`:
- Around line 372-394: The new orphan de-duplication test in upgradeSandboxes
should also verify that no rebuild is attempted when
NEMOCLAW_RESTORE_LATEST_BACKUP_ON_RECREATE is "0". Update the test around
createRecoveryHarness and upgradeSandboxes to assert rebuildSpy was not called,
matching the sibling orphan cases and strengthening the public-behavior coverage
without changing the existing console-output assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 80d63cb7-c4a6-4f16-a916-909e2ad55b91

📥 Commits

Reviewing files that changed from the base of the PR and between 7580557 and fc79a06.

📒 Files selected for processing (2)
  • src/lib/actions/upgrade-sandboxes-recovery.test.ts
  • src/lib/actions/upgrade-sandboxes.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/actions/upgrade-sandboxes.ts

… into domain modules

PR Review Advisor findings on #6539: upgrade-sandboxes.ts and run-plan.ts
absorbed the #6520 logic as monolith growth. Move the pure pieces to the
domain layer where they are independently unit-tested:

- domain/maintenance/orphan-detection.ts: ORPHANED_SANDBOX_MARKER, the
  own-gateway orphan classification, and the summary/remediation wording;
  the action file keeps only a rendering wrapper. The install.sh harness
  now imports the marker from the domain module.
- domain/maintenance/upgrade.ts: describeStaleUpgrade with the downgrade
  suffix, plus compareDottedVersions as its canonical home
  (onboard/docker-driver-gateway-compat re-exports it for existing
  consumers). Display tests move next to the domain module.
- domain/uninstall/messaging.ts: preserved-registry warnings and the
  delete/no-op skip wording, shared by run-plan.ts.

Also print the orphan diagnosis in `upgrade-sandboxes --check` so check
mode and auto mode agree on the source-of-truth diagnosis (advisor PRA-2),
covered by a new recovery test.

Refs #6520

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
@Dongni-Yang

Copy link
Copy Markdown
Contributor Author

PR Review Advisor disposition (54ffc02):

  • Nemotron PRA-2 / PRA-3 (monolith growth) — Extracted. Orphan detection (marker, classification, wording) now lives in src/lib/domain/maintenance/orphan-detection.ts; describeStaleUpgrade/downgrade labeling in src/lib/domain/maintenance/upgrade.ts (with compareDottedVersions moved to its canonical domain home, re-exported from onboard/docker-driver-gateway-compat for existing consumers); uninstall messaging in src/lib/domain/uninstall/messaging.ts. All three have co-located unit tests. Actual counts: upgrade-sandboxes.ts 513→465 (base 439 — residual growth is orchestration call sites and the check-mode parity line), run-plan.ts 990→992 after biome formatting (the messaging bodies moved out; the executor keeps the call sites and the import block). The logic the finding targeted is out of the action files.
  • GPT-5.5 PRA-2 (check/auto disagreement) — Fixed: upgrade-sandboxes --check now prints the same orphan diagnosis as auto mode; covered by a new recovery test.
  • PRA-1 / PRA-6 / PRA-8 (source-of-truth review) — Answered in the PR description ("Source-of-Truth Constraints" section): invalid state, source boundary, why the preserve contract can't be fixed here, regression tests, and the removal condition.
  • PRA-5 (cross-language marker coupling) — The marker constant is the single source (orphan-detection.ts); test/install-orphaned-sandbox-recovery.test.ts builds its stub from the imported constant and drives the real install.sh grep, so drift on either side fails the suite. Comments on both sides reference this gate.
  • PRA-7 (remediation text sync) — Both texts now derive from domain/uninstall/messaging.ts and domain/maintenance/orphan-detection.ts, each pinned by unit tests asserting the same command names.
  • PRA-4 / PRA-11 / T4 (test-file split, shared harness) — Deferred as follow-up: upgrade-sandboxes-recovery.test.ts remains within the repo's test-size budget, and extracting createRecoveryHarness mid-PR would churn 25 passing [All Platforms][Upgrade] v0.0.55 → v0.0.76 leaves pre-existing sandboxes stuck in Provisioning/Error — user data inaccessible until manual rebuild #6114 contract tests. Happy to do it in a dedicated test-refactor PR.
  • Runtime-validation follow-ups (T1–T3/T6) — Live uninstall→reinstall verification on macOS + colima is being run by the operator against this branch (NEMOCLAW_INSTALL_REF=fix/6520-uninstall-reinstall-orphan-sandbox); logs will be posted here.

Signed-off-by: Dongni Yang dongniy@nvidia.com

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/actions/upgrade-sandboxes.ts (1)

316-329: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Exclude rejected recoveries from the orphan summary src/lib/actions/upgrade-sandboxes.ts:318-329, 459-461
rejectedRecoveries can still satisfy classifyOrphanedRegistrySandboxes(...), so a sandbox can be printed once under “Backup recovery blocked:” and again in the orphan block at the end. Filter rejected names out before printOrphanedRegistrySandboxes(...) and add coverage for the rejected-recovery + orphan overlap.

🤖 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/lib/actions/upgrade-sandboxes.ts` around lines 316 - 329, Rejected
recoveries are still being treated as orphans, which can cause the same sandbox
to appear in both the recovery-blocked summary and the orphan summary. Update
the logic around classifyOrphanedRegistrySandboxes in upgrade-sandboxes.ts to
exclude rejectedRecovery names before calling printOrphanedRegistrySandboxes,
and make sure the orphan filtering also respects the rejectedRecoveries set when
building the final lists. Add a test that covers the overlap between rejected
recoveries and orphaned sandboxes so the same sandbox is not printed twice.
🤖 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/lib/domain/maintenance/orphan-detection.test.ts`:
- Around line 15-25: The helper in classify currently introduces a new if
statement inside resolveGatewayBinding, which violates the Codebase Growth
Guardrails for this test. Refactor the mock in classifyOrphanedRegistrySandboxes
to avoid any conditional statement, using a conditional expression or a small
throwing helper for the "corrupt" gatewayName case instead. Keep the existing
behavior for Entry inputs and the observedNames/reconnectedNames setup
unchanged.

---

Outside diff comments:
In `@src/lib/actions/upgrade-sandboxes.ts`:
- Around line 316-329: Rejected recoveries are still being treated as orphans,
which can cause the same sandbox to appear in both the recovery-blocked summary
and the orphan summary. Update the logic around
classifyOrphanedRegistrySandboxes in upgrade-sandboxes.ts to exclude
rejectedRecovery names before calling printOrphanedRegistrySandboxes, and make
sure the orphan filtering also respects the rejectedRecoveries set when building
the final lists. Add a test that covers the overlap between rejected recoveries
and orphaned sandboxes so the same sandbox is not printed twice.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0bca91ba-0e8c-47b2-97cf-b040a0487b7f

📥 Commits

Reviewing files that changed from the base of the PR and between fc79a06 and 54ffc02.

📒 Files selected for processing (11)
  • src/lib/actions/uninstall/run-plan.ts
  • src/lib/actions/upgrade-sandboxes-recovery.test.ts
  • src/lib/actions/upgrade-sandboxes.ts
  • src/lib/domain/maintenance/orphan-detection.test.ts
  • src/lib/domain/maintenance/orphan-detection.ts
  • src/lib/domain/maintenance/upgrade.test.ts
  • src/lib/domain/maintenance/upgrade.ts
  • src/lib/domain/uninstall/messaging.test.ts
  • src/lib/domain/uninstall/messaging.ts
  • src/lib/onboard/docker-driver-gateway-compat.ts
  • test/install-orphaned-sandbox-recovery.test.ts
✅ Files skipped from review due to trivial changes (1)
  • src/lib/domain/uninstall/messaging.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/install-orphaned-sandbox-recovery.test.ts
  • src/lib/actions/uninstall/run-plan.ts

Comment thread src/lib/domain/maintenance/orphan-detection.test.ts
…linear

The compareDottedVersions move shifted DEFAULT_COMPAT_IMAGE from line 11
to 16 in docker-driver-gateway-compat.ts; update the platform-matrix
citation and regenerate the platform docs so the cited line stays
non-empty. Also restructure the orphan-detection resolver stub so changed
test files stay free of if statements per the growth guardrail.

Refs #6520

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
@Dongni-Yang

Copy link
Copy Markdown
Contributor Author

Live verification on macOS (Mac Studio) + colima — full repro-before / verify-after cycle (answers the PR Review Advisor runtime-validation follow-ups T1–T3/T6):

Log Released tags (before) This branch (after)
uninstall (v0.0.77) ✅ bug reproduced: 6× Deleted … skipped, no unrecoverability warning
reinstall (→ v0.0.76) ✅ bug reproduced: skip line + false "recovered and upgraded", no downgrade callout
same-tag reinstall (v0.0.76) ✅ bug reproduced: same contradiction while the sandbox is actually gone
uninstall (branch) Provider 'nvidia-nim' already removed or unreachable; ✅ Preserved sandboxes.json references the gateway… warning + remediation
reinstall (branch) 1 recorded sandbox(es) were not found on their recorded gateway + remediation; ✅ Installation completed with warnings, no false "recovered" claim
remediation (branch) nemoclaw <name> destroy + nemoclaw onboard rebuild clean, dashboard live

Environment: macOS (Mac Studio), colima container runtime, installed via NEMOCLAW_INSTALL_REF=fix/6520-uninstall-reinstall-orphan-sandbox. Both the versioned-reinstall repro from the original report and the same-tag variant (which the observation-based classifier specifically covers) were exercised.

Signed-off-by: Dongni Yang dongniy@nvidia.com

@Dongni-Yang Dongni-Yang added the v0.0.79 Release target label Jul 9, 2026
@cv
cv merged commit 940eee4 into main Jul 9, 2026
48 checks passed
@cv
cv deleted the fix/6520-uninstall-reinstall-orphan-sandbox branch July 9, 2026 09:01
@jyaunches jyaunches mentioned this pull request Jul 9, 2026
21 tasks
cv pushed a commit that referenced this pull request Jul 9, 2026
<!-- markdownlint-disable MD041 -->
## Summary
Adds the pre-tag v0.0.79 release notes entry to
`docs/about/release-notes.mdx` so the release plan can be generated
after docs merge.
The entry summarizes the merged v0.0.79 release train across inference,
diagnostics, runtime hardening, policies, onboarding recovery, and
release validation.

## Changes
- Added the v0.0.79 release notes section with linked follow-up
documentation for OpenRouter onboarding, managed vLLM changes,
completion and logging, Deep Agents runtime limits, policy updates,
onboarding recovery, and release validation.
- Source summary:
- #6461 -> `docs/about/release-notes.mdx`: Documents OpenRouter
onboarding support and links to inference/provider references.
- #6271 and #6272 -> `docs/about/release-notes.mdx`: Documents shell
completion and structured logging highlights.
- #6465, #6539, #6570, and #6528 -> `docs/about/release-notes.mdx`:
Documents status route-drift, orphaned sandbox, gateway cleanup, and DGX
Spark express-install diagnostics.
- #6523, #6551, #6484, #6488, #6324, and #6542 ->
`docs/about/release-notes.mdx`: Documents managed vLLM, Qwen3.6 tool
parser, compaction, and timeout/readiness improvements.
- #6559, #6538, #6560, #6568, #6552, #6567, and #6587 ->
`docs/about/release-notes.mdx`: Documents runtime, credential, proxy,
PID namespace, TOML, and provider-state hardening.
- #6541, #5415, #6246, #6496, and #6573 ->
`docs/about/release-notes.mdx`: Documents GitHub policy, Gmail policy,
MCP allowlist, WhatsApp, and messaging-variant updates.
- #6253, #6572, #6444, #6536, and #5860 ->
`docs/about/release-notes.mdx`: Documents onboarding resume and
create-step recovery improvements.
- #6508, #6527, #5506, #6588, #6446, #6447, #6582, #6296, #6367, #6397,
and #6505 -> `docs/about/release-notes.mdx`: Documents docs,
release-risk, and E2E validation updates.

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [x] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates
<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [ ] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [x] Tests not applicable — justification: Release-note prose only.
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [ ] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: Tests
not applicable, release-note prose only.
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

Docs validation note: `npm run docs:check-agent-variants && npm run
docs:check-routes && git diff --check` passed. Full `npm run docs` is
currently blocked before Fern validation because the pinned
`fern-api@5.65.2` package is unavailable from npm (`ETARGET No matching
version found`).

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added release notes for v0.0.79 with a new summary of recent
improvements, including onboarding and inference options, operator/CLI
diagnostics, sandbox recovery hardening, runtime limits, network policy
behavior, and release validation updates.
  * Added updated references and links for the latest release.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
…nd reinstall (NVIDIA#6539)

<!-- markdownlint-disable MD041 -->
## Summary

`nemoclaw uninstall --yes` preserves `sandboxes.json` but removes the
gateway registration, provider registrations, and Docker image its
recorded sandboxes depend on; a later reinstall then reported
"Installation complete" and "Existing sandboxes were recovered and
upgraded" while silently stranding the user's sandbox with no
remediation path. This PR makes the whole cycle honest: the CLI derives
a dedicated, observation-based orphan signal (independent of version
classification, so the same-version reinstall repro is also caught), the
installer consumes it to report "completed with warnings" with concrete
remediation instead of false success, and uninstall warns at preserve
time that the kept registry is not self-sufficient.

## Related Issue

Fixes NVIDIA#6520

## Changes

- `src/lib/actions/upgrade-sandboxes.ts`: new orphan classifier — a
recorded sandbox absent in any phase from the selected gateway, whose
persisted binding resolves to that same gateway, is reported via the
exported `ORPHANED_SANDBOX_MARKER` line with `<name> destroy` /
`onboard` remediation. Sandboxes bound to another live gateway,
exonerated by the confirming second listing, or restored by
prepared-backup recovery are excluded, preserving the NVIDIA#6114
multi-gateway exit-0 contract. Stale listings now label version
regressions with a `(downgrade)` suffix (`describeStaleUpgrade` exported
for tests).
- `scripts/install.sh`: `recover_preexisting_sandboxes_before_onboard`
mirrors recovery output through a trap-registered temp log
(`_cleanup_files`), takes the CLI's own exit status via `PIPESTATUS[0]`
so a `tee` write failure cannot fake the NVIDIA#5735 failure path, and greps
the orphan marker into `_PREEXISTING_SANDBOX_ORPHANED`; `print_done`
downgrades the banner to `=== Installation completed with warnings ===`
and replaces the false recovery claim with hedged wording plus
remediation; the onboarding-skip line no longer claims recovery in the
orphaned case.
- `src/lib/actions/uninstall/run-plan.ts`: warns at preserve time that
preserved `sandboxes.json` records cannot be recovered automatically on
reinstall (non-interactive and interactive keep paths, suppressed under
`--destroy-user-data`); provider/sandbox delete no-ops now print
`already removed or unreachable` instead of the contradictory `Deleted …
skipped`.
- Tests: `test/install-orphaned-sandbox-recovery.test.ts` (bash harness
driving the real `install.sh` classification and `print_done`; the stub
output is built from the exported marker constant so wording drift on
either side fails the suite),
`src/lib/actions/upgrade-sandboxes-display.test.ts` (downgrade
labeling), five orphan-classifier cases in
`upgrade-sandboxes-recovery.test.ts` (own-gateway orphan for current-
and stale-classified records; negatives for other-gateway,
reconnect-race, and recovered sandboxes),
`src/lib/actions/uninstall/run-plan-preserved-registry.test.ts`
(preserve warning on non-interactive and interactive keep paths, no-op
wording fix, purge suppression).
- Docs: `reference/commands.mdx`, `manage-sandboxes/lifecycle.mdx`,
`get-started/quickstart.mdx`, `reference/host-files-and-state.mdx`
updated for the preserve-time warning, the warnings install outcome, the
remediation path, and the `(downgrade)` label.

## Source-of-Truth Constraints (advisor PRA-1/6/8)

- **Invalid state:** preserved `sandboxes.json` entries whose gateway
registration, provider registrations, and Docker image were removed by a
prior `nemoclaw uninstall`.
- **Source boundary:** the uninstall command's preserve-by-default
user-data contract. `--yes` is deliberately non-destructive; existing
automation depends on it never purging user data.
- **Why the root cause is not fixed here:** making uninstall/reinstall
symmetric means either preserving the gateway/providers/image (defeats
uninstall) or purging the registry by default (breaks the preserve
contract and `--yes` automation). Both are behavior contracts owned by
uninstall, out of scope for an honesty fix. The chosen resolution
matches the issue's stated "Acceptable" bar: detect, diagnose with
remediation, and never report false success.
- **Regression tests:** `test/install-orphaned-sandbox-recovery.test.ts`
(real install.sh classification + print_done, stub built from the domain
marker constant), `src/lib/actions/upgrade-sandboxes-recovery.test.ts`
(orphan classifier incl. multi-gateway/reconnect/recovered negatives and
check-mode parity),
`src/lib/domain/maintenance/orphan-detection.test.ts`,
`src/lib/domain/uninstall/messaging.test.ts`,
`src/lib/actions/uninstall/run-plan-preserved-registry.test.ts`.
- **Removal condition:** when uninstall either preserves everything its
recorded sandboxes depend on, purges the registry by default, or records
gateway-removal evidence in the registry so reinstall can distinguish
states — the observation-based classifier and installer marker grep can
then be retired.

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [x] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates
<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: `npx
vitest run --project cli
src/lib/actions/upgrade-sandboxes-display.test.ts
src/lib/actions/upgrade-sandboxes-recovery.test.ts
src/lib/actions/uninstall/` → 79 passed; `npx vitest run --project
integration test/install-orphaned-sandbox-recovery.test.ts
test/install-preexisting-sandbox-recovery.test.ts
test/install-onboard-exit.test.ts` → 14 passed; `npm run typecheck:cli`
clean; `shfmt -i 2 -ci -bn -d scripts/install.sh` clean
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Dongni Yang <dongniy@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added orphan-detection for preserved registry sandboxes to drive more
accurate “recovered with warnings” vs “not recovered” messaging.
* Enhanced stale-sandbox labeling (downgrade/unknown-build) and refined
upgrade/recovery output to reduce ambiguity.
* **Bug Fixes**
* Uninstall and recovery messaging now better distinguishes
unreachable/removal no-ops from successful deletions.
* “Installation completed with warnings” is shown when recovery exits
cleanly but sandboxes can’t be recovered.
* **Documentation**
* Updated quickstart, lifecycle, command, and state/uninstall docs with
remediation guidance.
* **Tests**
* Added/expanded Vitest coverage for orphan detection, installer
completion honesty, and uninstall messaging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
<!-- markdownlint-disable MD041 -->
## Summary
Adds the pre-tag v0.0.79 release notes entry to
`docs/about/release-notes.mdx` so the release plan can be generated
after docs merge.
The entry summarizes the merged v0.0.79 release train across inference,
diagnostics, runtime hardening, policies, onboarding recovery, and
release validation.

## Changes
- Added the v0.0.79 release notes section with linked follow-up
documentation for OpenRouter onboarding, managed vLLM changes,
completion and logging, Deep Agents runtime limits, policy updates,
onboarding recovery, and release validation.
- Source summary:
- NVIDIA#6461 -> `docs/about/release-notes.mdx`: Documents OpenRouter
onboarding support and links to inference/provider references.
- NVIDIA#6271 and NVIDIA#6272 -> `docs/about/release-notes.mdx`: Documents shell
completion and structured logging highlights.
- NVIDIA#6465, NVIDIA#6539, NVIDIA#6570, and NVIDIA#6528 -> `docs/about/release-notes.mdx`:
Documents status route-drift, orphaned sandbox, gateway cleanup, and DGX
Spark express-install diagnostics.
- NVIDIA#6523, NVIDIA#6551, NVIDIA#6484, NVIDIA#6488, NVIDIA#6324, and NVIDIA#6542 ->
`docs/about/release-notes.mdx`: Documents managed vLLM, Qwen3.6 tool
parser, compaction, and timeout/readiness improvements.
- NVIDIA#6559, NVIDIA#6538, NVIDIA#6560, NVIDIA#6568, NVIDIA#6552, NVIDIA#6567, and NVIDIA#6587 ->
`docs/about/release-notes.mdx`: Documents runtime, credential, proxy,
PID namespace, TOML, and provider-state hardening.
- NVIDIA#6541, NVIDIA#5415, NVIDIA#6246, NVIDIA#6496, and NVIDIA#6573 ->
`docs/about/release-notes.mdx`: Documents GitHub policy, Gmail policy,
MCP allowlist, WhatsApp, and messaging-variant updates.
- NVIDIA#6253, NVIDIA#6572, NVIDIA#6444, NVIDIA#6536, and NVIDIA#5860 ->
`docs/about/release-notes.mdx`: Documents onboarding resume and
create-step recovery improvements.
- NVIDIA#6508, NVIDIA#6527, NVIDIA#5506, NVIDIA#6588, NVIDIA#6446, NVIDIA#6447, NVIDIA#6582, NVIDIA#6296, NVIDIA#6367, NVIDIA#6397,
and NVIDIA#6505 -> `docs/about/release-notes.mdx`: Documents docs,
release-risk, and E2E validation updates.

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [x] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates
<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [ ] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [x] Tests not applicable — justification: Release-note prose only.
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [ ] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: Tests
not applicable, release-note prose only.
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

Docs validation note: `npm run docs:check-agent-variants && npm run
docs:check-routes && git diff --check` passed. Full `npm run docs` is
currently blocked before Fern validation because the pinned
`fern-api@5.65.2` package is unavailable from npm (`ETARGET No matching
version found`).

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added release notes for v0.0.79 with a new summary of recent
improvements, including onboarding and inference options, operator/CLI
diagnostics, sandbox recovery hardening, runtime limits, network policy
behavior, and release validation updates.
  * Added updated references and links for the latest release.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
jyaunches added a commit that referenced this pull request Jul 24, 2026
## Summary

Reopen follow-up for #6520: `nemoclaw uninstall --yes` followed by a
curl reinstall hard-aborts at the installer strict pre-upgrade backup.
The uninstall removes the gateway registration and the sandbox container
but preserves `sandboxes.json`, so the stranded record can only ever be
skipped — and the strict gate (`NEMOCLAW_REQUIRE_ALL_SANDBOX_BACKUPS=1`,
#6114) exits 1 before the installer recovery phase
(`recover_preexisting_sandboxes_before_onboard`), the phase that knows
how to surface orphans, ever runs. The #6539 orphan classifier is wired
only into `upgrade-sandboxes`, which is downstream of the aborting
backup.

## What changed

- `backupAll()` now classifies stranded records with the existing
`classifyOrphanedRegistrySandboxes` (unobserved on the selected gateway
AND persisted binding resolving to that same gateway), gated on the new
`isSandboxContainerDefinitivelyAbsent()`. Stranded records are tracked
separately from `skipped`, so the strict gate keeps failing closed for
every genuine skip, and the run ends with `orphanedRegistrySummary` +
`orphanedRegistryRemediation` (same destroy/onboard guidance as the
recovery phase). End-to-end, the installer now proceeds to its recovery
phase, which reports the orphans and yields "completed with warnings"
instead of a hard abort.
- The exemption is two-phase (PRA-1): after the backup loop, a
confirming second pinned listing re-checks every stranded candidate —
the same #6114 confirmation idiom as `upgrade-sandboxes` — and any
candidate the gateway observes again reverts to a genuine strict skip.
Container absence is itself checked per candidate at skip time.
- `isSandboxContainerDefinitivelyAbsent()` fails closed everywhere
absence cannot be proven: non-docker or unknown driver (including a
throwing registry read), and a failed or timed-out labeled listing. The
listing is status-checked (`docker ps -a` with the OpenShell labels)
rather than reusing `findLabeledSandboxContainers`, which swallows
docker errors — a dead daemon yields `null`, never "absent" — and passes
`ignoreError` so the probe cannot `process.exit` the run.
- `backup-all` now pins its sandbox listing to the selected gateway
(same #6114 rationale and idiom as `upgrade-sandboxes`): an unpinned
list taken from a sibling gateway selection must not feed a fail-open
stranded decision.
- The exemption carries an in-code source-of-truth review block (PRA-2):
source boundary (`nemoclaw uninstall` preserves `sandboxes.json` by
design), source-fix constraint (backup-all must not reconcile the
registry; record removal is owned by the recovery phase destroy/onboard
flow), and removal condition (install/uninstall registry reconciliation,
or running the recovery phase before the strict backup).

## Constraints preserved (pinned by tests)

- Strict gate still aborts on any genuine skip (#6114 cases untouched
and green).
- A sandbox bound to a sibling gateway is never claimed stranded, even
when its container is absent on this host.
- An unobserved sandbox whose container still exists keeps the strict
abort (reconnect race).
- A stranded candidate the confirming listing observes again reverts to
a strict skip (lifecycle race).
- Real backup failures (EACCES, non-manifest paths) still
re-throw/abort.
- A registry row with a null/unknown driver keeps the strict abort —
fail closed, since absence cannot be proven for it.
- `ORPHANED_SANDBOX_MARKER` / summary / remediation wording unchanged;
install.sh greps the marker only from the recovery-phase log, which this
change does not touch.

## Review responses

- **PR Review Advisor PRA-1** (revalidate absence before exempting the
gate): addressed by the confirming second pinned listing after the
backup loop plus per-candidate absence checks at skip time; new test
pins the reappeared-candidate revert.
- **PR Review Advisor PRA-2** (bound the workaround to its source fix):
addressed with the in-code source-of-truth review block (source boundary
/ fix constraint / removal condition).
- `reviewed-npm-audit` failure is upstream-wide (also fails on #7289)
and is being remediated by #7286; this PR adds no dependencies.

## Verification

- `vitest`: `maintenance.test.ts` (32), `stopped-sandbox-backup.test.ts`
(24), `orphan-detection.test.ts`, `upgrade-sandboxes-recovery.test.ts`,
`openshell-sandbox-list.test.ts` — 108 tests green; installer lane
`test/install-orphaned-sandbox-recovery.test.ts` (8, drives strict
backup through recovery and the real install.sh orphan grep) green;
`typecheck:cli`, `lint`, and `biome check` clean. The new maintenance
tests pin `GATEWAY_PORT` so they stay green under an exported
`NEMOCLAW_GATEWAY_PORT`.
- Live end-to-end on a workstation (real docker daemon, real registry
file, openshell CLI shimmed to a healthy empty gateway, real built CLI):
stranded record → warning + `0 skipped` + exit 0 under strict mode
(through both listings); same record bound to `gatewayPort: 9999` →
strict abort exit 1 with no orphan claim; same record with a labeled
container present → strict abort exit 1 with no orphan claim.

Refs #6520

Signed-off-by: Dongni Yang <dongniy@nvidia.com>

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


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved backup-all orphan handling: potentially stranded sandboxes
are skipped when their containers are definitively absent, then
rechecked with the same selected gateway before remediation occurs.
* Tightened strict-mode “fail closed” behavior for confirmed absent
containers, including mismatched-gateway scenarios and reappearing
candidates.
* Reduced false “container absent” results by treating Docker/registry
uncertainties as unknown rather than absent.
* **Tests**
* Expanded coverage for stranded-orphan detection and definitive
container absence (including fail-closed, reappearance, and
gateway-pinning scenarios) with improved Docker mock observability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


## Takeover review response

- The PR Review Advisor handoff warning is addressed by an installer
boundary regression that drives strict `backup-all`, orphan recovery,
and the completed-with-warnings result in order.

## Documentation Writer Review

- [x] Documentation writer subagent reviewed the completed changes
- Result: `no-docs-needed`
- Evidence: Reviewed the complete five-file diff at `71a02caebe9f`. No
documentation paths changed; existing docs already cover strict backup
and stranded-record recovery and remediation.
- Agent: Codex Desktop
- PR: #7290
<!-- docs-review-head-sha: 71a02ca -->
<!-- docs-review-agents-blob-sha:
560ff38 -->

Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>

---------

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
Co-authored-by: Charan Jagwani <cjagwani@nvidia.com>
Co-authored-by: Julie Yaunches <jyaunches@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v0.0.79 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[NemoClaw][onboard][All Platforms] nemoclaw uninstall + reinstall flow leaves an orphaned sandbox that the reinstalled CLI can't recover or clean up

3 participants