Skip to content

fix(onboard): restore not-ready check after compatibility decisions - #8373

Open
jyaunches wants to merge 2 commits into
NVIDIA:mainfrom
jyaunches:fix/8027-not-ready-resume-ordering
Open

fix(onboard): restore not-ready check after compatibility decisions#8373
jyaunches wants to merge 2 commits into
NVIDIA:mainfrom
jyaunches:fix/8027-not-ready-resume-ordering

Conversation

@jyaunches

@jyaunches jyaunches commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Restores the original ordering of the not_ready sandbox check in
decideSandboxResume, placing it after compatibility, configuration,
and tool-disclosure checks instead of before them.

Related Issue

Regression introduced by #8027 (commit 3312899).

Problem

PR #8027 moved the not_ready → repair-and-recreate decision to the
top of decideSandboxResume. This caused gateway upgrade/recreation
and named-rebuild resume flows to fail because:

  1. The sandbox appears not_ready on an upgraded gateway (expected
    transient state during upgrade)
  2. repair-and-recreate fires before compatibility checks can route
    the resume through their proven journal-capable paths
  3. selectedGatewayForSandboxRecreate returns null for old-version
    checkpoints without gateway authority → journal cannot begin
  4. replacesSameNameSandbox is true for repair-and-recreate → throws
    Cannot replace same-name sandbox: no recreate transaction proves ownership of the source sandbox and its registry row.

Fix

Restore the original ordering so that:

  • Gateway upgrades route through compatibility-driven recreate (no
    journal ownership required since removeRegistryEntry is false)
  • Named rebuilds route through recreateSandboxRequested (config check)
  • not_ready only triggers repair-and-recreate as a last resort for
    genuinely damaged sandboxes where no other trigger explains the state

Regressions Fixed

  • Gateway upgrade/recreation — 4/5 gateway-version E2E jobs failing
    with Recreate failed after sandbox was destroyed
  • Deep Agents named rebuild — rebuilding to disable thread opt-in
    left nemoclaw status failing (same recreate/resume surface)

Type of Change

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

Quality Gates

  • Tests added or updated for changed behavior
  • Docs not applicable — no user-facing behavior change
  • Sensitive paths changed (onboarding, sandbox)
  • Sensitive-path review completed — minimal ordering-only revert
    in a pure decision function; no new code paths introduced

Verification

  • npx vitest run src/lib/onboard/machine/handlers/sandbox-resume.test.ts — 30 passed
  • npx vitest run src/lib/onboard/machine/ — 602 passed
  • npm run typecheck:cli — clean
  • npm run build:cli — clean
  • All pre-commit hooks passed

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

Summary by CodeRabbit

  • Bug Fixes
    • Improved sandbox resume handling when environments are not ready.
    • Compatible sandboxes can now resume without unnecessary recreation.
    • Added safer recovery when runtime settings, tools, inference routes, approval settings, or reasoning capabilities change.
    • Ensured affected sandboxes are recreated with appropriate cleanup and explanatory notes.

PR NVIDIA#8027 moved the not_ready → repair-and-recreate decision to the top
of decideSandboxResume, ahead of compatibility, configuration, and tool-
disclosure checks. This caused gateway upgrade/recreation and named-
rebuild resume flows to fail with 'Cannot replace same-name sandbox: no
recreate transaction proves ownership' because:

1. The sandbox appears not_ready on an upgraded gateway (expected)
2. repair-and-recreate fires before compatibility checks can route the
   resume through their proven journal-capable paths
3. selectedGatewayForSandboxRecreate returns null for old-version
   checkpoints without gateway authority → journal cannot begin
4. replacesSameNameSandbox is true for repair-and-recreate → throws

Restoring the original ordering (not_ready checked AFTER compatibility,
reuse, configuration, and tool-disclosure) ensures:
- Gateway upgrades route through compatibility-driven recreate (no
  journal ownership required since removeRegistryEntry is false)
- Named rebuilds route through recreateSandboxRequested (config check)
- not_ready only triggers repair-and-recreate as a last resort for
  genuinely damaged sandboxes where no other trigger explains the state

Fixes the gateway-version E2E regression (4/5 jobs failing) and the
Deep Agents named-rebuild regression introduced by 3312899.

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

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

decideSandboxResume now evaluates compatibility, reuse, runtime-configuration, and tool-disclosure decisions before the not-ready fallback. Tests cover the resulting recreation types, notes, and registry cleanup behavior.

Changes

Sandbox resume handling

Layer / File(s) Summary
Resume decision precedence
src/lib/onboard/machine/handlers/sandbox-resume.ts
The handler evaluates compatibility, reuse, runtime-configuration, and tool-disclosure decisions before returning the not-ready fallback.
Resume decision test coverage
src/lib/onboard/machine/handlers/sandbox-resume.test.ts
Tests preserve repair-and-recreate behavior for DCode auto-approval and reasoning drift. Other not-ready drift cases now assert compatibility or runtime-configuration recreation, diagnostic notes, and registry cleanup.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • NVIDIA/NemoClaw#8027: Both changes update not-ready sandbox recreation decisions in sandbox-resume.ts and its tests.
  • NVIDIA/NemoClaw#8073: Both changes update resume handling and recreation behavior for not-ready sandboxes.
  • NVIDIA/NemoClaw#8339: Both changes refine decideSandboxResume behavior for not-ready sandboxes.

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

Suggested reviewers: apurvvkumaria, cv

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: restoring the not-ready check after compatibility decisions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/onboard/machine/handlers/sandbox-resume.test.ts`:
- Around line 136-138: Rename the test case around the DCode auto-approval
scenario to behavior-oriented wording that states it falls through to
repair-and-recreate when the sandbox is not_ready. Keep the test implementation
and assertions unchanged.
- Around line 163-178: Strengthen the compatibility-recreate test for
decideSandboxResume by using the table’s _noteFragment parameter to assert that
note contains the expected fragment, and require removeRegistryEntry to equal
false. Keep the existing recreate-kind assertion and verify these concrete
metadata values for every drift case.
🪄 Autofix

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: fbe2284b-915a-4443-9a61-cf75552e76a5

📥 Commits

Reviewing files that changed from the base of the PR and between c81f7fc and 18ef9e3.

📒 Files selected for processing (2)
  • src/lib/onboard/machine/handlers/sandbox-resume.test.ts
  • src/lib/onboard/machine/handlers/sandbox-resume.ts

Comment thread src/lib/onboard/machine/handlers/sandbox-resume.test.ts Outdated
Comment on lines +163 to +178
["live DCode inference selection", { inferenceSelectionChanged: true }, "DCode model/provider"],
["agent selection", { resumeAgentChanged: true }, "Agent selection changed"],
["Hermes inference route", { inferenceRouteConfigChanged: true }, "inference route"],
] as const)("uses compatibility recreate for %s drift even when not-ready", (_label, drift, _noteFragment) => {
expect(
decideSandboxResume(
resumeSignals({
sandboxReuseState: "not_ready",
...drift,
}),
),
).toEqual({ kind: "repair-and-recreate" });
).toEqual({
kind: "recreate",
note: expect.any(String),
removeRegistryEntry: expect.any(Boolean),
});

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the compatibility decision metadata.

The table defines _noteFragment, but the assertion does not use it. expect.any(String) accepts an unrelated note, and expect.any(Boolean) accepts removeRegistryEntry: true. The test can pass after a registry-entry handling regression. Assert the note fragment and removeRegistryEntry: false for each case.

As per path instructions, tests must verify observable outcomes through the public boundary instead of accepting arbitrary values for contract fields.

Proposed fix
-      note: expect.any(String),
-      removeRegistryEntry: expect.any(Boolean),
+      note: expect.stringContaining(_noteFragment),
+      removeRegistryEntry: false,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
["live DCode inference selection", { inferenceSelectionChanged: true }, "DCode model/provider"],
["agent selection", { resumeAgentChanged: true }, "Agent selection changed"],
["Hermes inference route", { inferenceRouteConfigChanged: true }, "inference route"],
] as const)("uses compatibility recreate for %s drift even when not-ready", (_label, drift, _noteFragment) => {
expect(
decideSandboxResume(
resumeSignals({
sandboxReuseState: "not_ready",
...drift,
}),
),
).toEqual({ kind: "repair-and-recreate" });
).toEqual({
kind: "recreate",
note: expect.any(String),
removeRegistryEntry: expect.any(Boolean),
});
["live DCode inference selection", { inferenceSelectionChanged: true }, "DCode model/provider"],
["agent selection", { resumeAgentChanged: true }, "Agent selection changed"],
["Hermes inference route", { inferenceRouteConfigChanged: true }, "inference route"],
] as const)("uses compatibility recreate for %s drift even when not-ready", (_label, drift, _noteFragment) => {
expect(
decideSandboxResume(
resumeSignals({
sandboxReuseState: "not_ready",
...drift,
}),
),
).toEqual({
kind: "recreate",
note: expect.stringContaining(_noteFragment),
removeRegistryEntry: false,
});
🤖 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/machine/handlers/sandbox-resume.test.ts` around lines 163 -
178, Strengthen the compatibility-recreate test for decideSandboxResume by using
the table’s _noteFragment parameter to assert that note contains the expected
fragment, and require removeRegistryEntry to equal false. Keep the existing
recreate-kind assertion and verify these concrete metadata values for every
drift case.

Source: Path instructions

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — Informational

Advisor assessment: Informational / low confidence
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions
Status: Partial review preserved 0 canonical finding(s) and 3 terminology decision(s) before the advisor stopped.

Model lanes

  • GPT-5.6 Terra (primary): Failed after a partial review · low confidence · 0 blockers · 0 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Failed after a partial review · low confidence · 2 blockers · 4 warnings · 0 suggestions

Second-opinion terminology and E2E selections are advisory. They do not change the primary assessment or E2E / PR Gate.

3 semantic terminology decisions

Terminology decisions are advisory. They affect the assessment only when a separate finding identifies concrete semantic impact.

  • established — compatibility recreate at src/lib/onboard/machine/handlers/sandbox-resume.test.ts:167: Retain the established term.
  • justified — runtime-configuration recreate at src/lib/onboard/machine/handlers/sandbox-resume.test.ts:189: Retain the modifier because it identifies the tested decision source.
  • justified — tool-disclosure recreate at src/lib/onboard/machine/handlers/sandbox-resume.test.ts:207: Retain the modifier because it identifies the tested decision source.

E2E guidance

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

Recommended E2E: cloud-onboard, onboard-repair, onboard-resume

Workflow run details

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

Address PR review advisor PRA-2 warning: add test coverage ensuring
runtime-configuration decisions (recreateSandboxRequested, web search,
GPU, messaging, Hermes gateway, observability) and tool-disclosure
decisions (migration, change) take precedence over the not-ready repair
fallback. Also rename the DCode auto-approval test to accurately reflect
the repair-and-recreate outcome (PRA-1 suggestion).

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/onboard/machine/handlers/sandbox-resume.test.ts`:
- Around line 182-202: Update the parameterized cases in the decideSandboxResume
tests to include an expected diagnostic-note fragment for each drift scenario,
including the cases around the second assertion block. Replace each
expect.any(String) note matcher with
expect.stringContaining(expectedNoteFragment), while preserving the existing
kind and exact removeRegistryEntry assertions.
🪄 Autofix

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: 200fc238-e218-4a25-89ac-73b498592c8a

📥 Commits

Reviewing files that changed from the base of the PR and between 18ef9e3 and fd475a0.

📒 Files selected for processing (1)
  • src/lib/onboard/machine/handlers/sandbox-resume.test.ts

Comment on lines +182 to +202
it.each([
["explicit recreate request", { recreateSandboxRequested: true }, false],
["web search config change", { webSearchConfigChanged: true }, true],
["sandbox GPU config change", { sandboxGpuConfigChanged: true }, true],
["messaging channel config change", { messagingChannelConfigChanged: true }, true],
["Hermes tool gateway config change", { hermesToolGatewayConfigChanged: true }, true],
["observability change", { observabilityChanged: true }, false],
] as const)("uses runtime-configuration recreate for %s even when not-ready", (_label, drift, expectedRemoveRegistry) => {
expect(
decideSandboxResume(
resumeSignals({
sandboxReuseState: "not_ready",
...drift,
}),
),
).toEqual({
kind: "recreate",
note: expect.any(String),
removeRegistryEntry: expectedRemoveRegistry,
});
});

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the scenario-specific diagnostic note.

The expect.any(String) matchers at Line 199 and Line 217 only prove that the result contains a string. They do not prove that decideSandboxResume reports the drift that triggered recreation. A wrong branch can pass when its kind and removeRegistryEntry values match.

Add an expected note fragment to each table row. Assert it with expect.stringContaining(expectedNoteFragment). Keep the exact removeRegistryEntry assertions.

As per path instructions, tests must verify observable outcomes through the public boundary instead of accepting arbitrary values for contract fields.

Also applies to: 204-219

🤖 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/machine/handlers/sandbox-resume.test.ts` around lines 182 -
202, Update the parameterized cases in the decideSandboxResume tests to include
an expected diagnostic-note fragment for each drift scenario, including the
cases around the second assertion block. Replace each expect.any(String) note
matcher with expect.stringContaining(expectedNoteFragment), while preserving the
existing kind and exact removeRegistryEntry assertions.

Source: Path instructions

@cjagwani cjagwani added v0.0.104 Release target and removed v0.0.103 labels Aug 5, 2026
@cjagwani cjagwani removed the v0.0.103 label Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v0.0.104 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants