Skip to content

fix(cli): let strict backup-all skip stranded orphan sandboxes#7290

Merged
jyaunches merged 5 commits into
mainfrom
fix/6520-strict-backup-skip-stranded-sandbox
Jul 24, 2026
Merged

fix(cli): let strict backup-all skip stranded orphan sandboxes#7290
jyaunches merged 5 commits into
mainfrom
fix/6520-strict-backup-skip-stranded-sandbox

Conversation

@Dongni-Yang

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

Copy link
Copy Markdown
Contributor

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 [All Platforms][Upgrade] v0.0.55 → v0.0.76 leaves pre-existing sandboxes stuck in Provisioning/Error — user data inaccessible until manual rebuild #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 [All Platforms][Upgrade] v0.0.55 → v0.0.76 leaves pre-existing sandboxes stuck in Provisioning/Error — user data inaccessible until manual rebuild #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 ([All Platforms][Upgrade] v0.0.55 → v0.0.76 leaves pre-existing sandboxes stuck in Provisioning/Error — user data inaccessible until manual rebuild #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

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

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.

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

  • 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: fix(cli): let strict backup-all skip stranded orphan sandboxes #7290

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

`nemoclaw uninstall --yes` followed by a curl reinstall aborts at the
installer strict pre-upgrade backup: the uninstall removes the gateway
registration and 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)
that knows how to surface orphans can run.

Teach backupAll() to classify such records with the #6539 orphan
classifier (unobserved on the selected gateway AND persisted binding
resolving to that same gateway) and to downgrade the strict abort to a
warning only when the sandbox OpenShell-labeled container is
definitively absent. 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 so standalone backup-all users get the same
destroy/onboard guidance as the recovery phase.

The new 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 (not findLabeledSandboxContainers, which
swallows docker errors) so a dead daemon yields null, never "absent",
and it passes ignoreError so the probe cannot process.exit the run.
backup-all now also pins its sandbox listing to the selected gateway
(same #6114 rationale as upgrade-sandboxes): an unpinned list from a
sibling selection must not feed a fail-open stranded decision.

Constraints preserved (pinned by tests):
- strict gate still aborts on any genuine skip (#6114)
- a sandbox bound to a sibling gateway is never claimed stranded
- an unobserved sandbox whose container still exists keeps the abort
  (reconnect race)
- real backup failures still re-throw/abort
- a registry row with a null/unknown driver keeps the strict abort
  (fail closed; absence cannot be proven for it)

Refs #6520

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

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

backupAll() now performs gateway-scoped orphan classification and skips sandboxes whose Docker containers are definitively absent. A fail-closed absence classifier probes labeled Docker containers, while tests cover gateway binding, strict-mode behavior, probe failures, and remediation reporting.

Changes

Stranded orphan backup handling

Layer / File(s) Summary
Definitive Docker container absence detection
src/lib/actions/sandbox/stopped-sandbox-backup.ts, src/lib/actions/sandbox/stopped-sandbox-backup.test.ts
Adds a fail-closed, labeled docker ps -a probe and tests Docker, registry, timeout, command failure, and non-Docker cases.
Gateway-scoped backup integration and validation
src/lib/actions/maintenance.ts, src/lib/actions/maintenance.test.ts
Pins sandbox listing to the selected gateway, skips definitively absent stranded orphans, confirms candidates with a second listing, reports remediation instructions, and preserves strict abort behavior for uncertain or reappearing classifications.

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

Sequence Diagram(s)

sequenceDiagram
  participant backupAll
  participant gatewaySandboxListing
  participant isSandboxContainerDefinitivelyAbsent
  participant dockerRun
  backupAll->>gatewaySandboxListing: capture selected-gateway sandbox list
  backupAll->>isSandboxContainerDefinitivelyAbsent: classify sandbox container absence
  isSandboxContainerDefinitivelyAbsent->>dockerRun: run labeled docker ps -a probe
  dockerRun-->>isSandboxContainerDefinitivelyAbsent: container listing or probe failure
  isSandboxContainerDefinitivelyAbsent-->>backupAll: definitive absence or uncertain state
  backupAll->>gatewaySandboxListing: confirm stranded candidate
  backupAll-->>backupAll: skip, report, or preserve strict failure
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: strict backup-all now skips stranded orphan sandboxes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/6520-strict-backup-skip-stranded-sandbox

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

@github-code-quality

github-code-quality Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit 71a02ca in the fix/6520-strict-back... branch remains at 96%, unchanged from commit 5faed2c in the main branch.

TypeScript / code-coverage/cli

The overall coverage in commit 71a02ca in the fix/6520-strict-back... branch remains at 80%, unchanged from commit 5faed2c in the main branch.

Show a code coverage summary of the most impacted files.
File main 5faed2c fix/6520-strict-back... 71a02ca +/-
src/lib/domain/...an-detection.ts 100% 100% 0%
src/lib/onboard...der-metadata.ts 100% 100% 0%
src/lib/sandbox...rce-identity.ts 87% 87% 0%
src/lib/state/m...ock-identity.ts 95% 95% 0%
src/lib/state/g...way-registry.ts 94% 95% +1%
src/lib/actions/maintenance.ts 79% 84% +5%
src/lib/actions...ndbox-backup.ts 72% 85% +13%

Updated July 24, 2026 17:00 UTC

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — Informational

Advisor assessment: Informational / high confidence
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions
Status: No actionable findings remain in the canonical review ledger.

Model lanes

  • GPT-5.6 Terra (primary): Completed · high confidence · 0 blockers · 0 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Completed · high confidence · 0 blockers · 0 warnings · 0 suggestions
  • Model comparison: normalized findings match; normalized E2E selections differ; severity counts match.

Nemotron output stays in workflow artifacts and does not change the assessment above.

E2E guidance

Advisory only. E2E / PR Gate selects and runs jobs independently.

Recommended E2E: onboard-repair, onboard-resume, state-backup-restore, upgrade-stale-sandbox

2 optional E2E recommendations
  • gateway-guard-recovery
  • sandbox-operations

Workflow run details

This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge.

@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/maintenance.ts (1)

66-96: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting stranded-orphan classification out of backupAll.

The logic here is correct, but backupAll (lines 57-260) is already a very large function, and this change adds another self-contained concern (gateway-scoped orphan classification + stranded tracking + summary reporting) directly inline. As per coding guidelines, **/*.{js,ts,tsx} should "Keep function complexity low and avoid introducing unnecessary complexity hotspots." Pulling the orphan-classification setup (lines 79-96) and/or the stranded-check branch (115-120) into a small named helper would keep backupAll's control flow easier to follow without changing behavior.

Also applies to: 105-120, 224-227

🤖 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/maintenance.ts` around lines 66 - 96, Extract the
gateway-scoped orphan classification and stranded-sandbox handling from
backupAll into a small named helper, covering the setup around
classifyOrphanedRegistrySandboxes and the stranded-check branch and summary
reporting. Have the helper accept the existing sandbox/listing context and
return the values backupAll needs, preserving gateway selection,
container-absence checks, and current behavior.

Source: Coding guidelines

🤖 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/maintenance.ts`:
- Around line 66-96: Extract the gateway-scoped orphan classification and
stranded-sandbox handling from backupAll into a small named helper, covering the
setup around classifyOrphanedRegistrySandboxes and the stranded-check branch and
summary reporting. Have the helper accept the existing sandbox/listing context
and return the values backupAll needs, preserving gateway selection,
container-absence checks, and current behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2b8ee3a3-c3ca-42c2-b33e-db072b62a2ea

📥 Commits

Reviewing files that changed from the base of the PR and between d5fa194 and cd8fc00.

📒 Files selected for processing (4)
  • src/lib/actions/maintenance.test.ts
  • src/lib/actions/maintenance.ts
  • src/lib/actions/sandbox/stopped-sandbox-backup.test.ts
  • src/lib/actions/sandbox/stopped-sandbox-backup.ts

Address PR Review Advisor findings on #7290:

- PRA-1: the orphan classification was only as fresh as the pre-loop
  listing, and the backup loop can run for minutes. backup-all now
  re-lists the selected gateway after the loop (same two-phase
  confirmation as upgrade-sandboxes, #6114) and reverts any stranded
  candidate the gateway observes again to the genuine strict skip it
  would otherwise have been.
- PRA-2: document the exemption source-of-truth boundary in code: the
  stranded state is created by `nemoclaw uninstall` (which preserves
  sandboxes.json by design), backup-all must not reconcile the registry
  itself, and the exemption is removable once install/uninstall
  reconciles the registry or the installer recovery phase runs before
  the strict pre-upgrade backup.
- Apply biome format to the absence tests.

Refs #6520

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
@Dongni-Yang Dongni-Yang added the v0.0.91 Release target label Jul 21, 2026
@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output area: install Install, setup, prerequisites, or uninstall flow area: onboarding Onboarding FSM, provider setup, sandbox launch, or first-run flow area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression labels Jul 21, 2026
@senthilr-nv senthilr-nv added v0.0.92 and removed v0.0.91 Release target labels Jul 21, 2026
Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
@jyaunches
jyaunches merged commit a63ac38 into main Jul 24, 2026
121 of 125 checks passed
@jyaunches
jyaunches deleted the fix/6520-strict-backup-skip-stranded-sandbox branch July 24, 2026 17:21
@senthilr-nv senthilr-nv mentioned this pull request Jul 25, 2026
23 tasks
senthilr-nv added a commit that referenced this pull request Jul 25, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Adds the canonical pre-tag `## v0.0.95` release entry to
`docs/changelog/2026-07-24.mdx`, before the existing v0.0.94 entry. The
entry summarizes approved user-visible changes merged since v0.0.94 and
excludes internal-only prerequisites.

## Changes

- Adds the v0.0.95 summary and detailed bullets for gateway lifecycle,
recovery, state transfer, inference compatibility, sandbox security,
Discord policy, and E2E evidence.
- Links each user-facing theme to the most specific published
documentation.
- Records the release entry in the shared native changelog used by the
OpenClaw, Hermes, and Deep Agents guides.

Source summary:

- [#7246](#7246),
[#7228](#7228),
[#7267](#7267),
[#7489](#7489),
[#7509](#7509),
[#7351](#7351), and
[#7290](#7290) ->
`docs/changelog/2026-07-24.mdx`: Gateway authority, forward teardown and
retry, managed recovery, Hermes restart recovery, scoped uninstall, and
orphan-aware backup behavior.
- [#7344](#7344) and
[#7416](#7416) ->
`docs/changelog/2026-07-24.mdx`: Atomic SQLite restore and host download
verification.
- [#7476](#7476),
[#7347](#7347),
[#7281](#7281),
[#7485](#7485),
[#7491](#7491), and
[#7422](#7422) ->
`docs/changelog/2026-07-24.mdx`: Windows Ollama reuse, CDI fallback,
bounded OpenRouter connection setup, Nemotron-3 request compatibility,
and managed Deep Agents retry and provider-error behavior.
- [#6884](#6884),
[#7481](#7481),
[#6878](#6878),
[#7467](#7467),
[#7502](#7502),
[#7503](#7503),
[#7504](#7504), and
[#7486](#7486) ->
`docs/changelog/2026-07-24.mdx`: Trusted base-image overrides, local
rebuild images, runtime validation, config preservation, reviewed
package updates, and fewer final-image payload layers.
- [#7303](#7303) ->
`docs/changelog/2026-07-24.mdx`: Scoped Discord application-command
management.
- [#7488](#7488),
[#7465](#7465),
[#7497](#7497),
[#7464](#7464),
[#7501](#7501),
[#7494](#7494), and
[#7493](#7493) ->
`docs/changelog/2026-07-24.mdx`: Selected-test risk signals, retry
cleanup, full root-image validation, direct-main Hermes setup, executed
PR-gate evidence, nightly history, and runner wait reporting.
- [#7447](#7447) is an internal
pinned-runtime prerequisite and is intentionally excluded from canonical
supported-integration documentation.
- [#7370](#7370) adds
maintainer-only advisory reconciliation tooling and does not change
supported user behavior.
- [#7495](#7495) updates existing
documentation and does not add a new v0.0.95 behavior claim.

## 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

- [ ] Tests added or updated for changed behavior
- [x] Existing tests cover changed behavior — justification:
`test/changelog-docs.test.ts` validates the dated changelog structure,
heading uniqueness, and published links.
- [ ] Tests not applicable — justification:
- [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:

## Documentation Writer Review

- [x] Documentation writer subagent reviewed the completed changes
- Result: `docs-updated`
- Evidence: `docs/changelog/2026-07-24.mdx`; writing rules,
documentation style, factual release meaning, and published links
reviewed at exact head `58b02f2bf`.
- Agent: Codex documentation writer reviewer
<!-- docs-review-head-sha: 58b02f2 -->
<!-- docs-review-agents-blob-sha: 9c9b36d -->

## DGX Station Hardware Evidence

- [ ] Tested on DGX Station
- Tested commit:
- Station profile/scenario:
- Result:
- Supporting evidence:

## Verification

- [x] PR description includes a `Signed-off-by:` line 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 test/changelog-docs.test.ts` passed 6 tests.
- [ ] 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) — the
build passed with 0 errors and 2 Fern warnings.
- [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)

---
Signed-off-by: Senthil Ravichandran <senthilr@nvidia.com>


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

* **Documentation**
  * Added a new v0.0.95 changelog entry above v0.0.94.
* Documented improved externally supervised gateway lifecycle ownership.
  * Improved snapshot restore reliability and SQLite state handling.
  * Tightened CLI `backup-all` behavior and host artifact verification.
* Updated Windows onboarding guidance (including Ollama service reuse
and CDI directory fallback).
* Noted inference compatibility fixes, deeper agent failure
classification, stricter base-image validation, updated Discord bot
command permissions, and refined E2E release automation evidence
handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Senthil Ravichandran <senthilr@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output area: install Install, setup, prerequisites, or uninstall flow area: onboarding Onboarding FSM, provider setup, sandbox launch, or first-run flow area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants