Skip to content

fix(onboard): retry terminated forward listeners - #7267

Merged
prekshivyas merged 12 commits into
mainfrom
codex/fix-forward-listener-retry-7266
Jul 24, 2026
Merged

fix(onboard): retry terminated forward listeners#7267
prekshivyas merged 12 commits into
mainfrom
codex/fix-forward-listener-retry-7266

Conversation

@cjagwani

@cjagwani cjagwani commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

OpenShell can terminate a dashboard-forward attempt after its local listener fails to open, while NemoClaw continues polling the dead attempt for three minutes. This change recognizes that definitive failure, verifies the port is still closed, and performs bounded sandbox-scoped cleanup and retries while preserving the existing live ControlMaster exception.

Related Issue

Fixes #7266

Changes

  • Return a distinct terminal result for OpenShell's ssh exited before local forward listener opened and local forward listener did not open diagnostics instead of polling an attempt that cannot recover.
  • Extend the existing forward-start retry boundary used by dashboard and fixed agent forwards to clean and retry only port conflicts or definitive listener-start failures.
  • Probe before each attempt and reject a pre-existing listener or another sandbox's forward row, so retry cannot turn unrelated ownership into success.
  • Cover recovery, persistent failure, authentication failure, foreign ownership, arbitrary listeners, and the existing ControlMaster live-listener exception.

Type of Change

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

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: this changes internal recovery for an existing onboarding forward and does not add or change a documented command or supported contract.
  • 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:

DGX Station Hardware Evidence

  • Tested on DGX Station
  • Tested commit:
  • Station profile/scenario:
  • Result:
  • Supporting evidence:

Verification

  • PR description includes a Signed-off-by: line and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run check:diff passed when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — forward-start.test.ts: 35 passed; dashboard/messaging caller suite: 86 passed; dashboard integration suite: 46 passed; npm run typecheck:cli passed.
  • Applicable broad gate passed — npm test for broad runtime/test-harness changes; npm run check for repo-wide validation/coverage changes — command/result:
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Charan Jagwani cjagwani@nvidia.com

Summary by CodeRabbit

  • Bug Fixes
    • Improved dashboard and fixed-agent port-forward startup by detecting and classifying a new “listener-start-failure” condition.
    • Added bounded retry behavior for recoverable startup failures, with cleaner recovery between attempts.
    • Refined polling/probing and improved cleanup so retries and stops only occur for the correct sandbox/port.
  • Tests
    • Expanded onboarding coverage for listener-start recovery scenarios, including verification of forward-stop behavior and retry/sleep timing.

@cjagwani cjagwani self-assigned this Jul 20, 2026
@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

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: 0c2cf0d6-688a-4af8-a00b-d46b278725b9

📥 Commits

Reviewing files that changed from the base of the PR and between 1bde00d and 0f6fba2.

📒 Files selected for processing (1)
  • test/onboard-dashboard.test.ts

📝 Walkthrough

Walkthrough

The forward-start flow recognizes definitive listener-start failures, checks sandbox ownership and port liveness, and retries eligible failures with bounded attempts. Agent and dashboard onboarding use the new retry helper, with sandbox-scoped cleanup and expanded tests.

Changes

Forward startup retry handling

Layer / File(s) Summary
Listener failure detection and polling
src/lib/onboard/forward-start.ts, src/lib/onboard/forward-start.test.ts
Forward startup adds the listener-start-failure outcome, classifies definitive listener diagnostics, validates sandbox ownership or port liveness, and avoids extended polling after terminated listener attempts.
Bounded retry policy
src/lib/onboard/forward-start.ts, src/lib/onboard/forward-start.test.ts
runDetachedForwardStartWithRetries retries port conflicts and listener-start failures, preserves retry callbacks, and stops after bounded attempts while excluding authentication failures and arbitrary listeners.
Agent and dashboard integration
src/lib/onboard/agent-fixed-forward.ts, src/lib/onboard/dashboard.ts, test/onboard-dashboard.test.ts
Agent and dashboard forwarding flows use the replacement retry helper; dashboard retries create fresh sandbox-scoped stoppers, with integration tests covering target and foreign ports.

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

Sequence Diagram(s)

sequenceDiagram
  participant Onboarding
  participant ForwardStart
  participant OpenShell
  participant PortProbe
  Onboarding->>ForwardStart: start detached forward with retries
  ForwardStart->>OpenShell: start SSH forward and fetch forward list
  OpenShell-->>ForwardStart: listener diagnostic and ownership rows
  ForwardStart->>PortProbe: check local port after listener failure
  PortProbe-->>ForwardStart: live or closed
  ForwardStart-->>Onboarding: success or bounded retry outcome
Loading

Suggested reviewers: prekshivyas, cv, cv

🚥 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 is concise and accurately summarizes the main onboarding change: retrying terminated forward listeners.
Linked Issues check ✅ Passed The changes add listener-start-failure detection, bounded retries, port verification, sandbox-scoped cleanup, and preserve the fail-closed exceptions.
Out of Scope Changes check ✅ Passed The refactor and test updates stay focused on onboarding forward recovery and retry behavior described in the issue.
✨ 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 codex/fix-forward-listener-retry-7266

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

@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: onboard-repair, onboard-resume, cloud-onboard

1 optional E2E recommendation
  • tunnel-lifecycle

Workflow run details

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

@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 0f6fba2 in the codex/fix-forward-li... branch remains at 96%, unchanged from commit cef1fd0 in the main branch.

TypeScript / code-coverage/cli

The overall coverage in commit 0f6fba2 in the codex/fix-forward-li... branch remains at 80%, unchanged from commit 361ceac in the main branch.

Show a code coverage summary of the most impacted files.
File main 361ceac codex/fix-forward-li... 0f6fba2 +/-
src/lib/platform.ts 89% 84% -5%
src/lib/state/m...-acquisition.ts 89% 87% -2%
src/lib/onboard/dashboard.ts 72% 72% 0%
src/lib/messagi...nnels/policy.ts 100% 100% 0%
src/lib/sandbox...rce-identity.ts 87% 87% 0%
src/lib/securit...ntial-filter.ts 93% 93% 0%
src/lib/trace.ts 94% 94% 0%
src/lib/onboard...shboard-port.ts 90% 93% +3%
src/lib/onboard...orward-start.ts 89% 98% +9%
src/lib/onboard...ixed-forward.ts 38% 77% +39%

Updated July 24, 2026 19:33 UTC

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

326-346: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Verified against tests — logic is correct.

Traced the listedOwner/listedForAnotherSandbox computation and the port-conflict → listener-start-failure → untracked-forward branch ordering against every new test case (foreign ownership rejection, ControlMaster live-port exception, bounded retries). All outcomes match. One minor readability note: this block plus the retry predicate in runDetachedForwardStartWithRetries (Lines 393-424) are both flagged as high-complexity by the diff tooling, and the coding guidelines ask to keep function complexity low. Consider extracting the classification (spawn-conflict / listener-start-failure / ok-port-live decision) into a small named helper to keep runDetachedForwardStartWithDiagnostics's loop body flatter, though the current early-return control flow makes this non-trivial to extract cleanly.

As per coding guidelines, "Keep function complexity low and avoid introducing unnecessary complexity hotspots."

🤖 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/forward-start.ts` around lines 326 - 346, Reduce complexity
in runDetachedForwardStartWithDiagnostics by extracting the
listedOwner/port-conflict/listener-start-failure classification into a small
named helper that preserves the existing spawn-conflict, listener-start-failure,
ok-port-live, and normal-success outcomes. Keep retry behavior in
runDetachedForwardStartWithRetries unchanged and retain the current early-return
semantics.

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/onboard/forward-start.ts`:
- Around line 326-346: Reduce complexity in
runDetachedForwardStartWithDiagnostics by extracting the
listedOwner/port-conflict/listener-start-failure classification into a small
named helper that preserves the existing spawn-conflict, listener-start-failure,
ok-port-live, and normal-success outcomes. Keep retry behavior in
runDetachedForwardStartWithRetries unchanged and retain the current early-return
semantics.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 5256a50c-8531-44c3-adf6-7df259831eba

📥 Commits

Reviewing files that changed from the base of the PR and between 8331dcd and f26d5c9.

📒 Files selected for processing (4)
  • src/lib/onboard/agent-fixed-forward.ts
  • src/lib/onboard/dashboard.ts
  • src/lib/onboard/forward-start.test.ts
  • src/lib/onboard/forward-start.ts

@wscurran wscurran added area: networking DNS, proxy, TLS, ports, host aliases, or connectivity 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 20, 2026

@dfernandez365-rgb dfernandez365-rgb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Security review — changes required before merge.

The new success and retry paths do not prove listener ownership:

  • A port that is closed before spawn and TCP-live afterward is temporal correlation, not ownership. A foreign process can bind in that interval and be accepted as ok-port-live.
  • fetchForwardList() errors are collapsed to an empty list. A gateway authentication/authorization/access failure can therefore combine with listener diagnostics and a live foreign port to return success.
  • Retry cleanup waits and then stops by current sandboxName + port, not immutable attempt identity. It can stop a newer concurrent forward after OpenShell has already terminated the failed child.

Please require structured attempt/listener identity or an authenticated application-level challenge, fail closed whenever ownership enumeration fails, and avoid cleanup unless it is bound to an immutable attempt ID/PID/process identity. Add tests for access-denied plus live port, a foreign bind in the pre/post window, concurrent same-target replacement, and the positive-PID termination path. The current false-to-true mock encodes the race as proof rather than distinguishing it.

@cv cv added the v0.0.93 label Jul 22, 2026
prekshivyas and others added 5 commits July 24, 2026 12:11
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
@prekshivyas
prekshivyas merged commit 50ae554 into main Jul 24, 2026
81 checks passed
@prekshivyas
prekshivyas deleted the codex/fix-forward-listener-retry-7266 branch July 24, 2026 19:43
@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: networking DNS, proxy, TLS, ports, host aliases, or connectivity 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.

OpenShell listener-start failure leaves onboarding polling a terminated forward for 180 seconds

6 participants