Skip to content

fix(stop): tear down the dashboard port-forward on stop (#7227)#7228

Merged
prekshivyas merged 58 commits into
mainfrom
fix/7227-stop-teardown-dashboard-forward
Jul 24, 2026
Merged

fix(stop): tear down the dashboard port-forward on stop (#7227)#7228
prekshivyas merged 58 commits into
mainfrom
fix/7227-stop-teardown-dashboard-forward

Conversation

@yanyunl1991

@yanyunl1991 yanyunl1991 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

nemoclaw <sandbox> stop returned success but never tore down the host-side dashboard SSH port-forward it created, leaving an ssh -L 127.0.0.1:<port> listener alive. status then misreported the cleanly-stopped sandbox as a foreign sandbox_dashboard_port_conflict, and start/recover intermittently failed while contending for the still-held port. This PR releases the forward on stop. Exact-head live double-onboard also exposed the known recreated-sandbox readiness defect from #7273, so this branch includes the reviewed tri-state readiness fix from #7291 and keeps the restart regression enabled.

Closes #7227.
Closes #7273.

Reproduction

On our Ubuntu 24.04 x86_64 test host (no GPU), ollama-local sandbox. The reporter is on DGX Spark aarch64; the root cause (host-side port-forward not torn down on stop) is platform-independent.

  1. Onboard an OpenClaw sandbox on ollama-local; confirm Phase: Ready.
  2. openshell forward list / ss -ltnp | grep <dashboard-port> — a nemoclaw-spawned ssh -L forward holds the port.
  3. nemoclaw <sb> stop (exit 0; container Exited 137).
  4. Re-check the port and nemoclaw <sb> status.

Observed on main (before fix)

# after stop, the forward is still alive:
$ ss -ltnp | grep 18789
LISTEN 127.0.0.1:18789 ... users:(("ssh",pid=<same as before stop>,fd=3))
$ openshell forward list
<sb> 127.0.0.1 18789 <pid> running
$ nemoclaw <sb> status            # EXIT 1
Failure layer: sandbox_dashboard_port_conflict — sandbox container is stopped and the dashboard port is held by a foreign listener.

Observed on fix/... (after fix)

# after stop, no listener remains:
$ ss -ltnp | grep 18789            # 0 listeners
$ openshell forward list           # 0 entries for <sb>
$ nemoclaw <sb> status
Failure layer: sandbox_container_stopped — sandbox container exists but is not running.
# repeated start/stop cycles (x3): start always reaches Ready; stop always leaves 0 listeners.

Analysis

stopSandbox (src/lib/actions/sandbox/stop.ts) stops the labeled container(s) and returns, but it never releases the host-side dashboard port-forward. That forward is a nemoclaw-managed openshell forward (ssh -L 127.0.0.1:<port>); onboarding, destroy, and forward-recovery all tear it down with openshell forward stop <port> <sandbox>, but stop did not — so the listener outlives the container. getSandboxGatewayStateForStatus's classifier (gateway-failure-classifier.ts, sandbox_dashboard_port_conflict) then sees a stopped container plus a live listener on its dashboard port and reports a "foreign listener" conflict — the listener is nemoclaw's own leftover forward. start/recover re-establish the forward and probe gateway readiness while the just-stopped forward is still bound, which is the intermittent failure.

The exact-head E2E gate also exposed a finalization ordering gap: cloud-onboard created the sandbox and launched OpenClaw, but post-policy process recovery invalidated the initial dashboard forward before final deployment verification. Finalization re-established forwards only for non-default agents and did so before recovery, so the delivered OpenClaw dashboard on 127.0.0.1:18789 returned connection refused. Existing unit review did not model recovery invalidating the forward; the new regression makes that state transition explicit.

Fix

After the container is stopped, release the forward via a new teardownSandboxDashboardForward helper (forward-recovery.ts) that runs openshell forward stop <port> <sandbox> — reusing the exact teardown already used by onboard/destroy/forward-recovery, with the port from resolveSandboxDashboardPort. The helper scopes cleanup to the registry-owned gateway, applies the standard 30-second OpenShell operation timeout, and waits up to 5 seconds for the supported OpenShell 0.0.85 listener to release the host port. It is best-effort (a stop must still free container resources when OpenShell is unreachable) and is wired into stopSandbox after a successful Docker stop and for an already-stopped sandbox. It does not run when Docker stop fails because the container is still running and needs its forward.

Tests (stop.test.ts) pin the contract: the forward is torn down only after the container stops; it is not released when Docker stop fails; and a repeated stop releases a leftover forward for an already-at-rest sandbox. They also verify non-default gateway targeting, the operation timeout, delayed listener-release polling, and that a nonzero cleanup status skips the port probe.

Finalization now reconciles the default OpenClaw or declared-agent forward after post-policy process recovery and before warm-up or deployment verification. The regression simulates recovery invalidating an initially-live forward and requires finalization to restore it before reporting the deployment healthy.

Changes

  • src/lib/actions/sandbox/process-recovery.ts and focused tests: preserve the reviewed fix: preserve sandbox recovery readiness guard #7291 tri-state readiness contract and retry only the exact OpenShell supervisor re-registration states, including relay-target ENOENT/ECONNREFUSED, and no-op readiness-probe timeout exposed by exact-head double-onboard.
  • test/e2e/live/double-onboard.test.ts: retain the stop/start regression so live validation proves the recovery dependency instead of bypassing it.
  • src/lib/actions/sandbox/forward-recovery.ts: new teardownSandboxDashboardForward helper.
  • src/lib/actions/sandbox/stop.ts: release the dashboard forward after a successful stop.
  • src/lib/actions/sandbox/stop.test.ts: pin the teardown contract and its boundaries.
  • src/lib/onboard.ts and finalization tests: restore dashboard forwarding after post-policy process recovery, including the default OpenClaw path.
  • src/lib/onboard/policy-selection.ts and focused tests: after applying policy presets, require both sandbox phase readiness and a real OpenShell command probe before finalization.
  • Timeout documentation: map post-policy OpenShell re-registration to NEMOCLAW_SANDBOX_READY_TIMEOUT and distinguish it from post-create cleanup.
  • docs/manage-sandboxes/run-sandboxes.mdx: document dashboard-forward cleanup during stop.
  • docs/reference/commands.mdx: document cleanup ordering, failure handling, and idempotency.

Type of Change

  • Code change (feature, bug fix, or refactor)

Verification

  • Focused recovery tests pass: 37/37.
  • Focused finalization tests pass: 16/16.
  • Exact affected Vitest suite against current main passes: 107 files / 1,060 tests; latest changed-path lane passes: 61 files / 582 tests.
  • CLI type-check, CLI build, Biome, semantic E2E plans, git diff --check, normal pre-commit/commit-msg hooks, pre-push hooks, and gitleaks pass.
  • npm run docs passes with 0 errors and 2 existing warnings; agent variants are synchronized and valid.
  • Replacement exact-head live E2E is pending for the OpenShell relay-readiness and final-forward ordering fixes.
  • No secrets, API keys, or credentials committed

Documentation Writer Review

  • Documentation writer subagent reviewed the completed changes
  • Result: no-docs-needed
  • Evidence: PASS on exact head ba010ecc6428637282320f2a4de0580e36a06070 against base d87acc16b04c065ddc26c294a6c3af6795a43d7b. The reviewer confirmed the latest change only expands the existing bounded recreated-sandbox readiness retry for OpenShell 0.0.85 relay-target startup; it adds no command, output, configuration, deadline, workflow, or operator action. Existing recovery docs already cover bounded OpenShell re-registration and fail-closed behavior. Focused recovery tests pass 37/37; npm run test:changed passes 582/582; CLI build/typecheck, Biome, and normal hooks pass.
  • Agent: Codex Desktop

AI Disclosure

  • AI-assisted — tool: Claude Code

Signed-off-by: Yanyun Liao yanyunl@nvidia.com

Summary by CodeRabbit

  • Bug Fixes
    • Improved sandbox stop to best-effort release the host dashboard port-forward, including idempotent cleanup and safer degradation when OpenShell isn’t available.
    • Strengthened recovery readiness handling with bounded, per-stage timeouts, clearer retry vs fail-closed outcomes, and better gating before starting port-forwards.
    • Updated onboarding finalization to restore the correct dashboard forward after recovery and to gate preset synchronization on control-plane readiness.
  • Tests
    • Expanded unit, supervisor relaunch, onboarding, and e2e coverage for forward release, readiness retries, and edge-case recovery flows.
  • Documentation
    • Refreshed stop/recovery and timeout guidance to match the preserved assets and the updated dashboard forward behavior.

`nemoclaw <sandbox> stop` stopped the container but never released the
host-side dashboard SSH port-forward it had created, so the `ssh -L
127.0.0.1:<port>` listener stayed alive after stop. As a result `status`
misclassified the cleanly-stopped sandbox via `sandbox_dashboard_port_conflict`
("the dashboard port is held by a foreign listener" — actually nemoclaw's own
leftover forward), and `start` / `recover` intermittently failed their
gateway-readiness probe while contending for the still-held port.

After the container is stopped, release the forward via the shared
`openshell forward stop <port> <sandbox>` teardown (a new
`teardownSandboxDashboardForward` helper that mirrors the forward-stop already
used by the onboard, destroy, and forward-recovery paths). Best-effort: the
teardown runs only after the container actually stopped, and never when the
docker stop failed (the container is still running, so its forward must stay).

Verified end-to-end: after stop no listener remains on the dashboard port,
`status` reports `sandbox_container_stopped` instead of the phantom conflict,
and repeated start/stop cycles restart to Ready deterministically.

Fixes #7227

Signed-off-by: Yanyun Liao <yanyunl@nvidia.com>
@coderabbitai

coderabbitai Bot commented Jul 20, 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

Sandbox lifecycle handling now releases host dashboard forwards during stop, distinguishes retryable and definitive recreated-sandbox readiness outcomes, restores forwarding after recovery before verification, and gates policy synchronization on control-plane readiness. Tests and timeout documentation reflect these flows.

Changes

Sandbox lifecycle forwarding

Layer / File(s) Summary
Dashboard forward teardown
src/lib/actions/sandbox/forward-recovery.ts, src/lib/actions/sandbox/stop.ts, src/lib/actions/sandbox/stop.test.ts
Adds best-effort OpenShell forward teardown with bounded release confirmation and integrates it into successful and idempotent stops.
Recreated sandbox readiness classification
src/lib/actions/sandbox/process-recovery.ts, src/lib/actions/sandbox/process-recovery.test.ts, test/process-recovery-supervisor-relaunch.test.ts
Adds structured readiness outcomes, bounded retries for transient supervisor states, managed-health failure handling, and forward-start gating.
Onboarding forward reconciliation
src/lib/onboard.ts, src/lib/onboard/machine/handlers/finalization.ts, src/lib/onboard/machine/*final-flow-phases*.test.ts
Defers dashboard-forward setup until recovery completes, restores it before verification, and updates phase-order assertions.
Policy application readiness
src/lib/onboard/policy-selection.ts, src/lib/onboard/finalization-deps.ts, src/lib/onboard/policy-selection-recorded-tier.test.ts, test/*onboard*.test.ts
Adds pre- and post-policy readiness checks, including OpenShell re-registration after synchronization.
Lifecycle documentation and validation
docs/manage-sandboxes/*.mdx, docs/reference/*.mdx, docs/inference/configure-inference-timeouts.mdx, test/e2e/live/double-onboard.test.ts
Documents stop behavior, readiness budgets, forward cleanup, and selected-sandbox forward restoration.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant stopSandbox
  participant teardownSandboxDashboardForward
  participant OpenShell
  CLI->>stopSandbox: stop sandbox
  stopSandbox->>teardownSandboxDashboardForward: teardown dashboard forward
  teardownSandboxDashboardForward->>OpenShell: stop forward port sandbox gateway
  OpenShell-->>teardownSandboxDashboardForward: cleanup result
  stopSandbox-->>CLI: stop result
Loading

Possibly related issues

  • Issue 7418 — Covers the sandbox process-recovery readiness and managed supervisor probing updated here.
  • Issue 7404 — Covers related dashboard-forward restoration during post-reboot recovery.

Suggested labels: integration: openclaw

Suggested reviewers: cjagwani, ericksoa

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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
Linked Issues check ✅ Passed The changes address #7227 and #7273 by cleaning up the dashboard forward, preserving fail-closed recovery behavior, and adding the requested regressions.
Out of Scope Changes check ✅ Passed No clear out-of-scope code changes stand out; the onboarding, recovery, docs, and tests all support the stop/recovery readiness work.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main stop-related change: tearing down the dashboard port-forward on sandbox stop.
✨ 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/7227-stop-teardown-dashboard-forward

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

@github-code-quality

github-code-quality Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit ba010ec in the fix/7227-stop-teardo... branch remains at 96%, unchanged from commit 99776d3 in the main branch.


Updated July 24, 2026 13:34 UTC

@github-actions

github-actions Bot commented Jul 20, 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: cloud-onboard, credential-sanitization, security-posture, double-onboard, onboard-repair, onboard-resume

1 optional E2E recommendation
  • gateway-guard-recovery

Workflow run details

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

…ed) stop

Address PR review advisor PRA-1: the all-at-rest branch returned exit 0 before
the forward teardown ran, so a repeated stop (or a stop after the forward was
orphaned while openshell was unreachable) left the stale dashboard listener
alive — the exact conflict this change removes. Invoke the best-effort teardown
in the already-stopped success branch too, so a repeated stop always converges
on no leftover listener. The no-cleanup behavior when a docker stop fails is
unchanged (the container is still running, so its forward must stay).

Fixes #7227

Signed-off-by: Yanyun Liao <yanyunl@nvidia.com>
@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression labels Jul 20, 2026
@yanyunl1991 yanyunl1991 added the v0.0.91 Release target label Jul 21, 2026
Signed-off-by: ScarabSystems <scarab.systems@yahoo.com>
Signed-off-by: ScarabSystems <scarab.systems@yahoo.com>
Signed-off-by: ScarabSystems <scarab.systems@yahoo.com>
@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>
@prekshivyas prekshivyas self-assigned this Jul 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

@prekshivyas prekshivyas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Exact-head code/security review — PASS for 8c6f125 against ccf3dd4.

The previous phase-only post-policy wait was not sufficient because OpenShell can report a cached Ready phase before its supervisor session accepts commands. This head preserves the phase gate and additionally requires a bounded openshell sandbox exec --name <sandbox> -- true probe after policy mutation and before finalization. The probe reuses the existing exact retry classifier and NEMOCLAW_SANDBOX_READY_TIMEOUT; unexpected failures remain definitive. Focused tests pin phase-ready → policy mutation → phase-ready → control-plane-ready ordering.

  1. Secrets and credentials: PASS — no secret source, value, persistence, or logging change.
  2. Input validation and sanitization: PASS — the validated sandbox name is passed as an argv element; no shell interpolation or new parser is introduced.
  3. Authentication and authorization: PASS — OpenShell remains the control-plane authority and no identity, approval, or permission check is bypassed.
  4. Dependencies and supply chain: PASS — no dependency, image, action, or lockfile change.
  5. Error handling and logging: PASS — the post-policy probe is bounded by the documented readiness budget and fails closed with a distinct re-registration diagnostic.
  6. Cryptography and data protection: PASS — no cryptographic or protected-data behavior changes.
  7. Configuration and network exposure: PASS — no new listener, forward, destination, policy permission, or configuration surface is introduced; the existing timeout gains a documented call site.
  8. Security testing: PASS — focused policy suites passed 71 tests; npm run test:changed passed 99 files / 972 tests; CLI typecheck, Biome, hooks, gitleaks, docs, variants, and git diff --check passed.
  9. System security: PASS — finalization cannot proceed until both phase readiness and real OpenShell command execution are restored after policy application; existing managed-health and forward guards remain intact.

Product scope remains established by accepted issues #7227 and #7273. Documentation Writer review passed at this exact head with AGENTS blob 9c9b36d7f99ec7dd8f40af135f5a9e55354330d7. No code, security, or documentation finding remains; exact-head automated and credentialed E2E gates are still required before approval.

@prekshivyas

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

@prekshivyas prekshivyas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Exact-head code/security review — PASS for 51651d0 against ccf3dd4.

This exact-head delta is test-only and addresses PR Review Advisor warning PRA-1. It adds fail-closed coverage proving that a false post-policy OpenShell control-plane probe occurs after policy synchronization, invokes the existing fatal path with exit code 1, and does not return a successful selection. The runtime and documentation reviewed at 8c6f125 are unchanged.

  1. Secrets and credentials: PASS — no credential behavior or fixture data added.
  2. Input validation and sanitization: PASS — no runtime input path changed.
  3. Authentication and authorization: PASS — no runtime permission path changed.
  4. Dependencies and supply chain: PASS — no dependency, image, action, or lockfile change.
  5. Error handling and logging: PASS — the negative test now pins the distinct fatal re-registration path and exit code.
  6. Cryptography and data protection: PASS — no runtime or protected-data change.
  7. Configuration and network exposure: PASS — no runtime configuration, listener, forward, or policy change.
  8. Security testing: PASS — exact-head focused tests and npm run test:changed pass; CLI typecheck, Biome, hooks, gitleaks, and pre-push pass. Runtime-head affected coverage passed 99 files / 972 tests.
  9. System security: PASS — the test prevents regression from fail-closed post-policy command readiness to fail-open behavior.

Product scope remains established by accepted issues #7227 and #7273. Documentation Writer review passed at this exact head with AGENTS blob 9c9b36d7f99ec7dd8f40af135f5a9e55354330d7. No code, security, documentation, or current human-review finding remains; exact-head automated and credentialed E2E gates are still required before approval.

@prekshivyas

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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/onboard/policy-selection-recorded-tier.test.ts (1)

102-118: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Restore the process.exit spy locally.

Line 103 relies solely on afterEach; wrap the assertions in try/finally and call exit.mockRestore() so cleanup is guaranteed by the test that created it.

Proposed fix
-    await expect(setupPoliciesWithSelection(deps, "alpha", setupOptions)).rejects.toThrow(
-      "process.exit(1)",
-    );
-
-    expect(syncPresetSelection).toHaveBeenCalledWith("alpha", [], []);
-    expect(waitForSandboxControlPlaneReady).toHaveBeenCalledWith("alpha");
-    expect(waitForSandboxControlPlaneReady.mock.invocationCallOrder[0]).toBeGreaterThan(
-      syncPresetSelection.mock.invocationCallOrder[0],
-    );
-    expect(exit).toHaveBeenCalledWith(1);
+    try {
+      await expect(setupPoliciesWithSelection(deps, "alpha", setupOptions)).rejects.toThrow(
+        "process.exit(1)",
+      );
+
+      expect(syncPresetSelection).toHaveBeenCalledWith("alpha", [], []);
+      expect(waitForSandboxControlPlaneReady).toHaveBeenCalledWith("alpha");
+      expect(waitForSandboxControlPlaneReady.mock.invocationCallOrder[0]).toBeGreaterThan(
+        syncPresetSelection.mock.invocationCallOrder[0],
+      );
+      expect(exit).toHaveBeenCalledWith(1);
+    } finally {
+      exit.mockRestore();
+    }

Based on learnings, locally created spies/mocks must be explicitly restored, especially through try/finally failure paths.

🤖 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/onboard/policy-selection-recorded-tier.test.ts` around lines 102 -
118, Update the test case around setupPoliciesWithSelection and its assertions
to wrap the spy-dependent execution in try/finally, calling exit.mockRestore()
in the finally block. Preserve the existing rejection and invocation assertions
while ensuring the process.exit spy created by this test is restored even when
an assertion fails.

Source: Learnings

🤖 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/onboard/policy-selection-recorded-tier.test.ts`:
- Around line 102-118: Update the test case around setupPoliciesWithSelection
and its assertions to wrap the spy-dependent execution in try/finally, calling
exit.mockRestore() in the finally block. Preserve the existing rejection and
invocation assertions while ensuring the process.exit spy created by this test
is restored even when an assertion fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b31b1726-09e4-419e-a58d-1dbbabed1575

📥 Commits

Reviewing files that changed from the base of the PR and between 8c6f125 and 51651d0.

📒 Files selected for processing (1)
  • src/lib/onboard/policy-selection-recorded-tier.test.ts

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
@prekshivyas

Copy link
Copy Markdown
Collaborator

Exact-head security review — PASS

Reviewed head 14387c79fa846c5e4b149236da370c48e6ddb17c against base 04e6dfa883071dda9df429c66e73168e1a995cba.

Verdict

No security blockers or warnings found. The change narrows retries to exact OpenShell transient states, keeps the managed-health guard fail-closed, preserves bounded deadlines, and reconciles forwarding only after authenticated managed recovery. It does not add dependencies, endpoints, credentials, network-policy changes, or new privilege boundaries.

Findings

No findings.

Detailed analysis

  1. Secrets and credentials — PASS: no secret material added; gitleaks passed.
  2. Input validation and sanitization — PASS: sandbox/port inputs continue through existing registry-owned and argv-based helpers; no new string-evaluated command path.
  3. Authentication and authorization — PASS: retries remain gated by the pinned managed-supervisor health probe; definitive guard failures stop immediately.
  4. Dependencies and third-party libraries — PASS: no dependency or lockfile changes.
  5. Error handling and logging — PASS: only exact transient readiness messages are retryable; unrelated deadline/errors remain terminal and diagnostics contain no credentials.
  6. Cryptography and data protection — PASS: no cryptographic or sensitive-storage changes.
  7. Configuration and security headers — PASS: no HTTP, container privilege, policy, port-exposure, or security-header changes.
  8. Security testing — PASS: positive exact-transient coverage plus negative unrelated-deadline and definitive-guard cases; focused recovery 32/32, finalization 16/16, affected suite 107 files/1,060 tests.
  9. System security — PASS: bounded retries do not bypass health checks; forward reconciliation occurs after the final recovery and before verification, reducing stale-forward/TOCTOU exposure.

Files reviewed

All 22 changed files: five docs pages; sandbox forward/process/stop source and tests; onboarding/finalization/policy source and tests; live double-onboard coverage; and integration regressions.

Documentation Writer review: PASS on this exact head; npm run docs, CLI typecheck/build, check:diff, gitleaks, and pre-push hooks passed.

This is a review comment, not an approval; required GitHub and live E2E gates are still running.

@prekshivyas

Copy link
Copy Markdown
Collaborator

@coderabbitai review

Please review exact head 14387c79fa846c5e4b149236da370c48e6ddb17c. The prior written review ended at 51651d0a58e444ea6127baef66e18492f5a5df9b; the new delta is the exact OpenShell relay open timed out bounded retry plus final recovery/forward reconciliation after refreshing current main.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

@prekshivyas Reviewing the exact-head delta, with attention to the bounded transient retry classification and recovery-to-forward reconciliation path.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

Caution

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

⚠️ Outside diff range comments (2)
src/lib/actions/sandbox/process-recovery.ts (2)

1145-1160: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve unexpected readiness errors instead of labeling them as identity rejection.

The new catch converts every exception from confirmRecoveredSandboxGatewayManaged into relaunchedIdentityRejected, causing beforeProbe(false) to treat transient or unexpected OpenShell failures as definitive identity failures. This also prevents retryable control-plane errors from reaching the modeled classifier and produces misleading failure details. Catch only a typed identity failure, or preserve unexpected errors for the readiness result path.

🤖 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/sandbox/process-recovery.ts` around lines 1145 - 1160, The
catch around confirmRecoveredSandboxGatewayManaged should mark
relaunchedIdentityRejected only for the specific typed identity failure.
Preserve and propagate unexpected or transient readiness/control-plane errors so
beforeProbe(false) can classify them and retain their failure details, rather
than converting every exception into a definitive identity rejection.

692-745: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Migrate the production caller to the result-based readiness API. src/lib/onboard/finalization-deps.ts still depends on waitForRecreatedSandboxOpenShellReady, so the boolean wrapper is still a live runtime entrypoint. Either route that caller through waitForRecreatedSandboxOpenShellReadyResult or clearly bound the wrapper as compatibility with retirement criteria.

🤖 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/sandbox/process-recovery.ts` around lines 692 - 745, Update
the production caller in finalization-deps to use
waitForRecreatedSandboxOpenShellReadyResult instead of the boolean
waitForRecreatedSandboxOpenShellReady wrapper, and handle its result
appropriately while preserving existing readiness behavior. Leave the wrapper
only for non-production compatibility callers, or remove it if no callers
remain.

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.

Outside diff comments:
In `@src/lib/actions/sandbox/process-recovery.ts`:
- Around line 1145-1160: The catch around confirmRecoveredSandboxGatewayManaged
should mark relaunchedIdentityRejected only for the specific typed identity
failure. Preserve and propagate unexpected or transient readiness/control-plane
errors so beforeProbe(false) can classify them and retain their failure details,
rather than converting every exception into a definitive identity rejection.
- Around line 692-745: Update the production caller in finalization-deps to use
waitForRecreatedSandboxOpenShellReadyResult instead of the boolean
waitForRecreatedSandboxOpenShellReady wrapper, and handle its result
appropriately while preserving existing readiness behavior. Leave the wrapper
only for non-production compatibility callers, or remove it if no callers
remain.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: fe2bbd42-1a22-494a-ad06-3b5a992f3149

📥 Commits

Reviewing files that changed from the base of the PR and between 51651d0 and 14387c7.

📒 Files selected for processing (5)
  • docs/reference/commands.mdx
  • src/lib/actions/sandbox/process-recovery.test.ts
  • src/lib/actions/sandbox/process-recovery.ts
  • src/lib/onboard/machine/handlers/finalization.test.ts
  • src/lib/onboard/machine/handlers/finalization.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/lib/onboard/machine/handlers/finalization.test.ts
  • src/lib/onboard/machine/handlers/finalization.ts
  • docs/reference/commands.mdx
  • src/lib/actions/sandbox/process-recovery.test.ts

@prekshivyas prekshivyas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Sensitive-path security review for exact head 14387c79fa846c5e4b149236da370c48e6ddb17c against base 04e6dfa883071dda9df429c66e73168e1a995cba: PASS.

No security findings.

  1. Secrets and credentials — PASS: no credential values, secret material, or credential-boundary fallbacks were added.
  2. Input validation and sanitization — PASS: sandbox names, gateway names, and dashboard ports continue through registry-derived/validated values; OpenShell is invoked with an argument vector, not a shell command. Readiness matching is bounded to exact known control-plane states and a no-op true probe.
  3. Authentication and authorization — PASS: transactional recovery retains its pinned managed-supervisor health guard and does not introduce direct-Docker, SSH, or unauthenticated execution fallback. Post-policy readiness is proved through OpenShell command execution and fails closed.
  4. Dependencies — PASS: no dependency changes.
  5. Error handling and logging — PASS: unexpected OpenShell failures remain terminal; bounded retry covers only documented re-registration states or a timeout of the no-op readiness probe. Dashboard-forward cleanup is explicitly best-effort after successful container stop and cannot replace the container lifecycle result.
  6. Cryptography and data protection — PASS: no cryptographic or sensitive-data persistence changes.
  7. Configuration security — PASS: no new network destinations, exposed ports, wildcard policy permissions, debug modes, or container privilege changes.
  8. Security testing — PASS: positive and negative readiness cases cover sandbox-not-ready, supervisor reconnect, relay timeout, unrelated errors, command timeout, managed-health failure/inconclusive states, forward ownership, sibling isolation, post-policy fail-closed behavior, and final forward ordering.
  9. System security — PASS: stop targets only the selected sandbox's resolved dashboard forward and preserves sibling forwards; recovery remains fail-closed on identity/managed-health drift and does not start the primary forward until OpenShell re-registration succeeds.

Files reviewed: docs/inference/configure-inference-timeouts.mdx, docs/manage-sandboxes/recover-rebuild-sandboxes.mdx, docs/manage-sandboxes/run-sandboxes.mdx, docs/reference/commands.mdx, docs/reference/troubleshooting.mdx, src/lib/actions/sandbox/forward-recovery.ts, src/lib/actions/sandbox/process-recovery.ts, src/lib/actions/sandbox/process-recovery.test.ts, src/lib/actions/sandbox/stop.ts, src/lib/actions/sandbox/stop.test.ts, src/lib/onboard.ts, src/lib/onboard/finalization-deps.ts, src/lib/onboard/machine/final-flow-phases.runtime.test.ts, src/lib/onboard/machine/final-flow-phases.test.ts, src/lib/onboard/machine/handlers/finalization.ts, src/lib/onboard/machine/handlers/finalization.test.ts, src/lib/onboard/policy-selection.ts, src/lib/onboard/policy-selection-recorded-tier.test.ts, test/e2e/live/double-onboard.test.ts, test/onboard-preset-diff.test.ts, test/policy-tiers-onboard.test.ts, test/process-recovery-supervisor-relaunch.test.ts.

Exact-head ordinary CI and full controller-selected E2E remain separate readiness gates.

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
@prekshivyas
prekshivyas enabled auto-merge (squash) July 24, 2026 09:26
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
@prekshivyas
prekshivyas merged commit 4a61af7 into main Jul 24, 2026
70 of 72 checks passed
@prekshivyas
prekshivyas deleted the fix/7227-stop-teardown-dashboard-forward branch July 24, 2026 13:39
@jyaunches
jyaunches restored the fix/7227-stop-teardown-dashboard-forward branch July 24, 2026 13:49
@jyaunches
jyaunches deleted the fix/7227-stop-teardown-dashboard-forward branch July 24, 2026 13:50
@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: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression NV QA Bugs found by the NVIDIA QA Team

Projects

None yet

7 participants