Skip to content

fix(backup-all): catch orphan-manifest errors without swallowing real failures - #5819

Merged
cv merged 4 commits into
mainfrom
fix/5734-backup-all-narrow-catch
Jun 26, 2026
Merged

fix(backup-all): catch orphan-manifest errors without swallowing real failures#5819
cv merged 4 commits into
mainfrom
fix/5734-backup-all-narrow-catch

Conversation

@cjagwani

@cjagwani cjagwani commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Catches the loadAgent() orphan-manifest case from #5734 so the pre-upgrade backup loop survives a sandbox whose agent manifest is missing, without silently swallowing real failures (disk full, SSH timeout, permission denied, programming bugs) the way #5740's broad catch did.

Related Issue

Closes #5734. Supersedes #5740.

Why a new PR

#5740 (kagura-agent) implemented the right outcome with a broad try/catch that swallowed every error and counted it as 'skipped', then exited 0. That lets the installer march forward with an upgrade after a real disk-full or SSH-timeout failure, and the restore path later reads a manifest pointing at a corrupt or absent backup. Data loss scenario.

That PR is on a fork I cannot push to, so this is a fresh PR on the NVIDIA upstream so the PR Advisor and full CI run.

Changes

  • src/lib/actions/maintenance.ts: narrow try/catch on backupSandboxState() that only matches the exact orphan-manifest pattern (Agent '...' not found) thrown by loadAgent() at src/lib/agent/defs.ts:367. Anything else re-throws so the installer aborts as it did before any wrapping existed.
  • src/lib/actions/maintenance.test.ts: new test file with 3 cases:
    • Orphan manifest in one sandbox does not abort the batch
    • Orphan manifest is counted as skipped, not failed
    • Non-orphan errors (e.g. EACCES) are re-thrown so the installer aborts the upgrade

Verification

  • npx vitest run src/lib/actions/maintenance.test.ts — 3/3 pass
  • The narrow regex /^Agent '[^']*' not found/ matches the exact error shape from loadAgent at defs.ts:367

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)

Summary by CodeRabbit

  • Bug Fixes

    • Improved batch backup reliability: if a sandbox fails due to a missing/orphan agent manifest (matching the expected “not found: …manifest.yaml” pattern), the batch now logs a warning, marks the sandbox as skipped, updates counters, and continues processing remaining sandboxes.
    • Non-matching or unrecoverable errors still abort the batch as before.
  • Tests

    • Expanded maintenance test coverage for partial success/continuation, skipped behavior, and correct abort behavior for non-matching “not found” cases.

… failures

Closes #5734. Supersedes #5740.

When `loadAgent()` throws for a sandbox whose agent manifest is
missing (orphan from a previous higher-version install), the prior
backup-all loop aborted the entire batch and blocked the installer's
pre-upgrade backup step. #5740 attempted this fix by wrapping
backupSandboxState() in a broad try/catch and counting any throw as
'skipped'. That swallowed real failures (disk full mid tar write,
SSH timeout, permission denied on the backup directory, programming
bugs in the backup logic) and exited 0, so the installer would
march forward with the upgrade and the restore path would later
read a manifest pointing at a corrupt or absent backup.

This commit ships the same outcome with a narrow catch: it only
treats `Agent '...' not found` errors (the exact pattern thrown by
loadAgent in src/lib/agent/defs.ts:367) as recoverable orphans.
Anything else re-throws so the installer aborts as it did before
any wrapping existed.

Also renames the log line to 'Skipped (orphan manifest)' so the
batch summary distinguishes that case from 'Skipping (not running)'
above it.

Adds a regression test that backupAll() rejects on a non-orphan
error (EACCES) instead of counting it as skipped. Keeps the orphan
manifest tests from #5740.

Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
@cjagwani cjagwani added area: cli Command line interface, flags, terminal UX, or output v0.0.68 labels Jun 25, 2026
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: de32ed4b-74c0-4b20-b521-4f1bf3f81a41

📥 Commits

Reviewing files that changed from the base of the PR and between 033462d and 5a17b8d.

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

📝 Walkthrough

Walkthrough

backupAll now skips orphan-agent sandbox backup failures, continues with remaining sandboxes, and rethrows other errors. The tests cover continuation, skipped accounting, fatal propagation, and the missing-suffix orphan case.

Changes

Backup-all orphan manifest handling

Layer / File(s) Summary
Per-sandbox error handling
src/lib/actions/maintenance.ts
backupAll now catches per-sandbox errors, skips orphan-manifest failures, logs the skip, increments skipped counts, and rethrows other errors.
Backup-all behavior tests
src/lib/actions/maintenance.test.ts
A Vitest suite mocks the backup flow and verifies continuation after one sandbox fails, skipped accounting for orphan errors, rejection for non-orphan errors, and the missing-suffix case.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

A bunny hopped through backup rain,
One orphan skipped, then on again.
A warning winked, the batch stayed bright,
Then kept on bouncing through the night.
🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 clearly matches the main change: handling orphan-manifest backup errors without hiding real failures.
Linked Issues check ✅ Passed The code now skips only missing-manifest errors, continues remaining sandboxes, logs skipped counts, and adds regression tests.
Out of Scope Changes check ✅ Passed The diff is narrowly focused on backup-all error handling and regression tests, with no unrelated changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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/5734-backup-all-narrow-catch

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

@github-code-quality

github-code-quality Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the fix/5734-backup-all-... branch is 96%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main fix/5734-backup-all-... 5a17b8d +/-
nemoclaw/src/se...cret-scanner.ts 100%
nemoclaw/src/commands/slash.ts 100%
nemoclaw/src/li...bprocess-env.ts 100%
nemoclaw/src/bl...eprint/state.ts 98%
nemoclaw/src/onboard/config.ts 98%
nemoclaw/src/bl...int/snapshot.ts 97%
nemoclaw/src/bl...print/runner.ts 95%
nemoclaw/src/co...ration-state.ts 94%
nemoclaw/src/bl...ate-networks.ts 94%
nemoclaw/src/index.ts 94%

TypeScript / code-coverage/cli

The overall coverage in the fix/5734-backup-all-... branch is 47%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main fix/5734-backup-all-... 5a17b8d +/-
src/lib/state/o...oard-session.ts 91%
src/lib/inference/local.ts 77%
src/lib/sandbox/config.ts 72%
src/lib/actions...dbox/rebuild.ts 71%
src/lib/onboard/preflight.ts 64%
src/lib/actions...licy-channel.ts 60%
src/lib/state/sandbox.ts 56%
src/lib/policy/index.ts 49%
src/lib/onboard...er-gpu-patch.ts 44%
src/lib/onboard.ts 19%

Updated June 25, 2026 20:56 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@cjagwani cjagwani self-assigned this Jun 25, 2026
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: state-backup-restore-e2e, openshell-gateway-upgrade-e2e
Optional E2E: upgrade-stale-sandbox-e2e, rebuild-openclaw-e2e

Dispatch hint: state-backup-restore-e2e,openshell-gateway-upgrade-e2e

Auto-dispatched E2E: openshell-gateway-upgrade-e2e via nightly-e2e.yaml at 5a17b8df108d84ebd1943a408b184f580e3a81benightly run

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • state-backup-restore-e2e (medium): Direct E2E coverage for sandbox state backup/restore is required because the changed code alters backupAll() behavior and error classification for backup failures.
  • openshell-gateway-upgrade-e2e (high): This is the closest existing full upgrade-path E2E: it validates a real install/upgrade with pre-upgrade backup and restored agent state. The PR specifically changes pre-upgrade backup-all behavior.

Optional E2E

  • upgrade-stale-sandbox-e2e (high): Adjacent confidence for stale registered sandbox upgrade/rebuild handling, which is related to registry/agent-version drift scenarios but does not directly exercise the new orphan-manifest backupAll() branch.
  • rebuild-openclaw-e2e (high): Useful adjacent coverage for OpenClaw rebuild state preservation after lifecycle changes, though the direct risk is better covered by state-backup-restore-e2e and openshell-gateway-upgrade-e2e.

New E2E recommendations

  • installer/upgrade backup resilience (high): No existing E2E appears to explicitly create a registered running sandbox whose agent manifest is missing, then verify nemoclaw backup-all or the pre-upgrade install path skips only that orphan-manifest case while continuing other backups and preserving non-orphan failures as fatal.
    • Suggested test: Add an orphan-agent-manifest backup-all upgrade E2E that mutates the registry/agents tree to reproduce the missing manifest condition and asserts skipped/backed-up/failed summary semantics.

Dispatch hint

  • Workflow: .github/workflows/nightly-e2e.yaml
  • jobs input: state-backup-restore-e2e,openshell-gateway-upgrade-e2e

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Vitest E2E Scenario Recommendation

Required Vitest E2E scenarios: openshell-gateway-upgrade-vitest
Optional Vitest E2E scenarios: None

Dispatch required Vitest E2E scenarios:

  • gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=openshell-gateway-upgrade-vitest

Workflow run

Full Vitest E2E advisor summary

Vitest E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required Vitest E2E scenarios

  • openshell-gateway-upgrade-vitest: The PR changes backupAll(), which is used by the installer pre-upgrade backup path. The OpenShell gateway upgrade live Vitest job runs the real current installer upgrade flow and asserts the pre-upgrade backup contract, making it the smallest wired Vitest E2E coverage for this changed maintenance action surface.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=openshell-gateway-upgrade-vitest

Optional Vitest E2E scenarios

  • None.

Relevant changed files

  • src/lib/actions/maintenance.ts

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings

Merge posture: No blocking advisor findings
Primary next action: Add or justify PRA-T1 and any related test follow-ups.
Open items: 0 required · 0 warnings · 0 suggestions · 3 test follow-ups
Since last review: 1 prior item resolved · 0 still apply · 0 new items found

Action checklist

  • 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: Acceptance clause
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 — Add or identify a targeted integration-style test: `backupAll skips the real loadAgent missing manifest.yaml error from backupSandboxState and continues to a second ready sandbox`, while still mocking external OpenShell/Docker/process boundaries.. The changed behavior sits in a runtime/sandbox installer pre-upgrade backup path. The added unit tests are focused and cover the branch logic, but they mock `backupSandboxState()` rather than exercising the real `loadAgent()` missing-manifest boundary that justifies the string classifier.
  • PRA-T2 Runtime validation — Optionally assert `backupAll does not call process.exit when every thrown sandbox is an orphan-manifest skip and no backups fail` to make the installer exit-0 expectation explicit.. The changed behavior sits in a runtime/sandbox installer pre-upgrade backup path. The added unit tests are focused and cover the branch logic, but they mock `backupSandboxState()` rather than exercising the real `loadAgent()` missing-manifest boundary that justifies the string classifier.
  • PRA-T3 Acceptance clause — The CLI `nemoclaw <name> destroy` on the offending sandbox also throws (it also calls `loadAgent`), so the documented recovery path is itself broken. — add test evidence or identify existing coverage. This PR does not change the destroy path. The linked issue presents this as recovery-context evidence; the Expected Result and minimum acceptance criteria are specifically about pre-upgrade `backup-all` continuing so the installer can proceed.

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: 1

🤖 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/maintenance.ts`:
- Around line 89-90: The orphan-manifest matcher in maintenance handling is too
broad and catches more than the actual loadAgent() orphan case. Narrow the regex
in maintenance.ts to match the exact loadAgent() error contract from loadAgent()
in src/lib/agent/defs.ts, including the manifest path portion after Agent
'<name>' not found:. Keep non-matching errors flowing to the throw path so
unrelated failures still abort the backup instead of being skipped.
🪄 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: 6e182319-41f0-4bdf-bb4b-990af2b83f5a

📥 Commits

Reviewing files that changed from the base of the PR and between 133a4f2 and acc4837.

📒 Files selected for processing (2)
  • src/lib/actions/maintenance.test.ts
  • src/lib/actions/maintenance.ts

Comment thread src/lib/actions/maintenance.ts Outdated
cjagwani added 3 commits June 25, 2026 13:37
Advisor PRA-2 + CodeRabbit on #5819 both pointed out the orphan-manifest
regex `^Agent '[^']*' not found` is looser than the actual loadAgent()
throw shape at src/lib/agent/defs.ts:365-372 (`Agent '<name>' not found:
<manifestPath>`). A bare `Agent '...' not found` could plausibly surface
from a different layer (registry lookup, manifest index, future code)
and should still abort the backup batch instead of being silently
skipped.

Tighten the regex to require the `: <path>` suffix that loadAgent
emits, document the source-line anchor in a code comment, and update
the second orphan-skip test fixture so it reflects the real shape.

Add a regression test that a bare `Agent '<name>' not found` message
without the suffix is re-thrown, so a future widening of the matcher
fails CI.

Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
…PRA-1)

Advisor PRA-1 on #5819 asked for the five source-of-truth dimensions
to be documented for the localized orphan-manifest catch: invalid
state, source boundary, source-fix constraint, regression test, and
removal condition.

Expand the inline comment to name each dimension explicitly. No code
behavior change — pure documentation pass so the catch's source
boundary is auditable at the site instead of in a separate doc.

Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
…aml (PRA-1)

Advisor PRA-1 on #5819 (033462d) asked the matcher to specifically
identify the manifest.yaml path so a future error that wraps
`Agent '...' not found:` with a different artifact (binary, config,
registry entry) keeps aborting the batch instead of being treated as
an orphan manifest skip.

loadAgent at src/lib/agent/defs.ts:367 emits the path via
`path.join(AGENTS_DIR, name, "manifest.yaml")`, so the path always
ends in `/manifest.yaml`. Tighten the regex to that exact suffix and
add a regression test that a non-manifest-yaml path is re-thrown.

Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 28199869787
Target ref: 5a17b8df108d84ebd1943a408b184f580e3a81be
Workflow ref: main
Requested jobs: openshell-gateway-upgrade-e2e
Summary: 1 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
openshell-gateway-upgrade-e2e ✅ success

cjagwani added a commit that referenced this pull request Jun 25, 2026
Address advisor PRA-2 PRA-3 PRA-5 on #5455.

PRA-2 (required): wipeSandboxState() lived inside the destroy.ts
monolith. Move it to a sibling module src/lib/actions/sandbox/wipe-state.ts
so the wipe contract has its own surface, the destroy hotspot stays
focused on lifecycle orchestration, and future evolution of the wipe
logic (e.g. typed manifest schema) doesn't drag the destroy file
around. Re-export wipeSandboxState and WipeSandboxStateDeps from
destroy.ts so callers (tests, downstream code) keep working.

PRA-3 (warning): the path-escape boundary check used Node's host-OS
`path.resolve()` + `path.sep`. The remote sandbox shell that
executes the rm-rf script is Linux, but the CLI can run on Windows
or macOS, so the boundary semantics could diverge from what the
sandbox actually does (e.g. `\` separators or different normalization
on Windows). Switch to `path.posix` so the boundary check matches the
sandbox shell that will run the script.

PRA-5 (warning): expand the wipeSandboxState docstring to name the
five source-of-truth dimensions explicitly: invalid state, source
boundary, source-fix constraint, regression test, and removal
condition. Same pattern that converged the advisor on #5819.

No behavior change for the common in-sandbox path (configPaths.dir is
already a POSIX absolute path); the POSIX switch hardens the check
against future Windows-host callers.

Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
@cv cv added v0.0.69 and removed v0.0.68 labels Jun 25, 2026
@wscurran wscurran added the NV QA Bugs found by the NVIDIA QA Team label Jun 26, 2026
@cv
cv merged commit ababaad into main Jun 26, 2026
44 checks passed
@cv
cv deleted the fix/5734-backup-all-narrow-catch branch June 26, 2026 00:39
cv added a commit that referenced this pull request Jun 27, 2026
…5455)

## Summary
`nemoclaw <name> destroy` deleted the sandbox pod but left its
per-sandbox persistent volume intact, so re-onboarding with the same
name resurrected old workspace files (`USER.md`, `SOUL.md`, ...). This
makes destroy actually wipe that persistent state, restoring the
documented "clean workspace on re-onboard" contract.

## Related Issue
Fixes #5449

## Changes
- Add `wipeSandboxState()` in `src/lib/actions/sandbox/destroy.ts`:
while the sandbox is still live (before `openshell sandbox delete`), it
removes the agent-manifest state dirs/files plus discovered multi-agent
`workspace-*` dirs via `openshell sandbox exec -- sh -c 'rm -rf ...'`.
This is the inverse of `backupSandboxState`, so it targets exactly the
set snapshot/backup treat as durable state.
- Call `wipeSandboxState()` from `destroySandbox()` after the
confirmation gate and before the delete.
- Best-effort and non-fatal: a non-live sandbox (e.g. gateway down)
warns and lets destroy proceed, mirroring the existing
`removeShieldsState` pattern from #3114.
- Add `test/destroy-wipe-sandbox-state.test.ts` reproducing the issue:
asserts the wipe targets the `workspace/` dir under the agent config
dir, includes `workspace-*` (#1260), passes `ignoreError`, and never
throws on a failed exec.

### Root cause
`openshell sandbox delete` tears down the pod but the workspace lives in
a k3s local-path PVC keyed by sandbox name (inside the shared
`openshell-cluster-nemoclaw` Docker volume), which `delete` leaves
intact. `openshell sandbox delete --help` exposes no storage-wipe flag,
and the cluster volume is only removed on opt-in gateway teardown
(#2166). Re-onboarding with the same name rebinds the PVC. Same bug
class as #3114.

## Type of Change
- [x] 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)

## Verification
- [ ] `npx prek run --all-files` passes
- [ ] `npm test` passes
- [x] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [ ] Docs updated for user-facing behavior changes
- [ ] `make docs` builds without warnings (doc changes only)
- [ ] 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)

<!-- Verification notes: this branch builds (`npm run build:cli`) and
type-checks (`npm run typecheck:cli`) cleanly; `biome check` is clean on
both changed files; and the destroy suites
(`test/destroy-wipe-sandbox-state.test.ts`,
`test/destroy-cleanup-sandbox-services.test.ts`,
`src/lib/domain/sandbox/destroy.test.ts`) pass deterministically. The
full `npm test` and `npx prek run --all-files` were not run because the
local working tree carries heavy unrelated modifications and the full
suite is flaky in this environment; behavior was verified via the
targeted suites above. No docs change is needed — the fix makes code
match the existing contract in docs/manage-sandboxes/backup-restore.md.
-->

## Advisor state

1 required finding that contradicts the advisor's own original required
PRA-5 from this PR. PRA-5 required the wipe run AFTER
gateway-select-before-delete (the bug we fixed). A later round's PRA-2
asks the wipe defer until after delete proves destroy can complete —
physically impossible because `sandbox delete` unmounts the PVC and the
in-sandbox `rm -rf` can no longer reach it. The code keeps PRA-5's
ordering; the contradiction is named at
`src/lib/actions/sandbox/destroy.ts:386-389`. Plus 3 recurring advisory
warnings (source-of-truth recursion pattern — same plateau as #5712 and
#5819). Justifications in `wipeSandboxState()` docstring.

---
Signed-off-by: jason-ma-nv <jama@nvidia.com>

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

## Release Notes

* **Bug Fixes**
* Improved sandbox destruction to best-effort wipe persisted agent state
before deletion, including the agent manifest state plus `workspace` and
`workspace-*` multi-agent data.
* Added safer cleanup execution: failures are non-blocking, and warnings
are logged (e.g., “Could not wipe workspace state”).
* Strengthened security checks to prevent path escaping when generating
the cleanup command.

* **Tests**
* Expanded regression coverage for correct exec invocation, warning
behavior on failures, and secure `rm -rf` script generation (including
`cd` boundary and traversal/absolute-path protections).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Jason Ma <jama@nvidia.com>
Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
Co-authored-by: Test User <test@example.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: cjagwani <cjagwani@nvidia.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
… failures (NVIDIA#5819)

## Summary

Catches the `loadAgent()` orphan-manifest case from NVIDIA#5734 so the
pre-upgrade backup loop survives a sandbox whose agent manifest is
missing, **without** silently swallowing real failures (disk full, SSH
timeout, permission denied, programming bugs) the way NVIDIA#5740's broad
catch did.

## Related Issue

Closes NVIDIA#5734. **Supersedes NVIDIA#5740**.

## Why a new PR

NVIDIA#5740 (kagura-agent) implemented the right outcome with a broad
try/catch that swallowed every error and counted it as 'skipped', then
exited 0. That lets the installer march forward with an upgrade after a
real disk-full or SSH-timeout failure, and the restore path later reads
a manifest pointing at a corrupt or absent backup. Data loss scenario.

That PR is on a fork I cannot push to, so this is a fresh PR on the
NVIDIA upstream so the PR Advisor and full CI run.

## Changes

- `src/lib/actions/maintenance.ts`: narrow try/catch on
`backupSandboxState()` that only matches the exact orphan-manifest
pattern (`Agent '...' not found`) thrown by `loadAgent()` at
`src/lib/agent/defs.ts:367`. Anything else re-throws so the installer
aborts as it did before any wrapping existed.
- `src/lib/actions/maintenance.test.ts`: new test file with 3 cases:
  - Orphan manifest in one sandbox does not abort the batch
  - Orphan manifest is counted as `skipped`, not `failed`
- **Non-orphan errors (e.g. EACCES) are re-thrown so the installer
aborts the upgrade**

## Verification

- `npx vitest run src/lib/actions/maintenance.test.ts` — 3/3 pass
- The narrow regex `/^Agent '[^']*' not found/` matches the exact error
shape from `loadAgent` at `defs.ts:367`

## Type of Change

- [x] 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)

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

* **Bug Fixes**
* Improved batch backup reliability: if a sandbox fails due to a
missing/orphan agent manifest (matching the expected “not found:
<path>…manifest.yaml” pattern), the batch now logs a warning, marks the
sandbox as skipped, updates counters, and continues processing remaining
sandboxes.
* Non-matching or unrecoverable errors still abort the batch as before.

* **Tests**
* Expanded maintenance test coverage for partial success/continuation,
skipped behavior, and correct abort behavior for non-matching “not
found” cases.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
…5449) (NVIDIA#5455)

## Summary
`nemoclaw <name> destroy` deleted the sandbox pod but left its
per-sandbox persistent volume intact, so re-onboarding with the same
name resurrected old workspace files (`USER.md`, `SOUL.md`, ...). This
makes destroy actually wipe that persistent state, restoring the
documented "clean workspace on re-onboard" contract.

## Related Issue
Fixes NVIDIA#5449

## Changes
- Add `wipeSandboxState()` in `src/lib/actions/sandbox/destroy.ts`:
while the sandbox is still live (before `openshell sandbox delete`), it
removes the agent-manifest state dirs/files plus discovered multi-agent
`workspace-*` dirs via `openshell sandbox exec -- sh -c 'rm -rf ...'`.
This is the inverse of `backupSandboxState`, so it targets exactly the
set snapshot/backup treat as durable state.
- Call `wipeSandboxState()` from `destroySandbox()` after the
confirmation gate and before the delete.
- Best-effort and non-fatal: a non-live sandbox (e.g. gateway down)
warns and lets destroy proceed, mirroring the existing
`removeShieldsState` pattern from NVIDIA#3114.
- Add `test/destroy-wipe-sandbox-state.test.ts` reproducing the issue:
asserts the wipe targets the `workspace/` dir under the agent config
dir, includes `workspace-*` (NVIDIA#1260), passes `ignoreError`, and never
throws on a failed exec.

### Root cause
`openshell sandbox delete` tears down the pod but the workspace lives in
a k3s local-path PVC keyed by sandbox name (inside the shared
`openshell-cluster-nemoclaw` Docker volume), which `delete` leaves
intact. `openshell sandbox delete --help` exposes no storage-wipe flag,
and the cluster volume is only removed on opt-in gateway teardown
(NVIDIA#2166). Re-onboarding with the same name rebinds the PVC. Same bug
class as NVIDIA#3114.

## Type of Change
- [x] 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)

## Verification
- [ ] `npx prek run --all-files` passes
- [ ] `npm test` passes
- [x] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [ ] Docs updated for user-facing behavior changes
- [ ] `make docs` builds without warnings (doc changes only)
- [ ] 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)

<!-- Verification notes: this branch builds (`npm run build:cli`) and
type-checks (`npm run typecheck:cli`) cleanly; `biome check` is clean on
both changed files; and the destroy suites
(`test/destroy-wipe-sandbox-state.test.ts`,
`test/destroy-cleanup-sandbox-services.test.ts`,
`src/lib/domain/sandbox/destroy.test.ts`) pass deterministically. The
full `npm test` and `npx prek run --all-files` were not run because the
local working tree carries heavy unrelated modifications and the full
suite is flaky in this environment; behavior was verified via the
targeted suites above. No docs change is needed — the fix makes code
match the existing contract in docs/manage-sandboxes/backup-restore.md.
-->

## Advisor state

1 required finding that contradicts the advisor's own original required
PRA-5 from this PR. PRA-5 required the wipe run AFTER
gateway-select-before-delete (the bug we fixed). A later round's PRA-2
asks the wipe defer until after delete proves destroy can complete —
physically impossible because `sandbox delete` unmounts the PVC and the
in-sandbox `rm -rf` can no longer reach it. The code keeps PRA-5's
ordering; the contradiction is named at
`src/lib/actions/sandbox/destroy.ts:386-389`. Plus 3 recurring advisory
warnings (source-of-truth recursion pattern — same plateau as NVIDIA#5712 and
NVIDIA#5819). Justifications in `wipeSandboxState()` docstring.

---
Signed-off-by: jason-ma-nv <jama@nvidia.com>

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

## Release Notes

* **Bug Fixes**
* Improved sandbox destruction to best-effort wipe persisted agent state
before deletion, including the agent manifest state plus `workspace` and
`workspace-*` multi-agent data.
* Added safer cleanup execution: failures are non-blocking, and warnings
are logged (e.g., “Could not wipe workspace state”).
* Strengthened security checks to prevent path escaping when generating
the cleanup command.

* **Tests**
* Expanded regression coverage for correct exec invocation, warning
behavior on failures, and secure `rm -rf` script generation (including
`cd` boundary and traversal/absolute-path protections).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Jason Ma <jama@nvidia.com>
Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
Co-authored-by: Test User <test@example.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: cjagwani <cjagwani@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 NV QA Bugs found by the NVIDIA QA Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Ubuntu][Upgrade] pre-upgrade backup-all throws on first sandbox with an unknown agent manifest and aborts the install

3 participants