Skip to content

fix(rebuild): validate DCode recreation before deletion - #6214

Merged
cv merged 18 commits into
mainfrom
codex/fix-6195-atomic-dcode-rebuild
Jul 3, 2026
Merged

cv merged 18 commits into
mainfrom
codex/fix-6195-atomic-dcode-rebuild

Conversation

@cv

@cv cv commented Jul 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Prevents a Ready LangChain Deep Agents Code sandbox from being deleted until its recorded OpenShell gateway, live inference route, managed base image, and staged build context have been validated and sealed. A DCode-only, process-local orchestration facade carries that verified gateway/context pair through recreation, fixing the post-delete port-8080 false conflict from #6195 without adding generalized persisted replay or FSM state.

Related Issue

Fixes #6195
Refs #6218
Refs #6224
Refs #6226
Refs #5801

Changes

  • Bind DCode rebuilds to the sandbox's recorded gateway before credential validation, probe the live https://inference.local route without exposing credentials, and revalidate the gateway, registry entry, route, schema, base image, and staged context immediately before deletion.
  • Build a disposable DCode image and fingerprint its managed build context before mutation, then pass the retained context and recorded gateway as a one-shot handoff through the create-intent seam landed in refactor(onboard): separate sandbox intent from effects #6218. Ordinary onboarding and non-DCode rebuild behavior remain unchanged.
  • Put the DCode lifecycle in rebuild-dcode-orchestrator.ts: it owns scoped gateway cleanup, target preflight, replacement preparation, mutation-edge revalidation, recovery behavior, and one-shot handoff cleanup while depending on injected generic rebuild callbacks. This reduced rebuild.ts from 1,599 to 1,460 lines and split its 1,477-line flow spec into focused generic and DCode suites plus a shared harness.
  • Extract the onboarding handoff into prepared-dcode-rebuild.ts, which owns gateway validation, ordinary-versus-prepared staging/patching, and consume-before-call semantics. The top-level onboard.ts entrypoint is four lines smaller than main.
  • Preserve the existing sandbox when initial preflight fails; if mutation-edge validation fails after backup, keep the harmless backup but do not delete or recreate the sandbox. Prepared-backup recovery skips unavailable live-route probes and does not take a second backup. Messaging-preflight aborts also restore the prior process gateway.
  • Harden managed-context sealing against symlink substitution, pathname replacement, and in-place mutation by reading regular files through O_NOFOLLOW file descriptors and checking identity/metadata before and after each read.
  • Add a live DCode lifecycle fixture that rotates the credential stored in the real gateway, waits for a 401, runs rebuild without a host credential, and proves there was no backup/delete/create while container identity, readiness, and a workspace marker remain intact.
  • Document the DCode rebuild preflight and backup boundary in the quickstart. A documentation review confirmed no further user-facing changes are needed.
  • Keep generalized FSM/session capture and cross-agent replay out of scope for Epic: stabilize onboarding and runtime lifecycle contracts before public extensibility #6224/refactor(onboard): capture sandbox create intent before destructive effects #6226; true build/health/swap replacement remains rebuild/upgrade: atomic build-verify-swap before deleting old sandbox (health-before-delete) #5801.

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:
  • 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: independent final architecture and nine-category security reviews both passed the narrowed DCode-only design with no blocker; the security review included the final orchestration extraction and managed-context TOCTOU hardening.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Verification

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Git hooks passed during commit and push, or npx prek run --from-ref main --to-ref HEAD passes
  • Targeted tests pass for changed behavior
  • Full npm test passes (broad runtime changes only)
  • 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)

Commands and evidence:

  • make check — passed on the final narrowed implementation under the repository-expected umask 022.
  • npm test — 998 files passed, 2 platform suites skipped; 11,277 tests passed, 35 skipped.
  • Post-merge focused/current-main set — 96/96 passed across generic rebuild flow, DCode flow, managed-image preflight, prepared handoff, inference-provider compatibility, and messaging setup; the final descriptor-pinning, target-guard, and fail-closed review deltas passed 27/27 focused cases.
  • Additional affected environment-failure audit — 121/121 passed; E2E support — 26/26 passed.
  • npm run build:cli and npm run typecheck:cli — passed.
  • Current-tree Vitest project-membership check — 1,075 files across eight disjoint projects; source-shape, test-file-size, and changed-test conditional budgets passed.
  • npm run docs — passed with 0 errors and 2 pre-existing warnings.
  • Pre-push plugin and CLI TypeScript gates — passed.
  • GitHub commit verification — 18/18 commits report verified=true, reason valid.

Signed-off-by: Carlos Villela cvillela@nvidia.com

Summary by CodeRabbit

  • New Features

    • Added support for a new DCode rebuild flow during onboarding and sandbox recreation.
    • Introduced a preflight check for inference routing before rebuilds proceed.
  • Bug Fixes

    • Rebuilds now fail safely when credentials or routing are invalid, helping prevent unsafe sandbox changes.
    • Improved rebuild handling so existing sandboxes and backups are preserved if a later check fails.
  • Documentation

    • Updated the quickstart guide with clearer rebuild and backup behavior for managed DCode sandboxes.

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv cv self-assigned this Jul 2, 2026
@coderabbitai

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

This PR adds DCode-specific rebuild preflight, prepared-image and prepared-build-context handoff support, and wires that flow through rebuild, onboarding, registry wiring, and e2e coverage. It also updates docs and allowlists for the new internal GPU patch network handoff variable.

Changes

DCode Atomic Rebuild Preflight

Layer / File(s) Summary
Docs, allowlist, and target contract
ci/env-var-doc-allowlist.json, docs/get-started/quickstart-langchain-deepagents-code.mdx, src/lib/actions/sandbox/rebuild-dcode-target.ts, src/lib/actions/sandbox/rebuild-dcode-target.test.ts, src/lib/actions/sandbox/rebuild-inference-preflight.ts, src/lib/actions/sandbox/rebuild-inference-preflight.test.ts
Adds the GPU patch network allowlist entry, updates the quickstart rebuild description, and defines the DCode rebuild target and inference preflight contracts with their tests.
Managed replacement image preflight
src/lib/actions/sandbox/rebuild-managed-image-preflight.ts, src/lib/actions/sandbox/rebuild-managed-image-preflight.test.ts
Adds managed DCode replacement image staging, build-context fingerprinting, image verification and disposal helpers, and tests for success, retry, and redaction behavior.
DCode preflight scope and mutation-edge checks
src/lib/actions/sandbox/rebuild-dcode-preflight.ts, src/lib/actions/sandbox/rebuild-dcode-orchestrator.ts
Defines the DCode preflight scope, gateway selection and target validation helpers, pinned base-image handling, prepared replacement orchestration, mutation-edge revalidation, and the orchestrator that binds them into rebuild control flow.
Rebuild flow integration and recreate handoff
src/lib/actions/sandbox/rebuild.ts, src/lib/actions/sandbox/rebuild-gpu-opt-out.ts, src/lib/actions/sandbox/rebuild-gpu-opt-out.test.ts
Integrates the DCode preflight scope into the rebuild flow, routes failure cleanup and revalidation through DCode-specific helpers, and forwards the prepared rebuild handoff into recreate onboarding options.
Prepared DCode rebuild handoff in onboard
src/lib/onboard.ts, src/lib/onboard/build-context-stage.ts, src/lib/onboard/prepared-dcode-rebuild.ts, src/lib/onboard/prepared-dcode-rebuild.test.ts, test/onboard-prepared-build-context.test.ts, test/onboard-prepared-gateway-handoff.test.ts
Adds the prepared build-context contract, prepared DCode rebuild runtime, sandbox build-context and build-id resolvers, onboarding option wiring, and tests for gateway handling and prepared-context reuse.
DCode rebuild flow tests
test/helpers/rebuild-flow-harness.ts, src/lib/actions/sandbox/rebuild-flow.test.ts, src/lib/actions/sandbox/rebuild-dcode-flow.test.ts
Extends the rebuild-flow harness with DCode-specific hooks and adds tests for fail-closed behavior, drift handling, and recreate-from-backup execution.
Atomic credential preflight scenario
test/rebuild-credential-preflight.test.ts
Expands the rebuild credential preflight fixture and adds the HTTP 401 scenario that aborts before backup, delete, and recreate steps.
DCode invalid-credential lifecycle simulation
test/e2e/fixtures/clients/command.ts, test/e2e/fixtures/phases/index.ts, test/e2e/fixtures/phases/lifecycle-dcode-invalid-credential.ts, test/e2e/fixtures/phases/lifecycle.ts, test/e2e/support/e2e-phase-lifecycle.test.ts
Adds the DCode invalid-credential lifecycle simulator, shared readiness helper, lifecycle profile routing, and e2e tests for the invalid-credential rebuild path.
Registry wiring for the new lifecycle profile
test/e2e/registry/runtime-support.ts, test/e2e/registry/definitions/baseline.ts, test/e2e/live/registry-targets.test.ts, test/e2e/support/e2e-live-registry-discovery.test.ts
Registers the DCode lifecycle profile in registry support and baseline target definitions, and adds live-registry discovery coverage for that target.

Estimated code review effort: 5 (Critical) | ~120 minutes

Suggested labels: feature
Suggested reviewers: cjagwani, ericksoa

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes add DCode-specific preflight and revalidation before deletion, preserve the sandbox on failure, and test the non-atomic rebuild path end to end.
Out of Scope Changes check ✅ Passed The added modules, tests, fixture updates, and documentation all support the DCode atomic rebuild fix and do not show unrelated scope creep.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: validating DCode recreation before destructive deletion in the rebuild flow.
✨ 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-6195-atomic-dcode-rebuild

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

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

@github-code-quality

github-code-quality Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the codex/fix-6195-atomi... 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 codex/fix-6195-atomi... dd43445 +/-
nemoclaw/src/se...cret-scanner.ts — 100% —
nemoclaw/src/commands/slash.ts — 100% —
nemoclaw/src/bl...eprint/state.ts — 98% —
nemoclaw/src/onboard/config.ts — 98% —
nemoclaw/src/bl...int/snapshot.ts — 97% —
nemoclaw/src/blueprint/ssrf.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 codex/fix-6195-atomi... branch is 70%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main codex/fix-6195-atomi... dd43445 +/-
src/lib/actions...dbox/rebuild.ts — 82% —
src/lib/actions...all/run-plan.ts — 81% —
src/lib/state/o...oard-session.ts — 80% —
src/lib/shields/index.ts — 75% —
src/lib/state/sandbox.ts — 74% —
src/lib/policy/index.ts — 70% —
src/lib/onboard...er-gpu-patch.ts — 69% —
src/lib/onboard/preflight.ts — 69% —
src/lib/actions...licy-channel.ts — 57% —
src/lib/onboard.ts — 20% —

Updated July 03, 2026 11:44 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jul 2, 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 · 1 test follow-up
Since last review: 2 prior items resolved · 0 still apply · 0 new items found

Action checklist

  • PRA-T1 Add or justify test follow-up: Runtime validation
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 — Live DCode successful rebuild uses the recorded gateway and prepared context, ignores hostile ambient provider env, restores preserved state, and reports Ready after recreate.. The changed behavior crosses Docker, OpenShell gateway, sandbox lifecycle, build-context, and credential-route boundaries. Static and mocked coverage is broad, and the PR adds a targeted live invalid-credential DCode fixture for the reported failure; runtime validation remains the right confidence level for this class of change.

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.

@github-actions

github-actions Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — Blocked

Merge posture: Do not merge until addressed
Primary next action: Fix PRA-3: OPENSHELL_GATEWAY global env var mutation as one-process handoff; then add or justify PRA-T1.
Open items: 5 required · 19 warnings · 7 suggestions · 8 test follow-ups
Since last review: 0 prior items resolved · 7 still apply · 10 new items found

Action checklist

  • PRA-3 Fix: OPENSHELL_GATEWAY global env var mutation as one-process handoff in src/lib/actions/sandbox/rebuild-dcode-preflight.ts:45
  • PRA-4 Fix: SSRF surface: inference probe to inference.local without sandboxName re-validation at execution in src/lib/actions/sandbox/rebuild-inference-preflight.ts:40
  • PRA-5 Fix: onboard.ts monolith at 5419 lines with new DCode handoff surface in src/lib/onboard.ts:1
  • PRA-6 Fix: rebuild.ts exceeds monolith ratchet at 1460 lines (+46 net) in src/lib/actions/sandbox/rebuild.ts:1
  • PRA-7 Fix: rebuild-dcode-preflight.ts is a new 428-line monolith in src/lib/actions/sandbox/rebuild-dcode-preflight.ts:1
  • PRA-1 Resolve or justify: Source-of-truth review needed: Fingerprint fallback without O_NOFOLLOW in readPinnedRegularFile
  • PRA-2 Resolve or justify: Source-of-truth review needed: Build context not re-verified at onboard consumption
  • PRA-8 Resolve or justify: Source-of-truth review needed: OPENSHELL_GATEWAY global env var mutation in createDcodeRebuildPreflightScope in src/lib/actions/sandbox/rebuild-dcode-preflight.ts:45
  • PRA-9 Resolve or justify: Source-of-truth review needed: Gateway recovery accepts 'connected_other' in ensureDcodeRebuildTargetGatewaySelected in src/lib/actions/sandbox/rebuild-dcode-preflight.ts:158
  • PRA-10 Resolve or justify: Source-of-truth review needed: NEMOCLAW_DOCKER_GPU_PATCH_NETWORK env var handoff in prepareManagedDcodeRebuildImage in src/lib/actions/sandbox/rebuild-managed-image-preflight.ts:222
  • PRA-11 Resolve or justify: Source-of-truth review needed: OPENSHELL_GATEWAY global env var mutation in applyDockerGpuPatchNetwork in src/lib/actions/sandbox/rebuild-dcode-preflight.ts:121
  • PRA-12 Resolve or justify: Source-of-truth review needed: Gateway recovery accepts connected_other in recoverNamedGatewayRuntime in src/lib/gateway-runtime-action.ts:1
  • PRA-13 Resolve or justify: NEMOCLAW_DOCKER_GPU_PATCH_NETWORK one-process handoff via env var in src/lib/actions/sandbox/rebuild-managed-image-preflight.ts:148
  • PRA-14 Resolve or justify: Atomic order verification test uses brittle invocationCallOrder in src/lib/actions/sandbox/rebuild-dcode-flow.test.ts:388
  • PRA-15 Resolve or justify: Missing negative test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true in src/lib/actions/sandbox/rebuild-dcode-preflight.ts:1
  • 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: Runtime validation
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Runtime validation
  • PRA-T6 Add or justify test follow-up: Atomic order verification test uses brittle invocationCallOrder
  • PRA-T7 Add or justify test follow-up: Missing negative test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true
  • PRA-T8 Add or justify test follow-up: New test harness at 506 lines introduces complex mock infrastructure
  • PRA-18 In-scope improvement: Verify documentation matches test for late-failure backup behavior in docs/get-started/quickstart-langchain-deepagents-code.mdx:50
  • PRA-19 In-scope improvement: Custom/legacy image detection error message could be actionable in src/lib/actions/sandbox/rebuild-dcode-preflight.ts:360
  • PRA-20 In-scope improvement: Test uses single-use constant for OPENSHELL_GATEWAY value in src/lib/actions/sandbox/rebuild-dcode-flow.test.ts:86
  • PRA-21 In-scope improvement: OPENSHELL_GATEWAY env mutation should be explicit parameter passing in src/lib/actions/sandbox/rebuild-dcode-preflight.ts:172
  • PRA-24 In-scope improvement: Dynamic require() calls for internal modules reduce static analyzability in src/lib/onboard/prepared-dcode-rebuild.ts:1
  • PRA-26 In-scope improvement: resolveDcodeRebuildTarget throws on port mismatch but could suggest env var in src/lib/actions/sandbox/rebuild-dcode-target.ts:45
  • PRA-29 In-scope improvement: New env var NEMOCLAW_DOCKER_GPU_PATCH_NETWORK documented as internal handoff in ci/env-var-doc-allowlist.json:68

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture — Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Resolve/justify architecture — Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-3 Required security src/lib/actions/sandbox/rebuild-dcode-preflight.ts:45 Pass gatewayName explicitly to subprocess calls (docker, openshell) instead of mutating global process.env. If global mutation is unavoidable, document: the invalid state (ambient gateway selection leaking into rebuild), the source boundary (gateway selection should be explicit), why it cannot be fixed here (subprocess API limitation), the regression test (concurrent rebuilds with different gateways), and the removal condition (when subprocess APIs accept explicit gateway).
PRA-4 Required security src/lib/actions/sandbox/rebuild-inference-preflight.ts:40 Add assertion in preflightRebuildInferenceRoute that sandboxName matches a current registry entry with the expected agent/gateway immediately before executeSandboxExecCommand. Ensure shellQuote prevents command injection (appears correct).
PRA-5 Required architecture src/lib/onboard.ts:1 Extract DCode rebuild handoff handling into onboard-dcode-rebuild.ts or similar. Evaluate whether PreparedDcodeRebuildHandoff can be merged into PreparedSandboxBuildContext with optional gatewayName field.
PRA-6 Required architecture src/lib/actions/sandbox/rebuild.ts:1 Offset growth by removing legacy code paths or extract remaining non-DCode orchestration logic (stale recovery, OpenClaw preflight) into dedicated modules.
PRA-7 Required architecture src/lib/actions/sandbox/rebuild-dcode-preflight.ts:1 Split into: rebuild-dcode-gateway.ts (gateway selection/recovery), rebuild-dcode-base-image.ts (base image pinning), rebuild-dcode-image-build.ts (managed image build), rebuild-dcode-validation.ts (fingerprint verification, drift detection). At minimum, extract base image pinning and fingerprint verification into separate modules.
PRA-8 Resolve/justify architecture src/lib/actions/sandbox/rebuild-dcode-preflight.ts:45 Document this as intentional one-process handoff with explicit cleanup in all error paths (finally block at lines 56-72 appears correct). Consider explicit parameter passing in future refactor when subprocess APIs support it.
PRA-9 Resolve/justify architecture src/lib/actions/sandbox/rebuild-dcode-preflight.ts:158 Document the security rationale for allowing 'connected_other' recovery. Add a test verifying gateway restoration behavior on all error paths. Consider whether gateway affinity should be stricter.
PRA-10 Resolve/justify architecture src/lib/actions/sandbox/rebuild-managed-image-preflight.ts:222 Add explicit comment documenting this as intentional one-process handoff. Verify cleanup in all error paths (finally block at lines 308-318 appears correct). Consider explicit parameter passing in future refactor.
PRA-11 Resolve/justify architecture src/lib/actions/sandbox/rebuild-dcode-preflight.ts:121 Document as intentional one-process handoff. Verify the restore function is called in all code paths (used in rebuild.ts finally block).
PRA-12 Resolve/justify architecture src/lib/gateway-runtime-action.ts:1 Document why connected_other is recoverable for rebuild but not for registry recovery (registry-recovery-action.ts line 198 explicitly does NOT recover connected_other). Add test verifying the behavioral difference.
PRA-13 Resolve/justify architecture src/lib/actions/sandbox/rebuild-managed-image-preflight.ts:148 Add explicit comment documenting this as intentional one-process handoff. Verify cleanup in all error paths. Consider explicit parameter passing in future refactor.
PRA-14 Resolve/justify tests src/lib/actions/sandbox/rebuild-dcode-flow.test.ts:388 Replace invocationCallOrder with explicit counter-based ordering or a test helper that records call sequence explicitly.
PRA-15 Resolve/justify tests src/lib/actions/sandbox/rebuild-dcode-preflight.ts:1 Add unit test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true covering the stale recovery code path.
PRA-16 Resolve/justify correctness src/lib/onboard/prepared-dcode-rebuild.ts:68 Add validation: if (gatewayName && !gatewayName.trim()) throw Error. Improve error message to indicate which condition(s) failed (resume, recreateSandbox, agent).
PRA-17 Resolve/justify security src/lib/actions/sandbox/rebuild-dcode-preflight.ts:158 Document the security rationale for allowing 'connected_other' recovery. Add a test verifying gateway restoration behavior on all error paths. Consider whether gateway affinity should be stricter.
PRA-18 Improvement scope docs/get-started/quickstart-langchain-deepagents-code.mdx:50 Verify the documented behavior matches the test. The documentation appears accurate based on test expectations.
PRA-19 Improvement scope src/lib/actions/sandbox/rebuild-dcode-preflight.ts:360 Improve error message to suggest migration steps for custom/legacy images.
PRA-20 Improvement architecture src/lib/actions/sandbox/rebuild-dcode-flow.test.ts:86 Extract const PREVIOUS_GATEWAY = 'previous-gateway' at top of test file and reuse.

🚨 Required before merge

Address these before merging unless a maintainer explicitly overrides the advisor with rationale.

PRA-3 Required — OPENSHELL_GATEWAY global env var mutation as one-process handoff

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:45
  • Category: security
  • Problem: createDcodeRebuildPreflightScope mutates process.env.OPENSHELL_GATEWAY globally (lines 100-103, 157, 215) to pass gateway selection to subprocesses. This global state mutation affects all concurrent operations in the process and is not thread-safe.
  • Impact: Concurrent DCode rebuilds targeting different gateways will cross-contaminate gateway selection, potentially causing rebuilds to operate on the wrong sandbox or fail unpredictably. This is a sandbox isolation violation.
  • Required action: Pass gatewayName explicitly to subprocess calls (docker, openshell) instead of mutating global process.env. If global mutation is unavoidable, document: the invalid state (ambient gateway selection leaking into rebuild), the source boundary (gateway selection should be explicit), why it cannot be fixed here (subprocess API limitation), the regression test (concurrent rebuilds with different gateways), and the removal condition (when subprocess APIs accept explicit gateway).
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: grep -n 'OPENSHELL_GATEWAY' src/lib/actions/sandbox/rebuild-dcode-preflight.ts shows mutations at lines 45, 100-103, 157, 215
  • Missing regression test: Test concurrent DCode rebuilds targeting different gateways to verify no gateway cross-contamination
  • Done when: The required change is committed and verification passes: grep -n 'OPENSHELL_GATEWAY' src/lib/actions/sandbox/rebuild-dcode-preflight.ts shows mutations at lines 45, 100-103, 157, 215.
  • Evidence: createDcodeRebuildPreflightScope captures previous OPENSHELL_GATEWAY, sets process.env.OPENSHELL_GATEWAY = gatewayName at line 100, restores in cleanup at lines 106-111. applyDockerGpuPatchNetwork also mutates env at line 215.

PRA-4 Required — SSRF surface: inference probe to inference.local without sandboxName re-validation at execution

  • Location: src/lib/actions/sandbox/rebuild-inference-preflight.ts:40
  • Category: security
  • Problem: preflightRebuildInferenceRoute constructs a curl command to https://inference.local and executes it via executeSandboxExecCommand. The sandboxName is passed at call time but not re-validated against the current registry immediately before execution. A TOCTOU window exists where the sandbox could be recreated or swapped between preflight and execution.
  • Impact: If an attacker can influence sandbox registry state between preflight validation and probe execution, the probe could be directed to a different sandbox's inference route, potentially leaking inference metadata or causing side effects.
  • Required action: Add assertion in preflightRebuildInferenceRoute that sandboxName matches a current registry entry with the expected agent/gateway immediately before executeSandboxExecCommand. Ensure shellQuote prevents command injection (appears correct).
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Review executeSandboxExecCommand in process-recovery.ts to confirm it validates sandboxName against current registry before execution. Check if sandboxName is resolved to a container ID at execution time.
  • Missing regression test: Test that inference probe fails when sandboxName is swapped between preflight and execution (registry drift simulation)
  • Done when: The required change is committed and verification passes: Review executeSandboxExecCommand in process-recovery.ts to confirm it validates sandboxName against current registry before execution. Check if sandboxName is resolved to a container ID at execution time.
  • Evidence: preflightRebuildInferenceRoute at line 84 calls execute(input.sandboxName, buildRebuildInferenceProbeCommand(input), 100_000) without re-reading registry to verify sandboxName still maps to expected gateway/agent.

PRA-5 Required — onboard.ts monolith at 5419 lines with new DCode handoff surface

  • Location: src/lib/onboard.ts:1
  • Category: architecture
  • Problem: onboard.ts remains at 5419 lines and now imports prepared-dcode-rebuild, adding another handoff surface without extracting the existing monolith. The DCode rebuild handoff logic (createPreparedDcodeRebuildRuntime, resolveSandboxBuildContext, resolveSandboxBuildId) belongs in a dedicated module.
  • Impact: Cognitive complexity exceeds maintainability thresholds. New DCode handoff code is mixed with 50+ other onboarding concerns, making security review and refactoring difficult.
  • Required action: Extract DCode rebuild handoff handling into onboard-dcode-rebuild.ts or similar. Evaluate whether PreparedDcodeRebuildHandoff can be merged into PreparedSandboxBuildContext with optional gatewayName field.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/onboard.ts shows 5419 lines. grep 'prepared-dcode-rebuild' src/lib/onboard.ts shows the new import.
  • Missing regression test: None - architectural
  • Done when: The required change is committed and verification passes: wc -l src/lib/onboard.ts shows 5419 lines. grep 'prepared-dcode-rebuild' src/lib/onboard.ts shows the new import.
  • Evidence: onboard.ts imports preparedDcodeRebuild at line 11 and uses it in the create intent flow. The file has grown despite prior ratchet efforts.

PRA-6 Required — rebuild.ts exceeds monolith ratchet at 1460 lines (+46 net)

  • Location: src/lib/actions/sandbox/rebuild.ts:1
  • Category: architecture
  • Problem: rebuild.ts grew by 46 lines to 1460 lines while the ratchet is at ~1414. The DCode orchestrator integration adds new logic without removing legacy code paths (stale recovery, OpenClaw preflight).
  • Impact: Continues monolith growth trend. DCode-specific logic is mixed with generic rebuild orchestration, violating separation of concerns.
  • Required action: Offset growth by removing legacy code paths or extract remaining non-DCode orchestration logic (stale recovery, OpenClaw preflight) into dedicated modules.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/actions/sandbox/rebuild.ts shows 1460 lines. Compare with base version 1414 lines.
  • Missing regression test: None - architectural
  • Done when: The required change is committed and verification passes: wc -l src/lib/actions/sandbox/rebuild.ts shows 1460 lines. Compare with base version 1414 lines.
  • Evidence: rebuild.ts added DcodeRebuildOrchestrator integration (~80 lines) while only removing ~34 lines of old flow.

PRA-7 Required — rebuild-dcode-preflight.ts is a new 428-line monolith

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:1
  • Category: architecture
  • Problem: The new rebuild-dcode-preflight.ts is 428 lines and handles gateway selection, base image pinning, image building, fingerprinting, inference route validation, registry drift detection, and cleanup. This violates the single-responsibility principle and exceeds the cognitive complexity ratchet.
  • Impact: Hard to review, test, and maintain. Security-critical logic (gateway selection, credential handling, fingerprint verification) is interleaved with Docker build orchestration.
  • Required action: Split into: rebuild-dcode-gateway.ts (gateway selection/recovery), rebuild-dcode-base-image.ts (base image pinning), rebuild-dcode-image-build.ts (managed image build), rebuild-dcode-validation.ts (fingerprint verification, drift detection). At minimum, extract base image pinning and fingerprint verification into separate modules.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/actions/sandbox/rebuild-dcode-preflight.ts shows 428 lines.
  • Missing regression test: None - architectural
  • Done when: The required change is committed and verification passes: wc -l src/lib/actions/sandbox/rebuild-dcode-preflight.ts shows 428 lines.
  • Evidence: Single file contains createDcodeRebuildPreflightScope, ensureDcodeRebuildTargetGatewaySelected, buildPinnedDcodeBaseImage, prepareDcodeReplacementBeforeMutation, revalidateDcodeReplacementAtMutationEdge, fingerprint verification helpers.
Review findings by urgency: 5 required fixes, 19 items to resolve/justify, 7 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: Fingerprint fallback without O_NOFOLLOW in readPinnedRegularFile

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Symlink swap attack on platform without O_NOFOLLOW
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: readPinnedRegularFile lines 102-107: fallback returns null for regular file when O_NOFOLLOW unavailable; fingerprintBuildContext then calls readFileSync(path) which follows symlinks

PRA-2 Resolve/justify — Source-of-truth review needed: Build context not re-verified at onboard consumption

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Build context mutation between deletion edge and onboard consumption is detected
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: rebuild.ts passes preparedDcodeRebuild to onboard; prepared-dcode-rebuild.ts resolveSandboxBuildContext returns preparedBuildContext at line 93 without calling verifyPreparedDcodeRebuildImage

PRA-8 Resolve/justify — Source-of-truth review needed: OPENSHELL_GATEWAY global env var mutation in createDcodeRebuildPreflightScope

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:45
  • Category: architecture
  • Problem: The global env var mutation is a localized workaround for passing gateway selection to subprocesses. The invalid state is ambient gateway selection leaking into rebuild. The source boundary is that gateway selection should be explicit in subprocess calls. It cannot be fixed here because subprocess APIs (docker, openshell CLI) don't accept explicit gateway parameters. Regression test needed: concurrent rebuilds with different gateways. Removal condition: when subprocess APIs accept explicit gateway.
  • Impact: Global state mutation breaks isolation between concurrent rebuild operations.
  • Recommended action: Document this as intentional one-process handoff with explicit cleanup in all error paths (finally block at lines 56-72 appears correct). Consider explicit parameter passing in future refactor when subprocess APIs support it.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review createDcodeRebuildPreflightScope lines 45-130 for env capture/mutation/restore pattern.
  • Missing regression test: Test concurrent DCode rebuilds targeting different gateways to verify no gateway cross-contamination
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review createDcodeRebuildPreflightScope lines 45-130 for env capture/mutation/restore pattern.
  • Evidence: createDcodeRebuildPreflightScope captures previous OPENSHELL_GATEWAY at line 97, mutates at 100, restores in cleanup at 106-111.

PRA-9 Resolve/justify — Source-of-truth review needed: Gateway recovery accepts 'connected_other' in ensureDcodeRebuildTargetGatewaySelected

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:158
  • Category: architecture
  • Problem: recoverableStates includes 'connected_other' meaning a different gateway is currently active. This allows stealing the gateway from another sandbox. The security rationale for allowing this recovery is not documented. Gateway affinity should be stricter (only 'missing_named', 'named_unhealthy', 'named_unreachable').
  • Impact: A DCode rebuild can take over a gateway from another active sandbox, potentially disrupting that sandbox's operations.
  • Recommended action: Document the security rationale for allowing 'connected_other' recovery. Add a test verifying gateway restoration behavior on all error paths. Consider whether gateway affinity should be stricter.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review ensureDcodeRebuildTargetGatewaySelected line 158 and recoverNamedGatewayRuntime in gateway-runtime-action.ts.
  • Missing regression test: Test gateway recovery behavior when another sandbox holds the gateway (connected_other scenario)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review ensureDcodeRebuildTargetGatewaySelected line 158 and recoverNamedGatewayRuntime in gateway-runtime-action.ts.
  • Evidence: recoverableStates array at line 158 includes 'connected_other'. registry-recovery-action.ts line 198 explicitly does NOT recover connected_other for unseeded list.

PRA-10 Resolve/justify — Source-of-truth review needed: NEMOCLAW_DOCKER_GPU_PATCH_NETWORK env var handoff in prepareManagedDcodeRebuildImage

  • Location: src/lib/actions/sandbox/rebuild-managed-image-preflight.ts:222
  • Category: architecture
  • Problem: The env var is deleted at line 230, potentially set by preparePatch at line 240-260, captured at line 283, and restored in finally at lines 314-316. This is a one-process handoff from Docker GPU patch preparation into sandbox creation. The invalid state is GPU patch network mode leaking across rebuild stages.
  • Impact: Global env var used as cross-module handoff mechanism; could leak if cleanup fails.
  • Recommended action: Add explicit comment documenting this as intentional one-process handoff. Verify cleanup in all error paths (finally block at lines 308-318 appears correct). Consider explicit parameter passing in future refactor.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review prepareManagedDcodeRebuildImage lines 222-316 for env var lifecycle.
  • Missing regression test: Test that NEMOCLAW_DOCKER_GPU_PATCH_NETWORK is correctly restored after prepareManagedDcodeRebuildImage fails or succeeds
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review prepareManagedDcodeRebuildImage lines 222-316 for env var lifecycle.
  • Evidence: previousDockerGpuPatchNetwork captured at 222, deleted at 230, captured from process.env at 283, restored in finally at 314-316.

PRA-11 Resolve/justify — Source-of-truth review needed: OPENSHELL_GATEWAY global env var mutation in applyDockerGpuPatchNetwork

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:121
  • Category: architecture
  • Problem: applyDockerGpuPatchNetwork mutates process.env.NEMOCLAW_DOCKER_GPU_PATCH_NETWORK and returns a restore function. This is another one-process env var handoff pattern.
  • Impact: Additional global state mutation surface; restore function must be called in all paths.
  • Recommended action: Document as intentional one-process handoff. Verify the restore function is called in all code paths (used in rebuild.ts finally block).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review applyDockerGpuPatchNetwork at lines 121-130 and rebuild.ts usage at line ~800.
  • Missing regression test: Test that NEMOCLAW_DOCKER_GPU_PATCH_NETWORK is correctly restored after rebuild completes or aborts
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review applyDockerGpuPatchNetwork at lines 121-130 and rebuild.ts usage at line ~800.
  • Evidence: applyDockerGpuPatchNetwork returns a closure that restores previous value; called in rebuild.ts finally block.

PRA-12 Resolve/justify — Source-of-truth review needed: Gateway recovery accepts connected_other in recoverNamedGatewayRuntime

  • Location: src/lib/gateway-runtime-action.ts:1
  • Category: architecture
  • Problem: The recoverNamedGatewayRuntime function (used by ensureDcodeRebuildTargetGatewaySelected) accepts 'connected_other' as recoverable. This allows a rebuild to take over a gateway from another active sandbox. The source of this behavior is in gateway-runtime-action.ts which is shared across rebuild and registry recovery.
  • Impact: Inconsistent gateway affinity: registry recovery rejects connected_other for unseeded list but rebuild accepts it.
  • Recommended action: Document why connected_other is recoverable for rebuild but not for registry recovery (registry-recovery-action.ts line 198 explicitly does NOT recover connected_other). Add test verifying the behavioral difference.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review recoverNamedGatewayRuntime in gateway-runtime-action.ts lines 123, 147.
  • Missing regression test: Test that rebuild recovers connected_other gateway but registry recovery does not
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review recoverNamedGatewayRuntime in gateway-runtime-action.ts lines 123, 147.
  • Evidence: gateway-runtime-action.ts default recoverableStates includes connected_other at line 147; registry-recovery-action.ts canInspectLiveGatewayReadOnly requires healthy_named only.

PRA-13 Resolve/justify — NEMOCLAW_DOCKER_GPU_PATCH_NETWORK one-process handoff via env var

  • Location: src/lib/actions/sandbox/rebuild-managed-image-preflight.ts:148
  • Category: architecture
  • Problem: The env var is used to pass GPU patch network mode from prepareManagedDcodeRebuildImage to the onboard recreation. This is a cross-module handoff via global state.
  • Impact: Global state handoff between rebuild preflight and onboard recreation; fragile if process forks or cleanup fails.
  • Recommended action: Add explicit comment documenting this as intentional one-process handoff. Verify cleanup in all error paths. Consider explicit parameter passing in future refactor.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review rebuild-managed-image-preflight.ts lines 222-316 and prepared-dcode-rebuild.ts applyGatewayEnv.
  • Missing regression test: Test that the GPU patch network mode is correctly passed through to the recreated sandbox
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review rebuild-managed-image-preflight.ts lines 222-316 and prepared-dcode-rebuild.ts applyGatewayEnv.
  • Evidence: prepareManagedDcodeRebuildImage captures dockerGpuPatchNetwork at line 283; prepared-dcode-rebuild.ts applyGatewayEnv doesn't handle this env var - it's read by onboard's docker-gpu-patch logic.

PRA-14 Resolve/justify — Atomic order verification test uses brittle invocationCallOrder

  • Location: src/lib/actions/sandbox/rebuild-dcode-flow.test.ts:388
  • Category: tests
  • Problem: The test at line 388 uses mock.invocationCallOrder which is an internal Vitest property that may change. This makes the test fragile and not portable.
  • Impact: Test may break on Vitest upgrade; not testing actual behavior but implementation detail of mock tracking.
  • Recommended action: Replace invocationCallOrder with explicit counter-based ordering or a test helper that records call sequence explicitly.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review rebuild-dcode-flow.test.ts line 388-405 for invocationCallOrder usage.
  • Missing regression test: N/A - test infrastructure improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review rebuild-dcode-flow.test.ts line 388-405 for invocationCallOrder usage.
  • Evidence: const [firstRouteOrder, preBackupRouteOrder, deleteEdgeRouteOrder] = harness.preflightDcodeRouteSpy.mock.invocationCallOrder;

PRA-15 Resolve/justify — Missing negative test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:1
  • Category: tests
  • Problem: The skipLiveRoute parameter controls whether the live inference route is probed during preflight. There is no test covering the stale recovery code path where skipLiveRoute=true (used for non-Ready sandbox recovery).
  • Impact: Stale recovery path (non-Ready sandbox rebuild) lacks negative test coverage for credential/route failures.
  • Recommended action: Add unit test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true covering the stale recovery code path.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Search rebuild-dcode-flow.test.ts for skipLiveRoute - only the final test uses it via recoveryManifest.
  • Missing regression test: Unit test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Search rebuild-dcode-flow.test.ts for skipLiveRoute - only the final test uses it via recoveryManifest.
  • Evidence: prepareDcodeReplacementBeforeMutation accepts skipLiveRoute parameter; only test 'recreates non-Ready DCode from a validated backup without requiring a live route' exercises it but only happy path.

PRA-16 Resolve/justify — Whitespace-only gatewayName not rejected with clear error

  • Location: src/lib/onboard/prepared-dcode-rebuild.ts:68
  • Category: correctness
  • Problem: createPreparedDcodeRebuildRuntime accepts gatewayName with whitespace (line 68: gatewayName.trim() ?? null). A gatewayName of ' nemoclaw ' would pass validation but not match 'nemoclaw'. The error message should indicate which condition(s) failed (resume, recreateSandbox, agent).
  • Impact: Malformed gatewayName could cause confusing mismatch errors later in the flow.
  • Recommended action: Add validation: if (gatewayName && !gatewayName.trim()) throw Error. Improve error message to indicate which condition(s) failed (resume, recreateSandbox, agent).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review prepared-dcode-rebuild.ts lines 55-75 for gatewayName validation.
  • Missing regression test: Test that whitespace-only gatewayName throws clear validation error
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review prepared-dcode-rebuild.ts lines 55-75 for gatewayName validation.
  • Evidence: Line 68: const gatewayName = prepared?.gatewayName.trim() ?? null; Line 72: if (gatewayName !== null && gatewayName !== expectedGatewayName) throws 'does not match'.

PRA-17 Resolve/justify — Security rationale for 'connected_other' gateway recovery not documented

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:158
  • Category: security
  • Problem: The recoverableStates array includes 'connected_other' which allows a DCode rebuild to take over a gateway from another sandbox. This could be a privilege escalation if sandbox A's rebuild steals sandbox B's gateway. The security rationale is not documented in code.
  • Impact: Undocumented security decision allowing cross-sandbox gateway theft; registry recovery explicitly rejects this.
  • Recommended action: Document the security rationale for allowing 'connected_other' recovery. Add a test verifying gateway restoration behavior on all error paths. Consider whether gateway affinity should be stricter.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review ensureDcodeRebuildTargetGatewaySelected line 158 and compare with registry-recovery-action.ts line 198 which explicitly does NOT recover connected_other.
  • Missing regression test: Test gateway recovery behavior when another sandbox holds the gateway (connected_other scenario)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review ensureDcodeRebuildTargetGatewaySelected line 158 and compare with registry-recovery-action.ts line 198 which explicitly does NOT recover connected_other.
  • Evidence: recoverableStates = ['missing_named', 'named_unhealthy', 'named_unreachable', 'connected_other'] at line 158.

PRA-22 Resolve/justify — TOCTOU in fingerprintBuildContext: symlink swap between openSync and readFileSync

  • Location: src/lib/actions/sandbox/rebuild-managed-image-preflight.ts:79
  • Category: security
  • Problem: readPinnedRegularFile opens with O_NOFOLLOW|O_NONBLOCK, then fstats, then reads. However, between openSync and readFileSync(fd), a symlink swap could occur on platforms without O_NOFOLLOW. The code checks pathBefore.isSymbolicLink() but this is racy. The fallback read path (when O_NOFOLLOW unavailable) uses readFileSync(absolutePath) which follows symlinks.
  • Impact: On platforms without O_NOFOLLOW support, a malicious symlink swap could cause fingerprint verification to read attacker-controlled content, potentially allowing a compromised build context to pass verification.
  • Recommended action: On platforms without O_NOFOLLOW, use openSync + read(fd) instead of readFileSync(path) to maintain descriptor-based verification. Or document platform requirement for O_NOFOLLOW.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review readPinnedRegularFile lines 79-108 and fingerprintBuildContext lines 133-172. Note the fallback path at line 102-107.
  • Missing regression test: Test fingerprint verification with symlink swap attack on platform without O_NOFOLLOW
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review readPinnedRegularFile lines 79-108 and fingerprintBuildContext lines 133-172. Note the fallback path at line 102-107.
  • Evidence: Line 102-107: if O_NOFOLLOW not available, falls back to lstatEntry then returns null for regular file, but fingerprintBuildContext will call readFileSync on the path which follows symlinks.

PRA-23 Resolve/justify — Inference probe uses inference.local without certificate validation

  • Location: src/lib/actions/sandbox/rebuild-inference-preflight.ts:1
  • Category: security
  • Problem: buildRebuildInferenceProbeCommand uses curl with -sS but no -k flag (good), but also no explicit --cacert or certificate pinning. The inference.local endpoint relies on OpenShell's managed TLS. If the gateway TLS is compromised or misconfigured, the probe could be MITMed.
  • Impact: Inference route validation could be bypassed if TLS termination is compromised. The probe validates HTTP status but not certificate authenticity.
  • Recommended action: Document that inference.local TLS is managed by OpenShell gateway and trusted by design. If additional validation is needed, consider certificate pinning or gateway certificate verification.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review buildRebuildInferenceProbeCommand - curl command uses https://inference.local without explicit certificate validation flags.
  • Missing regression test: Test that inference probe fails when TLS certificate is invalid (if applicable)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review buildRebuildInferenceProbeCommand - curl command uses https://inference.local without explicit certificate validation flags.
  • Evidence: curl command at lines 45-55: no --cacert, --cert, --key, or --pinnedpubkey flags.

PRA-25 Resolve/justify — verifyPreparedDcodeRebuildImage called twice in prepareDcodeReplacementBeforeMutation

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:195
  • Category: correctness
  • Problem: Line 195 calls verifyPreparedDcodeRebuildImage(buildContext) and pinnedBase.verify(), then the replacement's verify() at line 260 calls both again. This is redundant but not incorrect.
  • Impact: Minor performance waste; potential confusion about which verification is authoritative.
  • Recommended action: Remove duplicate verification at line 195 or clarify intent. The replacement.verify() at line 260 is the authoritative check.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review prepareDcodeReplacementBeforeMutation lines 195 and 260.
  • Missing regression test: None - minor correctness
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review prepareDcodeReplacementBeforeMutation lines 195 and 260.
  • Evidence: Line 195: if (!verifyPreparedDcodeRebuildImage(buildContext) || !pinnedBase.verify()) fail(...); Line 260: replacement.verify() calls both again.

PRA-27 Resolve/justify — Prepared DCode rebuild context passed to onboard without re-validation at consumption

  • Location: src/lib/actions/sandbox/rebuild.ts:800
  • Category: security
  • Problem: In rebuild.ts, the preparedDcodeRebuild is passed to onboard via recreateOpts. The onboard side (prepared-dcode-rebuild.ts) validates the gatewayName matches expectedGatewayName at line 72, but does not re-verify the buildContext fingerprint. The fingerprint was verified at deletion edge in rebuild.ts but not at consumption in onboard.
  • Impact: If the build context is mutated between rebuild.ts deletion edge and onboard consumption, the mutated context would be used for sandbox creation.
  • Recommended action: Add fingerprint verification in resolveSandboxBuildContext when preparedBuildContext is provided, or document why the deletion-edge verification is sufficient (short time window, same process).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review rebuild.ts line ~800 where preparedDcodeRebuild is passed, and prepared-dcode-rebuild.ts resolveSandboxBuildContext which returns preparedBuildContext without re-verifying fingerprint.
  • Missing regression test: Test that build context mutation between deletion edge and onboard consumption is detected
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review rebuild.ts line ~800 where preparedDcodeRebuild is passed, and prepared-dcode-rebuild.ts resolveSandboxBuildContext which returns preparedBuildContext without re-verifying fingerprint.
  • Evidence: rebuild.ts passes preparedDcodeRebuild to onboard; prepared-dcode-rebuild.ts resolveSandboxBuildContext returns preparedBuildContext directly at line 93 without calling verifyPreparedDcodeRebuildImage.

PRA-28 Resolve/justify — New test harness at 506 lines introduces complex mock infrastructure

  • Location: test/helpers/rebuild-flow-harness.ts:1
  • Category: tests
  • Problem: The new rebuild-flow-harness.ts is 506 lines with extensive mocking of 20+ modules. This increases test maintenance burden and may mask integration issues. However, it does enable focused DCode flow testing.
  • Impact: Heavy mocking may hide integration bugs; high maintenance cost.
  • Recommended action: Ensure the harness doesn't over-mock security-critical paths (gateway selection, credential handling, fingerprint verification). Consider integration tests for critical paths.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review rebuild-flow-harness.ts for mock coverage and complexity.
  • Missing regression test: Integration test for DCode rebuild atomicity without heavy mocking
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review rebuild-flow-harness.ts for mock coverage and complexity.
  • Evidence: Harness mocks 20+ modules including gatewayRuntime, dockerImage, dockerInspect, agentDefs, agentOnboard, gatewayRuntime, onboardMod, onboardSession, registry, sandboxState, sandboxSession, sandboxVersion, rebuildShields, nim, policies, processRecovery, messagingHostForwardLifecycle, messaging, rebuildInference, rebuildManagedImage, rebuildMessagingConflict, shields.

PRA-30 Resolve/justify — prepareSandboxDockerfilePatch called with empty log/warn functions

  • Location: src/lib/actions/sandbox/rebuild-managed-image-preflight.ts:250
  • Category: security
  • Problem: preparePatch is called with log: () => {}, warn: () => {} at lines 250-251. This suppresses diagnostic output during the managed image build. If prepareSandboxDockerfilePatch emits security-relevant warnings, they would be lost.
  • Impact: Security-relevant warnings from dockerfile patching could be silently dropped.
  • Recommended action: Pass proper log/warn functions that capture or forward diagnostics. At minimum, collect warnings for inclusion in failure diagnostics.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review prepareManagedDcodeRebuildImage lines 245-255 and prepareSandboxDockerfilePatch in sandbox-dockerfile-patch-flow.ts.
  • Missing regression test: Test that security-relevant warnings from dockerfile patch are not suppressed
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review prepareManagedDcodeRebuildImage lines 245-255 and prepareSandboxDockerfilePatch in sandbox-dockerfile-patch-flow.ts.
  • Evidence: Lines 250-251: log: () => {}, warn: () => {} passed to preparePatch.

PRA-31 Resolve/justify — requireCurrentTarget calls registry.getSandbox which may return stale cached data

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:180
  • Category: correctness
  • Problem: requireCurrentTarget calls registry.getSandbox(sandboxName) to detect drift. If registry.load() was called earlier and cached, getSandbox might return stale data. The test mocks this with sandboxEntryReads array, but production code relies on registry.getSandbox which may cache.
  • Impact: Registry drift detection may fail if registry.getSandbox returns cached data instead of fresh read.
  • Recommended action: Verify registry.getSandbox reads fresh data from disk or invalidates cache appropriately. If it caches, requireCurrentTarget should call registry.load() directly.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review registry.getSandbox implementation in state/registry.ts to confirm it reads fresh data.
  • Missing regression test: Test registry drift detection with cached registry state
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review registry.getSandbox implementation in state/registry.ts to confirm it reads fresh data.
  • Evidence: requireCurrentTarget at line 180 calls registry.getSandbox(sandboxName); test uses sandboxEntryReads array to simulate drift.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

PRA-18 Improvement — Verify documentation matches test for late-failure backup behavior

  • Location: docs/get-started/quickstart-langchain-deepagents-code.mdx:50
  • Category: scope
  • Problem: The documentation states 'Initial failures stop before backup. NemoClaw checks the target, route, and retained build inputs again after backup, immediately before deletion, so late failures can leave a backup but keep the existing sandbox intact.' This matches the test expectations but should be verified against actual behavior.
  • Impact: Documentation drift risk if behavior changes.
  • Suggested action: Verify the documented behavior matches the test. The documentation appears accurate based on test expectations.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Compare documentation at line 50 with test 'preserves the live DCode sandbox when its registry target drifts after backup' in rebuild-dcode-flow.test.ts.
  • Missing regression test: None - documentation verification
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Documentation lines 50-52 describe pre-backup and post-backup validation; tests 8-11 verify this behavior.

PRA-19 Improvement — Custom/legacy image detection error message could be actionable

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:360
  • Category: scope
  • Problem: The error 'the registry has no NemoClaw-managed image fingerprint; custom and legacy images cannot be safely prebuilt and recreated automatically' doesn't tell the user how to migrate. Should suggest 'Run nemoclaw <name> onboard --fresh to migrate to a managed image, or use nemoclaw <name> snapshot create + manual recreate.'
  • Impact: Users with custom/legacy images get unhelpful error message.
  • Suggested action: Improve error message to suggest migration steps for custom/legacy images.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Review fail() call at line 360 in prepareDcodeReplacementBeforeMutation.
  • Missing regression test: None - UX improvement
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Error message at line 360-362 lacks actionable guidance.

PRA-20 Improvement — Test uses single-use constant for OPENSHELL_GATEWAY value

  • Location: src/lib/actions/sandbox/rebuild-dcode-flow.test.ts:86
  • Category: architecture
  • Problem: The test uses 'previous-gateway' as a magic string in multiple tests. Should extract as a constant at top of test file.
  • Impact: Magic string repeated 4 times; harder to maintain if value changes.
  • Suggested action: Extract const PREVIOUS_GATEWAY = 'previous-gateway' at top of test file and reuse.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Search for 'previous-gateway' in rebuild-dcode-flow.test.ts - appears 4 times.
  • Missing regression test: None - test maintainability
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Lines 86, 105, 116, 133, 353, 373 all use 'previous-gateway' literal.

PRA-21 Improvement — OPENSHELL_GATEWAY env mutation should be explicit parameter passing

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:172
  • Category: architecture
  • Problem: ensureDcodeRebuildTargetGatewaySelected mutates process.env.OPENSHELL_GATEWAY at line 172 (and 100-103 in createDcodeRebuildPreflightScope). This should be refactored to pass gatewayName explicitly to subprocess calls.
  • Impact: Global mutation is the root cause of PRA-6; this is the specific mutation site.
  • Suggested action: Refactor ensureDcodeRebuildTargetGatewaySelected and all callers to pass gatewayName explicitly to openshell/docker subprocess invocations instead of mutating global process.env.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Review ensureDcodeRebuildTargetGatewaySelected line 172 and createDcodeRebuildPreflightScope lines 100-103.
  • Missing regression test: None - refactoring
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Line 172: process.env.OPENSHELL_GATEWAY = gatewayName; after gateway recovery succeeds.

PRA-24 Improvement — Dynamic require() calls for internal modules reduce static analyzability

  • Location: src/lib/onboard/prepared-dcode-rebuild.ts:1
  • Category: correctness
  • Problem: prepared-dcode-rebuild.ts uses dynamic require() for build-context-stage, sandbox-dockerfile-patch-flow, and agent/onboard (lines 15-17, 107, 115). This defeats static analysis and tree-shaking.
  • Impact: Static analysis tools cannot trace dependencies; potential bundling issues.
  • Suggested action: Use static imports where possible. Dynamic requires appear to be for circular dependency avoidance - consider restructuring to eliminate cycles.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Review prepared-dcode-rebuild.ts imports.
  • Missing regression test: None - code quality
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Lines 15-17: dynamic require for build-context-stage, sandbox-dockerfile-patch-flow; lines 107, 115: dynamic require for agent/onboard.

PRA-26 Improvement — resolveDcodeRebuildTarget throws on port mismatch but could suggest env var

  • Location: src/lib/actions/sandbox/rebuild-dcode-target.ts:45
  • Category: correctness
  • Problem: Error message at line 45 suggests 'Re-run with NEMOCLAW_GATEWAY_PORT=${gatewayPort}' but this env var may not be the correct mechanism for cross-port rebuilds. Should verify this is the supported workflow.
  • Impact: Potentially misleading error message if NEMOCLAW_GATEWAY_PORT isn't the correct mechanism.
  • Suggested action: Verify NEMOCLAW_GATEWAY_PORT is the correct mechanism for cross-port rebuilds. If not, update error message.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Review resolveDcodeRebuildTarget line 45 and search for NEMOCLAW_GATEWAY_PORT usage.
  • Missing regression test: None - UX/clarity
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Error message at lines 43-47 suggests NEMOCLAW_GATEWAY_PORT env var.

PRA-29 Improvement — New env var NEMOCLAW_DOCKER_GPU_PATCH_NETWORK documented as internal handoff

  • Location: ci/env-var-doc-allowlist.json:68
  • Category: scope
  • Problem: The allowlist entry correctly documents this as 'Internal one-process handoff from Docker GPU patch preparation into sandbox creation. Rebuild scopes and restores it; users must not set it.' This is good practice for internal env vars.
  • Impact: Positive - documentation is appropriate.
  • Suggested action: No action needed - documentation is appropriate.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Review ci/env-var-doc-allowlist.json line 68-70.
  • Missing regression test: None - documentation
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Entry at lines 68-70: name NEMOCLAW_DOCKER_GPU_PATCH_NETWORK with reason documenting internal handoff.
Simplification opportunities: 3 possible cuts, net -3 lines possible

These are safe simplification checks only. Do not remove validation, security controls, data-loss prevention, or required tests.

  • PRA-20 shrink (src/lib/actions/sandbox/rebuild-dcode-flow.test.ts:86): Six occurrences of 'previous-gateway' string literal
    • Replacement: const PREVIOUS_GATEWAY = 'previous-gateway' at file top
    • Net: -2 lines
    • Safety boundary: Test behavior unchanged; only constant extraction
  • PRA-24 native (src/lib/onboard/prepared-dcode-rebuild.ts:1): Dynamic require() calls for internal modules
    • Replacement: Static import statements
    • Net: 0 lines
    • Safety boundary: Must verify no circular dependency introduced; may need module restructuring
  • PRA-25 delete (src/lib/actions/sandbox/rebuild-dcode-preflight.ts:195): Line 195 duplicate verification call
    • Replacement: Remove line 195; rely on replacement.verify() at line 260
    • Net: -1 lines
    • Safety boundary: replacement.verify() at line 260 still performs both checks
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 — Concurrent DCode rebuilds with different gateways to verify no gateway cross-contamination (PRA-6). Runtime/sandbox/infrastructure paths need behavioral runtime validation: rebuild-dcode-orchestrator.ts, rebuild-dcode-preflight.ts, rebuild-inference-preflight.ts, rebuild-managed-image-preflight.ts, prepared-dcode-rebuild.ts. Unit tests with heavy mocking cover logic but not integration boundaries.
  • PRA-T2 Runtime validation — connected_other gateway recovery with two live sandboxes (PRA-15). Runtime/sandbox/infrastructure paths need behavioral runtime validation: rebuild-dcode-orchestrator.ts, rebuild-dcode-preflight.ts, rebuild-inference-preflight.ts, rebuild-managed-image-preflight.ts, prepared-dcode-rebuild.ts. Unit tests with heavy mocking cover logic but not integration boundaries.
  • PRA-T3 Runtime validation — Fingerprint verification under symlink swap on macOS/Windows (PRA-20). Runtime/sandbox/infrastructure paths need behavioral runtime validation: rebuild-dcode-orchestrator.ts, rebuild-dcode-preflight.ts, rebuild-inference-preflight.ts, rebuild-managed-image-preflight.ts, prepared-dcode-rebuild.ts. Unit tests with heavy mocking cover logic but not integration boundaries.
  • PRA-T4 Runtime validation — Build context mutation between rebuild.ts deletion edge and onboard consumption (PRA-25). Runtime/sandbox/infrastructure paths need behavioral runtime validation: rebuild-dcode-orchestrator.ts, rebuild-dcode-preflight.ts, rebuild-inference-preflight.ts, rebuild-managed-image-preflight.ts, prepared-dcode-rebuild.ts. Unit tests with heavy mocking cover logic but not integration boundaries.
  • PRA-T5 Runtime validation — Stale recovery (skipLiveRoute=true) with invalid credential (PRA-12). Runtime/sandbox/infrastructure paths need behavioral runtime validation: rebuild-dcode-orchestrator.ts, rebuild-dcode-preflight.ts, rebuild-inference-preflight.ts, rebuild-managed-image-preflight.ts, prepared-dcode-rebuild.ts. Unit tests with heavy mocking cover logic but not integration boundaries.
  • PRA-T6 Atomic order verification test uses brittle invocationCallOrder — Replace invocationCallOrder with explicit counter-based ordering or a test helper that records call sequence explicitly.
  • PRA-T7 Missing negative test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true — Add unit test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true covering the stale recovery code path.
  • PRA-T8 New test harness at 506 lines introduces complex mock infrastructure — Ensure the harness doesn't over-mock security-critical paths (gateway selection, credential handling, fingerprint verification). Consider integration tests for critical paths.
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: Fingerprint fallback without O_NOFOLLOW in readPinnedRegularFile

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Symlink swap attack on platform without O_NOFOLLOW
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: readPinnedRegularFile lines 102-107: fallback returns null for regular file when O_NOFOLLOW unavailable; fingerprintBuildContext then calls readFileSync(path) which follows symlinks

PRA-2 Resolve/justify — Source-of-truth review needed: Build context not re-verified at onboard consumption

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Build context mutation between deletion edge and onboard consumption is detected
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: rebuild.ts passes preparedDcodeRebuild to onboard; prepared-dcode-rebuild.ts resolveSandboxBuildContext returns preparedBuildContext at line 93 without calling verifyPreparedDcodeRebuildImage

PRA-3 Required — OPENSHELL_GATEWAY global env var mutation as one-process handoff

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:45
  • Category: security
  • Problem: createDcodeRebuildPreflightScope mutates process.env.OPENSHELL_GATEWAY globally (lines 100-103, 157, 215) to pass gateway selection to subprocesses. This global state mutation affects all concurrent operations in the process and is not thread-safe.
  • Impact: Concurrent DCode rebuilds targeting different gateways will cross-contaminate gateway selection, potentially causing rebuilds to operate on the wrong sandbox or fail unpredictably. This is a sandbox isolation violation.
  • Required action: Pass gatewayName explicitly to subprocess calls (docker, openshell) instead of mutating global process.env. If global mutation is unavoidable, document: the invalid state (ambient gateway selection leaking into rebuild), the source boundary (gateway selection should be explicit), why it cannot be fixed here (subprocess API limitation), the regression test (concurrent rebuilds with different gateways), and the removal condition (when subprocess APIs accept explicit gateway).
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: grep -n 'OPENSHELL_GATEWAY' src/lib/actions/sandbox/rebuild-dcode-preflight.ts shows mutations at lines 45, 100-103, 157, 215
  • Missing regression test: Test concurrent DCode rebuilds targeting different gateways to verify no gateway cross-contamination
  • Done when: The required change is committed and verification passes: grep -n 'OPENSHELL_GATEWAY' src/lib/actions/sandbox/rebuild-dcode-preflight.ts shows mutations at lines 45, 100-103, 157, 215.
  • Evidence: createDcodeRebuildPreflightScope captures previous OPENSHELL_GATEWAY, sets process.env.OPENSHELL_GATEWAY = gatewayName at line 100, restores in cleanup at lines 106-111. applyDockerGpuPatchNetwork also mutates env at line 215.

PRA-4 Required — SSRF surface: inference probe to inference.local without sandboxName re-validation at execution

  • Location: src/lib/actions/sandbox/rebuild-inference-preflight.ts:40
  • Category: security
  • Problem: preflightRebuildInferenceRoute constructs a curl command to https://inference.local and executes it via executeSandboxExecCommand. The sandboxName is passed at call time but not re-validated against the current registry immediately before execution. A TOCTOU window exists where the sandbox could be recreated or swapped between preflight and execution.
  • Impact: If an attacker can influence sandbox registry state between preflight validation and probe execution, the probe could be directed to a different sandbox's inference route, potentially leaking inference metadata or causing side effects.
  • Required action: Add assertion in preflightRebuildInferenceRoute that sandboxName matches a current registry entry with the expected agent/gateway immediately before executeSandboxExecCommand. Ensure shellQuote prevents command injection (appears correct).
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Review executeSandboxExecCommand in process-recovery.ts to confirm it validates sandboxName against current registry before execution. Check if sandboxName is resolved to a container ID at execution time.
  • Missing regression test: Test that inference probe fails when sandboxName is swapped between preflight and execution (registry drift simulation)
  • Done when: The required change is committed and verification passes: Review executeSandboxExecCommand in process-recovery.ts to confirm it validates sandboxName against current registry before execution. Check if sandboxName is resolved to a container ID at execution time.
  • Evidence: preflightRebuildInferenceRoute at line 84 calls execute(input.sandboxName, buildRebuildInferenceProbeCommand(input), 100_000) without re-reading registry to verify sandboxName still maps to expected gateway/agent.

PRA-5 Required — onboard.ts monolith at 5419 lines with new DCode handoff surface

  • Location: src/lib/onboard.ts:1
  • Category: architecture
  • Problem: onboard.ts remains at 5419 lines and now imports prepared-dcode-rebuild, adding another handoff surface without extracting the existing monolith. The DCode rebuild handoff logic (createPreparedDcodeRebuildRuntime, resolveSandboxBuildContext, resolveSandboxBuildId) belongs in a dedicated module.
  • Impact: Cognitive complexity exceeds maintainability thresholds. New DCode handoff code is mixed with 50+ other onboarding concerns, making security review and refactoring difficult.
  • Required action: Extract DCode rebuild handoff handling into onboard-dcode-rebuild.ts or similar. Evaluate whether PreparedDcodeRebuildHandoff can be merged into PreparedSandboxBuildContext with optional gatewayName field.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/onboard.ts shows 5419 lines. grep 'prepared-dcode-rebuild' src/lib/onboard.ts shows the new import.
  • Missing regression test: None - architectural
  • Done when: The required change is committed and verification passes: wc -l src/lib/onboard.ts shows 5419 lines. grep 'prepared-dcode-rebuild' src/lib/onboard.ts shows the new import.
  • Evidence: onboard.ts imports preparedDcodeRebuild at line 11 and uses it in the create intent flow. The file has grown despite prior ratchet efforts.

PRA-6 Required — rebuild.ts exceeds monolith ratchet at 1460 lines (+46 net)

  • Location: src/lib/actions/sandbox/rebuild.ts:1
  • Category: architecture
  • Problem: rebuild.ts grew by 46 lines to 1460 lines while the ratchet is at ~1414. The DCode orchestrator integration adds new logic without removing legacy code paths (stale recovery, OpenClaw preflight).
  • Impact: Continues monolith growth trend. DCode-specific logic is mixed with generic rebuild orchestration, violating separation of concerns.
  • Required action: Offset growth by removing legacy code paths or extract remaining non-DCode orchestration logic (stale recovery, OpenClaw preflight) into dedicated modules.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/actions/sandbox/rebuild.ts shows 1460 lines. Compare with base version 1414 lines.
  • Missing regression test: None - architectural
  • Done when: The required change is committed and verification passes: wc -l src/lib/actions/sandbox/rebuild.ts shows 1460 lines. Compare with base version 1414 lines.
  • Evidence: rebuild.ts added DcodeRebuildOrchestrator integration (~80 lines) while only removing ~34 lines of old flow.

PRA-7 Required — rebuild-dcode-preflight.ts is a new 428-line monolith

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:1
  • Category: architecture
  • Problem: The new rebuild-dcode-preflight.ts is 428 lines and handles gateway selection, base image pinning, image building, fingerprinting, inference route validation, registry drift detection, and cleanup. This violates the single-responsibility principle and exceeds the cognitive complexity ratchet.
  • Impact: Hard to review, test, and maintain. Security-critical logic (gateway selection, credential handling, fingerprint verification) is interleaved with Docker build orchestration.
  • Required action: Split into: rebuild-dcode-gateway.ts (gateway selection/recovery), rebuild-dcode-base-image.ts (base image pinning), rebuild-dcode-image-build.ts (managed image build), rebuild-dcode-validation.ts (fingerprint verification, drift detection). At minimum, extract base image pinning and fingerprint verification into separate modules.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/actions/sandbox/rebuild-dcode-preflight.ts shows 428 lines.
  • Missing regression test: None - architectural
  • Done when: The required change is committed and verification passes: wc -l src/lib/actions/sandbox/rebuild-dcode-preflight.ts shows 428 lines.
  • Evidence: Single file contains createDcodeRebuildPreflightScope, ensureDcodeRebuildTargetGatewaySelected, buildPinnedDcodeBaseImage, prepareDcodeReplacementBeforeMutation, revalidateDcodeReplacementAtMutationEdge, fingerprint verification helpers.

PRA-8 Resolve/justify — Source-of-truth review needed: OPENSHELL_GATEWAY global env var mutation in createDcodeRebuildPreflightScope

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:45
  • Category: architecture
  • Problem: The global env var mutation is a localized workaround for passing gateway selection to subprocesses. The invalid state is ambient gateway selection leaking into rebuild. The source boundary is that gateway selection should be explicit in subprocess calls. It cannot be fixed here because subprocess APIs (docker, openshell CLI) don't accept explicit gateway parameters. Regression test needed: concurrent rebuilds with different gateways. Removal condition: when subprocess APIs accept explicit gateway.
  • Impact: Global state mutation breaks isolation between concurrent rebuild operations.
  • Recommended action: Document this as intentional one-process handoff with explicit cleanup in all error paths (finally block at lines 56-72 appears correct). Consider explicit parameter passing in future refactor when subprocess APIs support it.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review createDcodeRebuildPreflightScope lines 45-130 for env capture/mutation/restore pattern.
  • Missing regression test: Test concurrent DCode rebuilds targeting different gateways to verify no gateway cross-contamination
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review createDcodeRebuildPreflightScope lines 45-130 for env capture/mutation/restore pattern.
  • Evidence: createDcodeRebuildPreflightScope captures previous OPENSHELL_GATEWAY at line 97, mutates at 100, restores in cleanup at 106-111.

PRA-9 Resolve/justify — Source-of-truth review needed: Gateway recovery accepts 'connected_other' in ensureDcodeRebuildTargetGatewaySelected

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:158
  • Category: architecture
  • Problem: recoverableStates includes 'connected_other' meaning a different gateway is currently active. This allows stealing the gateway from another sandbox. The security rationale for allowing this recovery is not documented. Gateway affinity should be stricter (only 'missing_named', 'named_unhealthy', 'named_unreachable').
  • Impact: A DCode rebuild can take over a gateway from another active sandbox, potentially disrupting that sandbox's operations.
  • Recommended action: Document the security rationale for allowing 'connected_other' recovery. Add a test verifying gateway restoration behavior on all error paths. Consider whether gateway affinity should be stricter.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review ensureDcodeRebuildTargetGatewaySelected line 158 and recoverNamedGatewayRuntime in gateway-runtime-action.ts.
  • Missing regression test: Test gateway recovery behavior when another sandbox holds the gateway (connected_other scenario)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review ensureDcodeRebuildTargetGatewaySelected line 158 and recoverNamedGatewayRuntime in gateway-runtime-action.ts.
  • Evidence: recoverableStates array at line 158 includes 'connected_other'. registry-recovery-action.ts line 198 explicitly does NOT recover connected_other for unseeded list.

PRA-10 Resolve/justify — Source-of-truth review needed: NEMOCLAW_DOCKER_GPU_PATCH_NETWORK env var handoff in prepareManagedDcodeRebuildImage

  • Location: src/lib/actions/sandbox/rebuild-managed-image-preflight.ts:222
  • Category: architecture
  • Problem: The env var is deleted at line 230, potentially set by preparePatch at line 240-260, captured at line 283, and restored in finally at lines 314-316. This is a one-process handoff from Docker GPU patch preparation into sandbox creation. The invalid state is GPU patch network mode leaking across rebuild stages.
  • Impact: Global env var used as cross-module handoff mechanism; could leak if cleanup fails.
  • Recommended action: Add explicit comment documenting this as intentional one-process handoff. Verify cleanup in all error paths (finally block at lines 308-318 appears correct). Consider explicit parameter passing in future refactor.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review prepareManagedDcodeRebuildImage lines 222-316 for env var lifecycle.
  • Missing regression test: Test that NEMOCLAW_DOCKER_GPU_PATCH_NETWORK is correctly restored after prepareManagedDcodeRebuildImage fails or succeeds
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review prepareManagedDcodeRebuildImage lines 222-316 for env var lifecycle.
  • Evidence: previousDockerGpuPatchNetwork captured at 222, deleted at 230, captured from process.env at 283, restored in finally at 314-316.

PRA-11 Resolve/justify — Source-of-truth review needed: OPENSHELL_GATEWAY global env var mutation in applyDockerGpuPatchNetwork

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:121
  • Category: architecture
  • Problem: applyDockerGpuPatchNetwork mutates process.env.NEMOCLAW_DOCKER_GPU_PATCH_NETWORK and returns a restore function. This is another one-process env var handoff pattern.
  • Impact: Additional global state mutation surface; restore function must be called in all paths.
  • Recommended action: Document as intentional one-process handoff. Verify the restore function is called in all code paths (used in rebuild.ts finally block).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review applyDockerGpuPatchNetwork at lines 121-130 and rebuild.ts usage at line ~800.
  • Missing regression test: Test that NEMOCLAW_DOCKER_GPU_PATCH_NETWORK is correctly restored after rebuild completes or aborts
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review applyDockerGpuPatchNetwork at lines 121-130 and rebuild.ts usage at line ~800.
  • Evidence: applyDockerGpuPatchNetwork returns a closure that restores previous value; called in rebuild.ts finally block.

PRA-12 Resolve/justify — Source-of-truth review needed: Gateway recovery accepts connected_other in recoverNamedGatewayRuntime

  • Location: src/lib/gateway-runtime-action.ts:1
  • Category: architecture
  • Problem: The recoverNamedGatewayRuntime function (used by ensureDcodeRebuildTargetGatewaySelected) accepts 'connected_other' as recoverable. This allows a rebuild to take over a gateway from another active sandbox. The source of this behavior is in gateway-runtime-action.ts which is shared across rebuild and registry recovery.
  • Impact: Inconsistent gateway affinity: registry recovery rejects connected_other for unseeded list but rebuild accepts it.
  • Recommended action: Document why connected_other is recoverable for rebuild but not for registry recovery (registry-recovery-action.ts line 198 explicitly does NOT recover connected_other). Add test verifying the behavioral difference.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review recoverNamedGatewayRuntime in gateway-runtime-action.ts lines 123, 147.
  • Missing regression test: Test that rebuild recovers connected_other gateway but registry recovery does not
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review recoverNamedGatewayRuntime in gateway-runtime-action.ts lines 123, 147.
  • Evidence: gateway-runtime-action.ts default recoverableStates includes connected_other at line 147; registry-recovery-action.ts canInspectLiveGatewayReadOnly requires healthy_named only.

PRA-13 Resolve/justify — NEMOCLAW_DOCKER_GPU_PATCH_NETWORK one-process handoff via env var

  • Location: src/lib/actions/sandbox/rebuild-managed-image-preflight.ts:148
  • Category: architecture
  • Problem: The env var is used to pass GPU patch network mode from prepareManagedDcodeRebuildImage to the onboard recreation. This is a cross-module handoff via global state.
  • Impact: Global state handoff between rebuild preflight and onboard recreation; fragile if process forks or cleanup fails.
  • Recommended action: Add explicit comment documenting this as intentional one-process handoff. Verify cleanup in all error paths. Consider explicit parameter passing in future refactor.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review rebuild-managed-image-preflight.ts lines 222-316 and prepared-dcode-rebuild.ts applyGatewayEnv.
  • Missing regression test: Test that the GPU patch network mode is correctly passed through to the recreated sandbox
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review rebuild-managed-image-preflight.ts lines 222-316 and prepared-dcode-rebuild.ts applyGatewayEnv.
  • Evidence: prepareManagedDcodeRebuildImage captures dockerGpuPatchNetwork at line 283; prepared-dcode-rebuild.ts applyGatewayEnv doesn't handle this env var - it's read by onboard's docker-gpu-patch logic.

PRA-14 Resolve/justify — Atomic order verification test uses brittle invocationCallOrder

  • Location: src/lib/actions/sandbox/rebuild-dcode-flow.test.ts:388
  • Category: tests
  • Problem: The test at line 388 uses mock.invocationCallOrder which is an internal Vitest property that may change. This makes the test fragile and not portable.
  • Impact: Test may break on Vitest upgrade; not testing actual behavior but implementation detail of mock tracking.
  • Recommended action: Replace invocationCallOrder with explicit counter-based ordering or a test helper that records call sequence explicitly.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review rebuild-dcode-flow.test.ts line 388-405 for invocationCallOrder usage.
  • Missing regression test: N/A - test infrastructure improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review rebuild-dcode-flow.test.ts line 388-405 for invocationCallOrder usage.
  • Evidence: const [firstRouteOrder, preBackupRouteOrder, deleteEdgeRouteOrder] = harness.preflightDcodeRouteSpy.mock.invocationCallOrder;

PRA-15 Resolve/justify — Missing negative test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:1
  • Category: tests
  • Problem: The skipLiveRoute parameter controls whether the live inference route is probed during preflight. There is no test covering the stale recovery code path where skipLiveRoute=true (used for non-Ready sandbox recovery).
  • Impact: Stale recovery path (non-Ready sandbox rebuild) lacks negative test coverage for credential/route failures.
  • Recommended action: Add unit test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true covering the stale recovery code path.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Search rebuild-dcode-flow.test.ts for skipLiveRoute - only the final test uses it via recoveryManifest.
  • Missing regression test: Unit test for prepareDcodeReplacementBeforeMutation with skipLiveRoute=true
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Search rebuild-dcode-flow.test.ts for skipLiveRoute - only the final test uses it via recoveryManifest.
  • Evidence: prepareDcodeReplacementBeforeMutation accepts skipLiveRoute parameter; only test 'recreates non-Ready DCode from a validated backup without requiring a live route' exercises it but only happy path.

PRA-16 Resolve/justify — Whitespace-only gatewayName not rejected with clear error

  • Location: src/lib/onboard/prepared-dcode-rebuild.ts:68
  • Category: correctness
  • Problem: createPreparedDcodeRebuildRuntime accepts gatewayName with whitespace (line 68: gatewayName.trim() ?? null). A gatewayName of ' nemoclaw ' would pass validation but not match 'nemoclaw'. The error message should indicate which condition(s) failed (resume, recreateSandbox, agent).
  • Impact: Malformed gatewayName could cause confusing mismatch errors later in the flow.
  • Recommended action: Add validation: if (gatewayName && !gatewayName.trim()) throw Error. Improve error message to indicate which condition(s) failed (resume, recreateSandbox, agent).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review prepared-dcode-rebuild.ts lines 55-75 for gatewayName validation.
  • Missing regression test: Test that whitespace-only gatewayName throws clear validation error
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review prepared-dcode-rebuild.ts lines 55-75 for gatewayName validation.
  • Evidence: Line 68: const gatewayName = prepared?.gatewayName.trim() ?? null; Line 72: if (gatewayName !== null && gatewayName !== expectedGatewayName) throws 'does not match'.

PRA-17 Resolve/justify — Security rationale for 'connected_other' gateway recovery not documented

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:158
  • Category: security
  • Problem: The recoverableStates array includes 'connected_other' which allows a DCode rebuild to take over a gateway from another sandbox. This could be a privilege escalation if sandbox A's rebuild steals sandbox B's gateway. The security rationale is not documented in code.
  • Impact: Undocumented security decision allowing cross-sandbox gateway theft; registry recovery explicitly rejects this.
  • Recommended action: Document the security rationale for allowing 'connected_other' recovery. Add a test verifying gateway restoration behavior on all error paths. Consider whether gateway affinity should be stricter.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Review ensureDcodeRebuildTargetGatewaySelected line 158 and compare with registry-recovery-action.ts line 198 which explicitly does NOT recover connected_other.
  • Missing regression test: Test gateway recovery behavior when another sandbox holds the gateway (connected_other scenario)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Review ensureDcodeRebuildTargetGatewaySelected line 158 and compare with registry-recovery-action.ts line 198 which explicitly does NOT recover connected_other.
  • Evidence: recoverableStates = ['missing_named', 'named_unhealthy', 'named_unreachable', 'connected_other'] at line 158.

PRA-18 Improvement — Verify documentation matches test for late-failure backup behavior

  • Location: docs/get-started/quickstart-langchain-deepagents-code.mdx:50
  • Category: scope
  • Problem: The documentation states 'Initial failures stop before backup. NemoClaw checks the target, route, and retained build inputs again after backup, immediately before deletion, so late failures can leave a backup but keep the existing sandbox intact.' This matches the test expectations but should be verified against actual behavior.
  • Impact: Documentation drift risk if behavior changes.
  • Suggested action: Verify the documented behavior matches the test. The documentation appears accurate based on test expectations.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Compare documentation at line 50 with test 'preserves the live DCode sandbox when its registry target drifts after backup' in rebuild-dcode-flow.test.ts.
  • Missing regression test: None - documentation verification
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Documentation lines 50-52 describe pre-backup and post-backup validation; tests 8-11 verify this behavior.

PRA-19 Improvement — Custom/legacy image detection error message could be actionable

  • Location: src/lib/actions/sandbox/rebuild-dcode-preflight.ts:360
  • Category: scope
  • Problem: The error 'the registry has no NemoClaw-managed image fingerprint; custom and legacy images cannot be safely prebuilt and recreated automatically' doesn't tell the user how to migrate. Should suggest 'Run nemoclaw <name> onboard --fresh to migrate to a managed image, or use nemoclaw <name> snapshot create + manual recreate.'
  • Impact: Users with custom/legacy images get unhelpful error message.
  • Suggested action: Improve error message to suggest migration steps for custom/legacy images.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Review fail() call at line 360 in prepareDcodeReplacementBeforeMutation.
  • Missing regression test: None - UX improvement
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Error message at line 360-362 lacks actionable guidance.

PRA-20 Improvement — Test uses single-use constant for OPENSHELL_GATEWAY value

  • Location: src/lib/actions/sandbox/rebuild-dcode-flow.test.ts:86
  • Category: architecture
  • Problem: The test uses 'previous-gateway' as a magic string in multiple tests. Should extract as a constant at top of test file.
  • Impact: Magic string repeated 4 times; harder to maintain if value changes.
  • Suggested action: Extract const PREVIOUS_GATEWAY = 'previous-gateway' at top of test file and reuse.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Search for 'previous-gateway' in rebuild-dcode-flow.test.ts - appears 4 times.
  • Missing regression test: None - test maintainability
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Lines 86, 105, 116, 133, 353, 373 all use 'previous-gateway' literal.

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.

@github-actions

github-actions Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: ubuntu-repo-cloud-langchain-deepagents-code, rebuild-openclaw, onboard-resume, onboard-repair, cloud-onboard, inference-routing
Optional E2E: sandbox-rebuild, state-backup-restore, credential-sanitization

Dispatch hint: onboard-resume,onboard-repair,cloud-onboard,inference-routing,rebuild-openclaw; targets=ubuntu-repo-cloud-langchain-deepagents-code

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • ubuntu-repo-cloud-langchain-deepagents-code (high): Direct live coverage for the changed DCode target. The updated typed target onboards LangChain Deep Agents Code, exercises inference.local and terminal-agent policy suites, and now runs the dcode-rebuild-invalid-credential lifecycle to verify rejected stored credentials fail before backup/delete and leave the sandbox intact.
  • rebuild-openclaw (high): The core rebuild implementation changed. This existing live rebuild job validates the generic backup, delete, recreate, restore, policy reapply, and relock path for a normal OpenClaw sandbox, guarding against regressions outside the DCode-specific branch.
  • onboard-resume (medium): Required by the onboarding resume rule: onboard.ts and prepared DCode rebuild handoff changes affect resume/recreate behavior and session-driven onboarding continuation. Unit tests are not sufficient for these resume state-machine paths.
  • onboard-repair (high): Required with onboard-resume for resume/repair-sensitive onboarding changes. This validates live repair behavior after interrupted onboarding state and protects against prepared rebuild handoff changes corrupting repair recovery.
  • cloud-onboard (high): The onboard.ts and build-context changes can also affect full hosted onboarding, not only rebuild recreation. This job verifies a clean hosted onboarding path with real OpenShell/Docker and hosted inference credentials.
  • inference-routing (medium): The PR adds rebuild preflight probes through inference.local using the stored gateway route. The existing inference-routing E2E should run to confirm the hosted inference routing boundary still works in a live sandbox.

Optional E2E

  • sandbox-rebuild (high): Additional confidence for the user-facing sandbox rebuild command after broad rebuild.ts changes. Useful if time permits, but rebuild-openclaw plus the DCode registry target are the more direct merge-blocking checks.
  • state-backup-restore (medium): Adjacent coverage for backup/restore invariants touched by rebuild preflight boundary changes, especially the guarantee that late failures preserve existing sandbox state.
  • credential-sanitization (medium): The new rebuild inference preflight and invalid-credential lifecycle must not leak provider credentials in command output or artifacts. This is adjacent secret-boundary confidence beyond the direct DCode invalid-credential target.

New E2E recommendations

  • dcode-successful-managed-rebuild (high): Existing live coverage added by this PR focuses on an invalid stored credential failing before destructive work. There is no dedicated live DCode successful managed rebuild E2E that proves the prepared base/context handoff is consumed by OpenShell, the sandbox is recreated, state markers are restored, and inference.local works after rebuild.
    • Suggested test: Add a live typed target or free-standing job for LangChain Deep Agents Code successful managed rebuild with marker preservation and post-rebuild inference.local validation.
  • dcode-late-preflight-failure-after-backup (medium): The new code has important late mutation-edge checks for target drift, route drift, and prepared input drift after backup but before deletion. Unit tests cover these, but there is no live E2E that intentionally triggers a late failure and verifies the old DCode sandbox remains intact while a backup may exist.
    • Suggested test: Add a fault-injection live DCode rebuild lifecycle that fails after backup but before deletion and validates container identity, marker preservation, and relock behavior.

Dispatch hint

  • Workflow: .github/workflows/e2e.yaml
  • jobs input: onboard-resume,onboard-repair,cloud-onboard,inference-routing,rebuild-openclaw; targets=ubuntu-repo-cloud-langchain-deepagents-code

@github-actions

github-actions Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

E2E Target Recommendation

Required E2E targets: e2e-all
Optional E2E targets: None

Dispatch required E2E targets:

  • gh workflow run e2e.yaml --ref <pr-head-ref>

Workflow run

Full E2E target advisor summary

E2E Target Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E targets

  • e2e-all: The PR changes shared E2E target machinery: shared fixtures/phases, the live registry target entrypoint, registry target metadata, runtime support classification, and shared E2E support tests. These surfaces affect target discovery/execution across the registry, so the full E2E target fan-out is required.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref>

Optional E2E targets

  • None.

Relevant changed files

  • test/e2e/fixtures/clients/command.ts
  • test/e2e/fixtures/phases/index.ts
  • test/e2e/fixtures/phases/lifecycle-dcode-invalid-credential.ts
  • test/e2e/fixtures/phases/lifecycle.ts
  • test/e2e/live/registry-targets.test.ts
  • test/e2e/registry/definitions/baseline.ts
  • test/e2e/registry/runtime-support.ts
  • test/e2e/support/e2e-live-registry-discovery.test.ts
  • test/e2e/support/e2e-phase-lifecycle.test.ts

@cv cv added the v0.0.74 label Jul 2, 2026
@wscurran wscurran added area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression integration: dcode LangChain Deep Code integration behavior labels Jul 2, 2026

@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 (1)
src/lib/actions/sandbox/rebuild-flow-helpers.ts (1)

203-225: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

loadAgent failure bypasses the graceful preflight-bail path.

loadAgent(rebuildAgent) (line 208) runs outside the try/catch that wraps ensureAgentBaseImage. If the persisted rebuild agent name doesn't resolve to an installed manifest (a realistic drift scenario this preflight exists to catch), loadAgent throws uncaught instead of hitting the same bail(message) + "Sandbox is untouched — no data was lost." messaging used for every other failure in this function.

🐛 Proposed fix
   if (!rebuildAgent) return { ok: true, imageRef: null, overrideEnvVar: null };
-  const agentDef = loadAgent(rebuildAgent);
-  const overrideEnvVar = `NEMOCLAW_${agentDef.name
-    .toUpperCase()
-    .replace(/[^A-Z0-9]+/g, "_")}_SANDBOX_BASE_IMAGE_REF`;
   try {
+    const agentDef = loadAgent(rebuildAgent);
+    const overrideEnvVar = `NEMOCLAW_${agentDef.name
+      .toUpperCase()
+      .replace(/[^A-Z0-9]+/g, "_")}_SANDBOX_BASE_IMAGE_REF`;
     const result = ensureAgentBaseImage(agentDef, { forceBaseImageRebuild: true });
     return { ok: true, imageRef: result.imageTag, overrideEnvVar };
   } catch (err) {
🤖 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/rebuild-flow-helpers.ts` around lines 203 - 225, The
preflight path in ensureRebuildAgentBaseImage lets loadAgent(rebuildAgent) throw
before the existing graceful bail handling. Move the loadAgent call into the
same try/catch used around ensureAgentBaseImage so any manifest-resolution
failure is converted into the same message flow, including console.error output
and bail(message), with the final untouched-sandbox notice. Keep the
overrideEnvVar derivation and return shape in ensureRebuildAgentBaseImage
unchanged.
🧹 Nitpick comments (12)
docs/manage-sandboxes/lifecycle.mdx (1)

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

Split semicolon-joined sentences and code-format the literal port.

Lines 231, 234, and 235 each join two independent clauses with a semicolon on a single line, and line 231 has the literal port number 8080 outside of code formatting.

✏️ Proposed fix
-LangChain Deep Agents Code has no dashboard; rebuild does not treat its OpenShell gateway listener, including port 8080, as a dashboard conflict.
+LangChain Deep Agents Code has no dashboard.
+Rebuild does not treat its OpenShell gateway listener, including port `8080`, as a dashboard conflict.
-When matching onboarding state records Brave Search as enabled, rebuild also probes that route; a legacy Brave-enabled entry without matching state fails closed.
+When matching onboarding state records Brave Search as enabled, rebuild also probes that route.
+A legacy Brave-enabled entry without matching state fails closed.
-If both CPU and memory values are recorded, rebuild validates and replays both; a partial record fails before deletion, while legacy entries with neither value use OpenShell defaults.
+If both CPU and memory values are recorded, rebuild validates and replays both.
+A partial record fails before deletion, while legacy entries with neither value use OpenShell defaults.

As per path instructions, docs/**/*.{md,mdx} should "Keep one sentence per line in Markdown and MDX source files" and "Use code formatting for commands, paths, flags, environment variables, file names, and literal values."

Also applies to: 234-234, 235-235

🤖 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 `@docs/manage-sandboxes/lifecycle.mdx` at line 231, Split the semicolon-joined
sentences in the affected MDX text so each independent clause is on its own
line, following the one-sentence-per-line rule. In the relevant paragraph about
LangChain Deep Agents Code and its OpenShell gateway listener, ensure the
literal port 8080 is wrapped in code formatting. Keep the wording consistent
while adjusting the prose in the surrounding markdown.

Source: Path instructions

docs/reference/commands.mdx (1)

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

Split semicolon-joined sentences and code-format the literal port.

Lines 1683, 1698, and 1699 each join two independent clauses with a semicolon on one line, and line 1683 has the literal port number 8080 outside of code formatting.

✏️ Proposed fix
-LangChain Deep Agents Code has no dashboard; rebuild does not treat its OpenShell gateway listener, including port 8080, as a dashboard conflict.
+LangChain Deep Agents Code has no dashboard.
+Rebuild does not treat its OpenShell gateway listener, including port `8080`, as a dashboard conflict.
-When matching onboarding state records Brave Search as enabled, rebuild also probes that route; a legacy Brave-enabled entry without matching state fails closed.
+When matching onboarding state records Brave Search as enabled, rebuild also probes that route.
+A legacy Brave-enabled entry without matching state fails closed.
-If both CPU and memory values are recorded, rebuild validates and replays both; a partial record fails before deletion, while legacy entries with neither value use OpenShell defaults.
+If both CPU and memory values are recorded, rebuild validates and replays both.
+A partial record fails before deletion, while legacy entries with neither value use OpenShell defaults.

As per path instructions, docs/**/*.{md,mdx} should "Keep one sentence per line in Markdown and MDX source files" and "Use code formatting for commands, paths, flags, environment variables, file names, and literal values."

Also applies to: 1698-1698, 1699-1699

🤖 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 `@docs/reference/commands.mdx` at line 1683, In commands.mdx, split the
semicolon-joined sentences so each independent clause is on its own line,
following the one-sentence-per-line convention for Markdown/MDX, and update the
LangChain Deep Agents Code/OpenShell gateway mention to use code formatting for
the literal port value 8080. Make the same sentence split cleanup anywhere the
affected text appears in the nearby content so the documentation style stays
consistent.

Source: Path instructions

src/lib/actions/sandbox/rebuild-custom-image-preflight.ts (1)

203-218: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Redundant re-registration of the already-armed exit listener.

cleanupBuildContext is the closure returned by installBuildContextCleanupFallback, which already calls process.on("exit", cleanup) internally (line 105). Re-registering the same function reference here (line 212) creates a duplicate listener entry. In the common (non-interrupt) failure path this duplicate happens to get cleaned up by the interplay of the internal remove() and the outer removeListener in cleanupBuildContextWithExitFallback, but it's fragile and hard to reason about — e.g. if the inline cleanup returns false (cleanup failed), the duplicate listener is never removed and will invoke cleanupBuildCtx() twice on actual process exit.

♻️ Proposed simplification
     if (!retainBuildContext && cleanupBuildContext) {
-      // Keep an exit safety net armed if inline cleanup fails; staged contexts
-      // may contain copied source or secret-bearing build inputs.
-      process.on("exit", cleanupBuildContext);
       try {
         cleanupBuildContextWithExitFallback(cleanupBuildContext);
       } catch {
         // Best effort: preserve the original preflight result.
       }
     }

The exit listener installed by installBuildContextCleanupFallback already serves as the safety net; no need to re-arm it here.

🤖 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/rebuild-custom-image-preflight.ts` around lines 203 -
218, The finally block in rebuild-custom-image-preflight is redundantly
re-adding the exit listener for cleanupBuildContext, even though
installBuildContextCleanupFallback already armed it. Remove the extra
process.on("exit", cleanupBuildContext) call and rely on the existing fallback
managed by cleanupBuildContextWithExitFallback and cleanupBuildContext, keeping
the inline cleanup attempt and best-effort error handling intact.
src/lib/actions/sandbox/rebuild-web-search-preflight.ts (1)

15-26: 🚀 Performance & Scalability | 🔵 Trivial

Live billed API call on every rebuild preflight.

buildRebuildBraveSearchProbeCommand issues a real query against api.search.brave.com (q=ping) each time this preflight runs. Brave Search API is now a metered, credit-based service (roughly $0.003–$0.005 per query), so every rebuild attempt (and any retries) incurs a small real cost. Worth confirming this is an acceptable, intentional tradeoff for the atomicity guarantee, and that retries/backoff on the caller side don't multiply this unexpectedly.

🤖 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/rebuild-web-search-preflight.ts` around lines 15 -
26, The probe built by buildRebuildBraveSearchProbeCommand is making a real
billed Brave Search query on every preflight, so adjust the command or its
caller to avoid repeated paid requests unless this is explicitly intended.
Review the q=ping curl flow in buildRebuildBraveSearchProbeCommand and either
replace it with a non-billed validation path or add throttling/caching/retry
limits around this preflight so rebuild retries do not multiply API costs
unexpectedly.
src/lib/onboard/sandbox-messaging-preflight.ts (1)

101-101: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Newly exported checkMessagingPlanConflicts widens this module's public surface.

Minor note: worth double-checking no other callers outside this cohort need the exported symbol (vs. only tests), to avoid an incidental new public dependency.

🤖 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/sandbox-messaging-preflight.ts` at line 101, The newly
exported checkMessagingPlanConflicts function is expanding the module’s public
API unnecessarily; verify whether it is only needed by local callers and tests,
and if so remove the export or keep the symbol internal to avoid creating an
incidental public dependency.
src/lib/onboard/sandbox-provider-cleanup.ts (1)

54-59: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Two parallel regex families for similar OpenShell output classification.

STRICT_NOT_ATTACHED_OUTPUT_RE/TOLERATED_ATTACH_OUTPUT_RE are new and distinct from the existing TOLERATED_DETACH_OUTPUT_RE used by detachSandboxProviders. If the two are meant to recognize the same OpenShell CLI output shapes (just naming it "strict" for the exact-set path), consider deriving one from the other to avoid the two drifting apart as the CLI's error text evolves.

🤖 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/sandbox-provider-cleanup.ts` around lines 54 - 59, The new
OpenShell output regexes for attach cleanup are duplicating patterns already
handled by the existing detach classification, so they may drift apart over
time. Update the sandbox-provider-cleanup logic around
STRICT_NOT_ATTACHED_OUTPUT_RE and TOLERATED_ATTACH_OUTPUT_RE to reuse the same
underlying matcher structure as TOLERATED_DETACH_OUTPUT_RE, or derive the new
expressions from it, so attach/detach output recognition stays consistent as CLI
text changes.
src/lib/onboard/initial-policy.test.ts (1)

12-24: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add negative-path coverage for the new custom-preset validation.

networkPoliciesHasAllowedIps is stubbed to always return false (Line 24), and no test in this suite exercises the invalid-YAML, missing-network_policies-mapping, or disallowed-allowed_ips throw paths that prepareInitialSandboxCreatePolicy now implements for additionalPresetContents (see initial-policy.ts lines 274-299). These are the new security-boundary checks this PR introduces; without a test that makes the mock return true (or supplies genuinely invalid content), the rejection path is unverified.

✅ Suggested test additions
   mergePresetNamesIntoPolicy: (policy: string, presetNames: string[]) => {
     const missingPresets: string[] = presetNames.filter((name) => name === "missing-preset");
     return {
       policy: `${policy.trimEnd()}\n  slack: {}\n`,
       appliedPresets: presetNames.filter((name) => !missingPresets.includes(name)),
       missingPresets,
     };
   },
-  networkPoliciesHasAllowedIps: () => false,
+  networkPoliciesHasAllowedIps: (np: Record<string, unknown>) =>
+    JSON.stringify(np).includes("allowed_ips"),
+  it("throws when a custom preset contains disallowed allowed_ips", () => {
+    const basePolicyPath = tmpPolicy("version: 1\nnetwork_policies:\n  base: {}\n");
+
+    expect(() =>
+      prepareInitialSandboxCreatePolicy(basePolicyPath, [], {
+        additionalPresetContents: [
+          {
+            name: "unsafe",
+            content: "network_policies:\n  unsafe:\n    allowed_ips: [1.2.3.4]\n",
+          },
+        ],
+      }),
+    ).toThrow("disallowed allowed_ips");
+  });
+
+  it("throws when a custom preset has no network_policies mapping", () => {
+    const basePolicyPath = tmpPolicy("version: 1\nnetwork_policies:\n  base: {}\n");
+
+    expect(() =>
+      prepareInitialSandboxCreatePolicy(basePolicyPath, [], {
+        additionalPresetContents: [{ name: "bad", content: "version: 1\n" }],
+      }),
+    ).toThrow("no valid network_policies mapping");
+  });

As per path instructions, "Flag copied production algorithms, broad mocks that bypass the behavior under test, and conditionals that make a test pass without exercising its claim."

Also applies to: 276-311

🤖 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/initial-policy.test.ts` around lines 12 - 24, Add
negative-path coverage for prepareInitialSandboxCreatePolicy’s custom-preset
validation by exercising the invalid YAML, missing network_policies mapping, and
disallowed allowed_ips rejection paths instead of relying on the broad
networkPoliciesHasAllowedIps stub. Update the tests in initial-policy.test.ts so
at least one case supplies content that makes networkPoliciesHasAllowedIps
return true (or otherwise violates the new checks) and asserts the thrown error
from the additionalPresetContents handling in prepareInitialSandboxCreatePolicy.
Use the existing helpers extractPresetEntries, mergePresetIntoPolicy, and
mergePresetNamesIntoPolicy to keep the test focused on the validation behavior.

Source: Path instructions

src/lib/actions/sandbox/rebuild-gateway-drift.test.ts (1)

30-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract duplicated fixture helpers into shared test-support module.

healthyGatewayRecovery, sandboxGpuConfig, and runtimePreflightResult are re-declared verbatim in this file, rebuild-resume-snapshot.test.ts, and rebuild-shields-finally.test.ts. rebuild-flow.test-support.ts already exists as the shared home for cross-test helpers (e.g. requireDist, originalSandboxName); moving these fixtures there would remove the triplication and keep them in sync going forward.

♻️ Suggested consolidation
+// in rebuild-flow.test-support.ts
+export function healthyGatewayRecovery(gatewayName: string) { ... }
+export const sandboxGpuConfig = { ... };
+export const runtimePreflightResult = { ... };

Then import from the three test files instead of redefining locally.

🤖 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/rebuild-gateway-drift.test.ts` around lines 30 - 53,
The test fixtures are duplicated across multiple rebuild sandbox tests, so move
healthyGatewayRecovery, sandboxGpuConfig, and runtimePreflightResult into the
shared rebuild-flow.test-support.ts module alongside existing helpers like
requireDist and originalSandboxName. Then update rebuild-gateway-drift.test.ts,
rebuild-resume-snapshot.test.ts, and rebuild-shields-finally.test.ts to import
those shared fixtures instead of redefining them locally, keeping the recovery
and preflight setup consistent in one place.
src/lib/actions/sandbox/rebuild-gpu-opt-out.ts (1)

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

Rename the shadowed dashboardPort inside the Hermes block.

The outer dashboardPort (line 141) is the general control/dashboard port; the inner const dashboardPort = args.sb.hermesDashboardPort (line 182) shadows it with a different meaning inside the same function. The code works today (the outer var is correctly referenced later for controlUiPort, and args.sb.dashboardPort is explicitly used for the collision check), but the reused name is a latent trap for future edits.

♻️ Proposed rename
   if (args.rebuildAgent === "hermes" && args.sb?.hermesDashboardEnabled === true) {
-    const dashboardPort = args.sb.hermesDashboardPort;
-    const internalPort = args.sb.hermesDashboardInternalPort;
-    if (dashboardPort == null || internalPort == null) {
+    const hermesDashboardPort = args.sb.hermesDashboardPort;
+    const internalPort = args.sb.hermesDashboardInternalPort;
+    if (hermesDashboardPort == null || internalPort == null) {
       throw new Error("Cannot recreate an enabled Hermes dashboard without its persisted ports.");
     }
     authoritativeHermesDashboardConfig = readHermesDashboardConfig({
       [HERMES_DASHBOARD_ENABLE_ENV]: "1",
-      [HERMES_DASHBOARD_PORT_ENV]: String(dashboardPort),
+      [HERMES_DASHBOARD_PORT_ENV]: String(hermesDashboardPort),
       [HERMES_DASHBOARD_INTERNAL_PORT_ENV]: String(internalPort),
       ...(args.sb.hermesDashboardTui ? { [HERMES_DASHBOARD_TUI_ENV]: "1" } : {}),
     });
     if (
-      dashboardPort === internalPort ||
-      dashboardPort === HERMES_OPENAI_API_PORT ||
+      hermesDashboardPort === internalPort ||
+      hermesDashboardPort === HERMES_OPENAI_API_PORT ||
       internalPort === HERMES_OPENAI_API_PORT ||
       (args.sb.dashboardPort != null &&
-        (dashboardPort === args.sb.dashboardPort || internalPort === args.sb.dashboardPort))
+        (hermesDashboardPort === args.sb.dashboardPort || internalPort === args.sb.dashboardPort))
     ) {
       throw new Error("Invalid persisted Hermes dashboard port configuration.");
     }
   }

Also applies to: 180-202

🤖 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/rebuild-gpu-opt-out.ts` at line 141, Rename the inner
dashboardPort declared in the Hermes block of rebuild-gpu-opt-out.ts so it does
not shadow the outer dashboardPort from the same function. Update the
Hermes-specific variable near args.sb.hermesDashboardPort to a distinct name and
adjust its uses within that block, while leaving the outer control/dashboard
port and the later controlUiPort logic unchanged.
src/lib/onboard/machine/handlers/provider-inference.test.ts (1)

169-184: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider also asserting the "inference" step skip.

The production path (per handleProviderInferenceState) also calls deps.skippedStepMessage("inference", ...) and recordStateSkipped("inference", ...). Asserting on that too would close a small coverage gap where a regression in the second skip could go undetected.

🤖 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/provider-inference.test.ts` around lines 169
- 184, The provider-inference test currently asserts the provider_selection skip
but misses the inference skip. Update provider-inference.test.ts to also verify
the production flow in handleProviderInferenceState triggers
deps.skippedStepMessage("inference", ...) and recordStateSkipped("inference",
...) alongside the existing skipped assertion, using the existing calls.skipped
and result expectations to locate the test case.

Source: Path instructions

src/lib/onboard/fatal-runtime-preflight.ts (1)

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

Repeated default-exitProcess boilerplate across files.

(code: number) => never = (code) => process.exit(code) (or the equivalent named exitProcessByDefault) is duplicated verbatim in this file, sandbox-gpu-preflight.ts, and bridge-dns-preflight.ts (per this cohort alone). Extracting a single shared defaultExitProcess into a small utility module would remove the repetition and give one place to change behavior (e.g. flushing logs before exit) across all injectable-exit call sites.

♻️ Suggested shared helper
// src/lib/onboard/exit-process.ts
export const defaultExitProcess = (code: number): never => process.exit(code);

Also applies to: 86-95, 132-135, 292-295

🤖 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/fatal-runtime-preflight.ts` at line 51, The default
exit-process implementation is duplicated in fatal-runtime-preflight and the
other preflight files, so replace the local exitProcessByDefault / inline
`(code: number) => process.exit(code)` boilerplate with a shared utility such as
defaultExitProcess from a new exit-process module. Update the injectable-exit
call sites in fatal-runtime-preflight to import and use that shared helper so
all preflights share one behavior source and can be changed centrally later.
src/lib/onboard/machine/handlers/preflight.test.ts (1)

269-305: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test doesn't cover the persisted step-status outcome.

This test checks that GPU-detection/validation calls are skipped and that the returned values match the authoritative inputs, but it never asserts what recordStateSkipped/recordStepComplete are called with, nor the resulting session step status. Given the implementation calls both functions back-to-back for the same step (see preflight.ts comment), a test asserting the final persisted session.steps.preflight shape would catch regressions in that sequencing.

As per path instructions for test files: "Prefer observable outcomes through the public boundary over source-text, private-shape, or mock-call assertions."

🤖 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/preflight.test.ts` around lines 269 - 305,
Add coverage for the persisted preflight step outcome in this test: in
handlePreflightState, the resume path with authoritativePreflight should assert
the final session step status produced by recordStateSkipped and
recordStepComplete, not just the skipped runtime behavior. Update the test
around skippedStepMessage to verify the resulting session.steps.preflight shape
and the exact skipped/completed transition for the preflight step, using the
handlePreflightState and session persistence helpers as the observable boundary.

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/rebuild-flow-helpers.ts`:
- Around line 203-225: The preflight path in ensureRebuildAgentBaseImage lets
loadAgent(rebuildAgent) throw before the existing graceful bail handling. Move
the loadAgent call into the same try/catch used around ensureAgentBaseImage so
any manifest-resolution failure is converted into the same message flow,
including console.error output and bail(message), with the final
untouched-sandbox notice. Keep the overrideEnvVar derivation and return shape in
ensureRebuildAgentBaseImage unchanged.

---

Nitpick comments:
In `@docs/manage-sandboxes/lifecycle.mdx`:
- Line 231: Split the semicolon-joined sentences in the affected MDX text so
each independent clause is on its own line, following the one-sentence-per-line
rule. In the relevant paragraph about LangChain Deep Agents Code and its
OpenShell gateway listener, ensure the literal port 8080 is wrapped in code
formatting. Keep the wording consistent while adjusting the prose in the
surrounding markdown.

In `@docs/reference/commands.mdx`:
- Line 1683: In commands.mdx, split the semicolon-joined sentences so each
independent clause is on its own line, following the one-sentence-per-line
convention for Markdown/MDX, and update the LangChain Deep Agents Code/OpenShell
gateway mention to use code formatting for the literal port value 8080. Make the
same sentence split cleanup anywhere the affected text appears in the nearby
content so the documentation style stays consistent.

In `@src/lib/actions/sandbox/rebuild-custom-image-preflight.ts`:
- Around line 203-218: The finally block in rebuild-custom-image-preflight is
redundantly re-adding the exit listener for cleanupBuildContext, even though
installBuildContextCleanupFallback already armed it. Remove the extra
process.on("exit", cleanupBuildContext) call and rely on the existing fallback
managed by cleanupBuildContextWithExitFallback and cleanupBuildContext, keeping
the inline cleanup attempt and best-effort error handling intact.

In `@src/lib/actions/sandbox/rebuild-gateway-drift.test.ts`:
- Around line 30-53: The test fixtures are duplicated across multiple rebuild
sandbox tests, so move healthyGatewayRecovery, sandboxGpuConfig, and
runtimePreflightResult into the shared rebuild-flow.test-support.ts module
alongside existing helpers like requireDist and originalSandboxName. Then update
rebuild-gateway-drift.test.ts, rebuild-resume-snapshot.test.ts, and
rebuild-shields-finally.test.ts to import those shared fixtures instead of
redefining them locally, keeping the recovery and preflight setup consistent in
one place.

In `@src/lib/actions/sandbox/rebuild-gpu-opt-out.ts`:
- Line 141: Rename the inner dashboardPort declared in the Hermes block of
rebuild-gpu-opt-out.ts so it does not shadow the outer dashboardPort from the
same function. Update the Hermes-specific variable near
args.sb.hermesDashboardPort to a distinct name and adjust its uses within that
block, while leaving the outer control/dashboard port and the later
controlUiPort logic unchanged.

In `@src/lib/actions/sandbox/rebuild-web-search-preflight.ts`:
- Around line 15-26: The probe built by buildRebuildBraveSearchProbeCommand is
making a real billed Brave Search query on every preflight, so adjust the
command or its caller to avoid repeated paid requests unless this is explicitly
intended. Review the q=ping curl flow in buildRebuildBraveSearchProbeCommand and
either replace it with a non-billed validation path or add
throttling/caching/retry limits around this preflight so rebuild retries do not
multiply API costs unexpectedly.

In `@src/lib/onboard/fatal-runtime-preflight.ts`:
- Line 51: The default exit-process implementation is duplicated in
fatal-runtime-preflight and the other preflight files, so replace the local
exitProcessByDefault / inline `(code: number) => process.exit(code)` boilerplate
with a shared utility such as defaultExitProcess from a new exit-process module.
Update the injectable-exit call sites in fatal-runtime-preflight to import and
use that shared helper so all preflights share one behavior source and can be
changed centrally later.

In `@src/lib/onboard/initial-policy.test.ts`:
- Around line 12-24: Add negative-path coverage for
prepareInitialSandboxCreatePolicy’s custom-preset validation by exercising the
invalid YAML, missing network_policies mapping, and disallowed allowed_ips
rejection paths instead of relying on the broad networkPoliciesHasAllowedIps
stub. Update the tests in initial-policy.test.ts so at least one case supplies
content that makes networkPoliciesHasAllowedIps return true (or otherwise
violates the new checks) and asserts the thrown error from the
additionalPresetContents handling in prepareInitialSandboxCreatePolicy. Use the
existing helpers extractPresetEntries, mergePresetIntoPolicy, and
mergePresetNamesIntoPolicy to keep the test focused on the validation behavior.

In `@src/lib/onboard/machine/handlers/preflight.test.ts`:
- Around line 269-305: Add coverage for the persisted preflight step outcome in
this test: in handlePreflightState, the resume path with authoritativePreflight
should assert the final session step status produced by recordStateSkipped and
recordStepComplete, not just the skipped runtime behavior. Update the test
around skippedStepMessage to verify the resulting session.steps.preflight shape
and the exact skipped/completed transition for the preflight step, using the
handlePreflightState and session persistence helpers as the observable boundary.

In `@src/lib/onboard/machine/handlers/provider-inference.test.ts`:
- Around line 169-184: The provider-inference test currently asserts the
provider_selection skip but misses the inference skip. Update
provider-inference.test.ts to also verify the production flow in
handleProviderInferenceState triggers deps.skippedStepMessage("inference", ...)
and recordStateSkipped("inference", ...) alongside the existing skipped
assertion, using the existing calls.skipped and result expectations to locate
the test case.

In `@src/lib/onboard/sandbox-messaging-preflight.ts`:
- Line 101: The newly exported checkMessagingPlanConflicts function is expanding
the module’s public API unnecessarily; verify whether it is only needed by local
callers and tests, and if so remove the export or keep the symbol internal to
avoid creating an incidental public dependency.

In `@src/lib/onboard/sandbox-provider-cleanup.ts`:
- Around line 54-59: The new OpenShell output regexes for attach cleanup are
duplicating patterns already handled by the existing detach classification, so
they may drift apart over time. Update the sandbox-provider-cleanup logic around
STRICT_NOT_ATTACHED_OUTPUT_RE and TOLERATED_ATTACH_OUTPUT_RE to reuse the same
underlying matcher structure as TOLERATED_DETACH_OUTPUT_RE, or derive the new
expressions from it, so attach/detach output recognition stays consistent as CLI
text changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 75b55c65-e50f-4817-92c4-165510174c2b

📥 Commits

Reviewing files that changed from the base of the PR and between 55f8fb9 and 7da39a1.

📒 Files selected for processing (84)
  • ci/env-var-doc-allowlist.json
  • docs/manage-sandboxes/lifecycle.mdx
  • docs/reference/commands-nemohermes.mdx
  • docs/reference/commands.mdx
  • src/lib/actions/sandbox/rebuild-atomic-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-custom-image-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-custom-image-preflight.ts
  • src/lib/actions/sandbox/rebuild-env-isolation.test.ts
  • src/lib/actions/sandbox/rebuild-env-isolation.ts
  • src/lib/actions/sandbox/rebuild-flow-helpers.ts
  • src/lib/actions/sandbox/rebuild-flow.test-support.ts
  • src/lib/actions/sandbox/rebuild-flow.test.ts
  • src/lib/actions/sandbox/rebuild-gateway-drift.test.ts
  • src/lib/actions/sandbox/rebuild-gpu-opt-out.test.ts
  • src/lib/actions/sandbox/rebuild-gpu-opt-out.ts
  • src/lib/actions/sandbox/rebuild-inference-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-inference-preflight.ts
  • src/lib/actions/sandbox/rebuild-resume-config.test.ts
  • src/lib/actions/sandbox/rebuild-resume-config.ts
  • src/lib/actions/sandbox/rebuild-resume-snapshot.test.ts
  • src/lib/actions/sandbox/rebuild-shields-finally.test.ts
  • src/lib/actions/sandbox/rebuild-web-search-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-web-search-preflight.ts
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/gateway-runtime-action.ts
  • src/lib/inference/selection.ts
  • src/lib/onboard.ts
  • src/lib/onboard/authoritative-rebuild-target.test.ts
  • src/lib/onboard/authoritative-rebuild-target.ts
  • src/lib/onboard/branding.ts
  • src/lib/onboard/bridge-dns-preflight-exit.test.ts
  • src/lib/onboard/bridge-dns-preflight.ts
  • src/lib/onboard/build-context-stage.ts
  • src/lib/onboard/docker-driver-gateway-env.test.ts
  • src/lib/onboard/docker-driver-gateway-env.ts
  • src/lib/onboard/docker-driver-gateway-runtime.test.ts
  • src/lib/onboard/docker-driver-gateway-runtime.ts
  • src/lib/onboard/docker-gpu-local-inference.ts
  • src/lib/onboard/exit-step-failure.ts
  • src/lib/onboard/fatal-runtime-preflight.test.ts
  • src/lib/onboard/fatal-runtime-preflight.ts
  • src/lib/onboard/gateway-binding.test.ts
  • src/lib/onboard/gateway-binding.ts
  • src/lib/onboard/gateway-reuse.test.ts
  • src/lib/onboard/gateway-reuse.ts
  • src/lib/onboard/gateway-sandbox-reachability.test.ts
  • src/lib/onboard/gateway-sandbox-reachability.ts
  • src/lib/onboard/initial-policy.test.ts
  • src/lib/onboard/initial-policy.ts
  • src/lib/onboard/machine/core-flow-phases.ts
  • src/lib/onboard/machine/handlers/gateway.test.ts
  • src/lib/onboard/machine/handlers/gateway.ts
  • src/lib/onboard/machine/handlers/preflight.test.ts
  • src/lib/onboard/machine/handlers/preflight.ts
  • src/lib/onboard/machine/handlers/provider-inference.test.ts
  • src/lib/onboard/machine/handlers/provider-inference.ts
  • src/lib/onboard/machine/initial-flow-phases.ts
  • src/lib/onboard/messaging-prep.test.ts
  • src/lib/onboard/messaging-prep.ts
  • src/lib/onboard/onboard-exit-handler.ts
  • src/lib/onboard/preflight-cdi.test.ts
  • src/lib/onboard/preflight.ts
  • src/lib/onboard/resource-profile-selection.test.ts
  • src/lib/onboard/resource-profile-selection.ts
  • src/lib/onboard/sandbox-dockerfile-patch-flow.test.ts
  • src/lib/onboard/sandbox-dockerfile-patch-flow.ts
  • src/lib/onboard/sandbox-gpu-preflight.test.ts
  • src/lib/onboard/sandbox-gpu-preflight.ts
  • src/lib/onboard/sandbox-messaging-preflight.test.ts
  • src/lib/onboard/sandbox-messaging-preflight.ts
  • src/lib/onboard/sandbox-provider-cleanup.ts
  • src/lib/onboard/sandbox-registration.test.ts
  • src/lib/onboard/sandbox-registration.ts
  • src/lib/resources-cmd.test.ts
  • src/lib/resources-cmd.ts
  • src/lib/state/registry.ts
  • test/gateway-state-reconcile-2276.test.ts
  • test/onboard-exit-handler.test.ts
  • test/rebuild-credential-preflight.test.ts
  • test/rebuild-shields-auto-unlock.test.ts
  • test/rebuild-stale-recovery.test.ts
  • test/registry.test.ts
  • test/repro-2201.test.ts
  • test/sandbox-provider-cleanup.test.ts

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Vitest E2E Target Results — ❌ Some jobs failed

Run: 28629810289
Workflow ref: codex/fix-6195-atomic-dcode-rebuild
Requested targets: (selector rejected by workflow validation)
Requested jobs: (selector rejected by workflow validation)
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
generate-matrix ❌ failure

Failed jobs: generate-matrix. Check run artifacts for logs.

@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

🧹 Nitpick comments (5)
test/rebuild-credential-preflight.test.ts (1)

261-279: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Fake sandbox exec hardcodes the --name flag position and command-substring matching.

This is reasonable for a hermetic fake, but the implementation implicitly assumes the production rebuild/exec path always passes --name <sandbox> to openshell sandbox exec and that the probe/marker commands are recognizable purely via substring matching (rebuild-atomicity-marker.txt, https://inference.local/). If the real invocation format changes (flag order, alternate flag name, or command wrapping), this fixture could silently start returning wrong exit codes rather than failing loudly, masking a regression instead of catching it.

As per path instructions for **/*.test.{ts,js,mts,mjs,cts,cjs}, flag broad mocks/fakes whose internal logic embeds assumptions about the shape of the production call rather than only its externally observable inputs/outputs.

🤖 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 `@test/rebuild-credential-preflight.test.ts` around lines 261 - 279, The fake
sandbox exec handler in rebuild-credential-preflight.test.ts is too tightly
coupled to the current command shape. Update the test stub around the sandbox
exec branch to validate only the observable inputs it truly needs, and avoid
relying on fixed `--name` position or substring-only command detection in the
fake. Keep the logic localized to the sandbox exec mock and the related
marker/probe handling so the fixture fails clearly if the production invocation
format changes.

Source: Path instructions

src/lib/actions/sandbox/rebuild-gpu-opt-out.ts (1)

214-216: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Shallow copy, not deep — verify CustomPolicyEntry has no nested mutable fields.

{ ...policy } only clones top-level properties. The line-range summary describes this as "deep-copied", which doesn't match the code. If policy entries hold nested arrays/objects (rules, matchers, etc.), the "authoritative" copy still aliases those nested references to the original persisted entry, risking unintended mutation of source state during rebuild.

🔧 Proposed fix (if nested structures exist)
     authoritativeCustomPolicies: Array.isArray(args.sb?.customPolicies)
-      ? args.sb.customPolicies.map((policy) => ({ ...policy }))
+      ? structuredClone(args.sb.customPolicies)
       : [],
🤖 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/rebuild-gpu-opt-out.ts` around lines 214 - 216, The
authoritativeCustomPolicies copy in rebuild-gpu-opt-out is only a shallow clone
via the map spread, so verify whether CustomPolicyEntry contains any nested
mutable fields such as rules or matchers. If it does, update the rebuild path to
perform a true deep copy for each policy entry in the
authoritativeCustomPolicies mapping, or otherwise use an explicit cloning
strategy in rebuild-gpu-opt-out to avoid aliasing nested state from
args.sb.customPolicies.
src/lib/onboard/docker-driver-gateway-start.test.ts (1)

131-153: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Coverage gap: fresh-start/spawn/poll path untested.

All four scenarios exit early via reuse/adopt/drift-restart-throw/missing-binary. None exercise the actual fresh-start path (spawn, writeDockerDriverGatewayRuntimeMarkerForStateDir, health-poll loop) or a drift-restart that succeeds and falls through to the port-check/reuse logic below it. Given this module now centralizes the rebuild's runtime start orchestration, that path deserves direct coverage.

🤖 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/docker-driver-gateway-start.test.ts` around lines 131 - 153,
Add test coverage for the unexercised fresh-start flow in
docker-driver-gateway-start.test.ts by driving the start path through spawn,
writeDockerDriverGatewayRuntimeMarkerForStateDir, and the health-poll loop, not
just the early-return cases. Use createHarness and start to assert the
fresh-start orchestration when no recorded/adoptable PID is available, and add a
case where restartForDrift succeeds so execution continues into the subsequent
port-check/reuse logic instead of throwing.
src/lib/onboard/authoritative-rebuild-messaging.ts (1)

40-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate sync/async gateway-scoping helpers.

withTargetGateway and withTargetGatewayAsync are copy-pasted except for await/Promise handling, and the same restore-on-finally pattern is repeated a third time in the sibling test file (which is currently tripping a CI guardrail). Extracting one shared restoreEnvVar/scoping primitive would remove the duplication here and unblock the test-file guardrail failure in one change.

♻️ Proposed consolidation
-function withTargetGateway<T>(targetGatewayName: string, action: () => T): T {
-  const previousGateway = process.env.OPENSHELL_GATEWAY;
-  process.env.OPENSHELL_GATEWAY = targetGatewayName;
-  try {
-    return action();
-  } finally {
-    if (previousGateway === undefined) delete process.env.OPENSHELL_GATEWAY;
-    else process.env.OPENSHELL_GATEWAY = previousGateway;
-  }
-}
-
-async function withTargetGatewayAsync<T>(
-  targetGatewayName: string,
-  action: () => Promise<T>,
-): Promise<T> {
-  const previousGateway = process.env.OPENSHELL_GATEWAY;
-  process.env.OPENSHELL_GATEWAY = targetGatewayName;
-  try {
-    return await action();
-  } finally {
-    if (previousGateway === undefined) delete process.env.OPENSHELL_GATEWAY;
-    else process.env.OPENSHELL_GATEWAY = previousGateway;
-  }
-}
+function restoreEnvVar(name: string, previousValue: string | undefined): void {
+  if (previousValue === undefined) delete process.env[name];
+  else process.env[name] = previousValue;
+}
+
+function withTargetGateway<T>(targetGatewayName: string, action: () => T): T {
+  const previousGateway = process.env.OPENSHELL_GATEWAY;
+  process.env.OPENSHELL_GATEWAY = targetGatewayName;
+  try {
+    return action();
+  } finally {
+    restoreEnvVar("OPENSHELL_GATEWAY", previousGateway);
+  }
+}
+
+async function withTargetGatewayAsync<T>(
+  targetGatewayName: string,
+  action: () => Promise<T>,
+): Promise<T> {
+  const previousGateway = process.env.OPENSHELL_GATEWAY;
+  process.env.OPENSHELL_GATEWAY = targetGatewayName;
+  try {
+    return await action();
+  } finally {
+    restoreEnvVar("OPENSHELL_GATEWAY", previousGateway);
+  }
+}
🤖 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/authoritative-rebuild-messaging.ts` around lines 40 - 63, The
gateway-scoping logic is duplicated across withTargetGateway,
withTargetGatewayAsync, and the sibling test helper, and the repeated
restore-on-finally pattern is what is triggering the CI guardrail. Extract a
shared environment-variable scoping/restore helper (for example, a single
restoreEnvVar or generic wrapper used by both withTargetGateway and
withTargetGatewayAsync), then update these helpers and the test-file copy to
call that shared primitive so the OPENSHELL_GATEWAY setup/teardown lives in one
place.
src/lib/actions/sandbox/rebuild-flow-helpers.test.ts (1)

10-34: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor: duplicated dynamic-require loader scaffolding.

This file re-implements its own requireDist/loadAgentDefs/loadAgentOnboard helpers, while a sibling rebuild-flow.test-support.ts already exposes an equivalent requireDist. Consider consolidating into the shared test-support module to avoid two independently-maintained dynamic-require entry points drifting apart.

🤖 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/rebuild-flow-helpers.test.ts` around lines 10 - 34,
The test file has duplicated dynamic-require loader scaffolding, including
requireDist, loadAgentDefs, and loadAgentOnboard, which should be consolidated
with the shared rebuild-flow.test-support helper. Update the
rebuild-flow-helpers test to import and reuse the existing shared
requireDist/exported loader utilities instead of maintaining its own entry
points, and keep the unique symbols loadRebuildFlowHelpers, loadAgentDefs, and
loadAgentOnboard aligned with the shared module.
🤖 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/authoritative-rebuild-messaging.test.ts`:
- Around line 72-76: The `afterEach` block in
`authoritative-rebuild-messaging.test.ts` adds a new inline conditional that
triggers the `test-conditionals:scan` guardrail. Remove the `if/else` from the
test by using a shared env restore helper, and reuse that helper in the
source-side gateway restore paths as well. Locate the cleanup logic around
`afterEach`, `withTargetGateway`, and `withTargetGatewayAsync`, and centralize
the previous-value restore behavior so the test file no longer contains the
conditional.

In `@src/lib/onboard/authoritative-rebuild-runtime.ts`:
- Around line 152-173: The `authoritativeRebuildRuntime` flow mutates
`env.OPENSHELL_LOCAL_TLS_DIR` before the readiness checks, but only
`deps.setRuntimeState(previous)` is restored in the `finally`, so the TLS
directory can leak after a failure. Update the `authoritativeRebuildRuntime`
cleanup to capture the prior `OPENSHELL_LOCAL_TLS_DIR` value and restore it in
the same `finally` block alongside the runtime-state rollback, and add/update a
test in `authoritative-rebuild-runtime.test.ts` to verify the env value is
restored when the transport-rejection path fails.

---

Nitpick comments:
In `@src/lib/actions/sandbox/rebuild-flow-helpers.test.ts`:
- Around line 10-34: The test file has duplicated dynamic-require loader
scaffolding, including requireDist, loadAgentDefs, and loadAgentOnboard, which
should be consolidated with the shared rebuild-flow.test-support helper. Update
the rebuild-flow-helpers test to import and reuse the existing shared
requireDist/exported loader utilities instead of maintaining its own entry
points, and keep the unique symbols loadRebuildFlowHelpers, loadAgentDefs, and
loadAgentOnboard aligned with the shared module.

In `@src/lib/actions/sandbox/rebuild-gpu-opt-out.ts`:
- Around line 214-216: The authoritativeCustomPolicies copy in
rebuild-gpu-opt-out is only a shallow clone via the map spread, so verify
whether CustomPolicyEntry contains any nested mutable fields such as rules or
matchers. If it does, update the rebuild path to perform a true deep copy for
each policy entry in the authoritativeCustomPolicies mapping, or otherwise use
an explicit cloning strategy in rebuild-gpu-opt-out to avoid aliasing nested
state from args.sb.customPolicies.

In `@src/lib/onboard/authoritative-rebuild-messaging.ts`:
- Around line 40-63: The gateway-scoping logic is duplicated across
withTargetGateway, withTargetGatewayAsync, and the sibling test helper, and the
repeated restore-on-finally pattern is what is triggering the CI guardrail.
Extract a shared environment-variable scoping/restore helper (for example, a
single restoreEnvVar or generic wrapper used by both withTargetGateway and
withTargetGatewayAsync), then update these helpers and the test-file copy to
call that shared primitive so the OPENSHELL_GATEWAY setup/teardown lives in one
place.

In `@src/lib/onboard/docker-driver-gateway-start.test.ts`:
- Around line 131-153: Add test coverage for the unexercised fresh-start flow in
docker-driver-gateway-start.test.ts by driving the start path through spawn,
writeDockerDriverGatewayRuntimeMarkerForStateDir, and the health-poll loop, not
just the early-return cases. Use createHarness and start to assert the
fresh-start orchestration when no recorded/adoptable PID is available, and add a
case where restartForDrift succeeds so execution continues into the subsequent
port-check/reuse logic instead of throwing.

In `@test/rebuild-credential-preflight.test.ts`:
- Around line 261-279: The fake sandbox exec handler in
rebuild-credential-preflight.test.ts is too tightly coupled to the current
command shape. Update the test stub around the sandbox exec branch to validate
only the observable inputs it truly needs, and avoid relying on fixed `--name`
position or substring-only command detection in the fake. Keep the logic
localized to the sandbox exec mock and the related marker/probe handling so the
fixture fails clearly if the production invocation format changes.
🪄 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: 50f2573a-842c-4dcf-bcff-7ac56638075d

📥 Commits

Reviewing files that changed from the base of the PR and between 7da39a1 and c841ec6.

📒 Files selected for processing (37)
  • docs/manage-sandboxes/lifecycle.mdx
  • docs/reference/commands-nemohermes.mdx
  • docs/reference/commands.mdx
  • scripts/checks/run.ts
  • src/lib/actions/sandbox/rebuild-atomic-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-custom-image-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-custom-image-preflight.ts
  • src/lib/actions/sandbox/rebuild-env-isolation.test.ts
  • src/lib/actions/sandbox/rebuild-flow-helpers.test.ts
  • src/lib/actions/sandbox/rebuild-flow-helpers.ts
  • src/lib/actions/sandbox/rebuild-flow.test-support.ts
  • src/lib/actions/sandbox/rebuild-flow.test.ts
  • src/lib/actions/sandbox/rebuild-gateway-drift.test.ts
  • src/lib/actions/sandbox/rebuild-gpu-opt-out.ts
  • src/lib/actions/sandbox/rebuild-shields-finally.test.ts
  • src/lib/actions/sandbox/rebuild-signal-cleanup.test.ts
  • src/lib/actions/sandbox/rebuild-signal-cleanup.ts
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/onboard.ts
  • src/lib/onboard/authoritative-rebuild-messaging.test.ts
  • src/lib/onboard/authoritative-rebuild-messaging.ts
  • src/lib/onboard/authoritative-rebuild-policy.ts
  • src/lib/onboard/authoritative-rebuild-runtime.test.ts
  • src/lib/onboard/authoritative-rebuild-runtime.ts
  • src/lib/onboard/docker-driver-gateway-start.test.ts
  • src/lib/onboard/docker-driver-gateway-start.ts
  • src/lib/onboard/gateway-bound-operations.test.ts
  • src/lib/onboard/gateway-bound-operations.ts
  • src/lib/onboard/gateway-health-wait.ts
  • src/lib/onboard/gateway-start-failure.ts
  • src/lib/onboard/initial-policy.test.ts
  • src/lib/onboard/machine/handlers/provider-inference.test.ts
  • src/lib/onboard/machine/handlers/provider-inference.ts
  • src/lib/onboard/options.ts
  • src/lib/onboard/policy-selection-runtime.ts
  • test/no-direct-credential-env.test.ts
  • test/rebuild-credential-preflight.test.ts
✅ Files skipped from review due to trivial changes (2)
  • src/lib/onboard/options.ts
  • docs/reference/commands.mdx
🚧 Files skipped from review as they are similar to previous changes (13)
  • src/lib/onboard/initial-policy.test.ts
  • src/lib/onboard/machine/handlers/provider-inference.test.ts
  • src/lib/actions/sandbox/rebuild-env-isolation.test.ts
  • docs/manage-sandboxes/lifecycle.mdx
  • src/lib/onboard/machine/handlers/provider-inference.ts
  • src/lib/actions/sandbox/rebuild-atomic-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-flow-helpers.ts
  • src/lib/actions/sandbox/rebuild-custom-image-preflight.ts
  • src/lib/actions/sandbox/rebuild-shields-finally.test.ts
  • src/lib/actions/sandbox/rebuild-flow.test-support.ts
  • src/lib/actions/sandbox/rebuild-gateway-drift.test.ts
  • src/lib/actions/sandbox/rebuild-flow.test.ts
  • src/lib/onboard.ts

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Vitest E2E Target Results — ✅ All selected jobs passed

Run: 28630986293
Workflow ref: codex/fix-6195-atomic-dcode-rebuild
Requested targets: ubuntu-repo-cloud-langchain-deepagents-code
Requested jobs: (default — all default-enabled free-standing jobs; explicit-only jobs hermes-gpu-startup, openshell-gateway-auth-contract, jetson-nvmap-gpu, and sandbox-rlimits-connect are skipped unless selected)
Summary: 1 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
live ✅ success

@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

🧹 Nitpick comments (1)
src/lib/actions/sandbox/rebuild-custom-image-preflight.test.ts (1)

17-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: hoist the duplicated restoreEnv helper into shared test-support.

This identical 3-line helper is copied across several rebuild/onboard test files (e.g. rebuild-flow.test.ts, bridge-dns-preflight-exit.test.ts, authoritative-rebuild-*.test.ts). Consider exporting it once from a shared test-support module to keep env-restore semantics consistent.

🤖 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/rebuild-custom-image-preflight.test.ts` around lines
17 - 20, The duplicated restoreEnv helper used in rebuild/onboard tests should
be centralized into shared test-support so env restoration stays consistent
across files. Move the shared logic out of
rebuild-custom-image-preflight.test.ts into a common exported helper in the
test-support area, then update rebuild-flow.test.ts,
bridge-dns-preflight-exit.test.ts, and the authoritative-rebuild tests to import
and use it instead of copying the same implementation.
🤖 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/sandbox/rebuild-flow.test.ts`:
- Around line 1259-1263: The sandbox list test stub currently calls the real
process.exit(1), which can kill the Vitest worker instead of letting the
assertion fail. In rebuild-flow.test.ts, update the harness used around
harness.captureOpenshellSpy so process.exit is stubbed/spied to throw or
otherwise remain test-safe before the sandbox list branch is exercised. Keep the
fix scoped to the test harness setup so the sandbox list path can be asserted
without exiting the test process.

In `@src/lib/actions/sandbox/rebuild-provider-detach-transaction.ts`:
- Around line 76-86: The signal cleanup path in rollbackForSignal only calls
options.relock() and rollback(), so SIGINT/SIGTERM can be swallowed and leave
the CLI running. Update rebuild-provider-detach-transaction’s signal handlers so
onSigint and onSigterm perform the existing cleanup and then re-raise the same
signal or terminate with the corresponding exit behavior, matching the rebuild
signal-recovery pattern while keeping the onExit cleanup intact.

---

Nitpick comments:
In `@src/lib/actions/sandbox/rebuild-custom-image-preflight.test.ts`:
- Around line 17-20: The duplicated restoreEnv helper used in rebuild/onboard
tests should be centralized into shared test-support so env restoration stays
consistent across files. Move the shared logic out of
rebuild-custom-image-preflight.test.ts into a common exported helper in the
test-support area, then update rebuild-flow.test.ts,
bridge-dns-preflight-exit.test.ts, and the authoritative-rebuild tests to import
and use it instead of copying the same implementation.
🪄 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: 3664f26a-3664-4cb7-af95-a9e2c0993e46

📥 Commits

Reviewing files that changed from the base of the PR and between c841ec6 and b7ceaf5.

📒 Files selected for processing (14)
  • src/lib/actions/sandbox/rebuild-atomic-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-custom-image-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-custom-image-preflight.ts
  • src/lib/actions/sandbox/rebuild-flow.test.ts
  • src/lib/actions/sandbox/rebuild-inference-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-provider-detach-transaction.ts
  • src/lib/actions/sandbox/rebuild-signal-cleanup.test.ts
  • src/lib/actions/sandbox/rebuild-web-search-preflight.test.ts
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/onboard/authoritative-rebuild-messaging.test.ts
  • src/lib/onboard/authoritative-rebuild-runtime.test.ts
  • src/lib/onboard/authoritative-rebuild-runtime.ts
  • src/lib/onboard/authoritative-rebuild-target.test.ts
  • src/lib/onboard/bridge-dns-preflight-exit.test.ts
✅ Files skipped from review due to trivial changes (1)
  • src/lib/actions/sandbox/rebuild-web-search-preflight.test.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/lib/actions/sandbox/rebuild-inference-preflight.test.ts
  • src/lib/onboard/authoritative-rebuild-runtime.test.ts
  • src/lib/actions/sandbox/rebuild-signal-cleanup.test.ts
  • src/lib/onboard/authoritative-rebuild-target.test.ts
  • src/lib/actions/sandbox/rebuild-atomic-preflight.test.ts
  • src/lib/onboard/authoritative-rebuild-messaging.test.ts
  • src/lib/actions/sandbox/rebuild-custom-image-preflight.ts
  • src/lib/onboard/authoritative-rebuild-runtime.ts
  • src/lib/onboard/bridge-dns-preflight-exit.test.ts

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Vitest E2E Target Results — ❌ Some jobs failed

Run: 28630986312
Workflow ref: codex/fix-6195-atomic-dcode-rebuild
Requested targets: (default — all supported)
Requested jobs: onboard-resume,onboard-repair,cloud-onboard,rebuild-openclaw,rebuild-hermes,sandbox-rebuild,inference-routing,credential-sanitization,network-policy
Summary: 7 passed, 2 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ✅ success
credential-sanitization ✅ success
inference-routing ✅ success
network-policy ✅ success
onboard-repair ✅ success
onboard-resume ✅ success
rebuild-hermes ❌ failure
rebuild-openclaw ❌ failure
sandbox-rebuild ✅ success

Failed jobs: rebuild-hermes, rebuild-openclaw. Check run artifacts for logs.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Vitest E2E Target Results — ❌ Some jobs failed

Run: 28632214086
Workflow ref: codex/fix-6195-atomic-dcode-rebuild
Requested targets: (default — all supported)
Requested jobs: rebuild-openclaw
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
rebuild-openclaw ❌ failure

Failed jobs: rebuild-openclaw. Check run artifacts for logs.

@cv

cv commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 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: Carlos Villela <cvillela@nvidia.com>
@cv

cv commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/lib/onboard/prepared-dcode-rebuild.ts (1)

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

Reuse the existing DCode agent-name constant
src/lib/onboard/prepared-dcode-rebuild.ts:17 should use DCODE_AGENT_NAME from src/lib/actions/sandbox/rebuild-dcode-target.ts instead of repeating "langchain-deepagents-code" here, so the rebuild path keeps one source of truth.

🤖 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/prepared-dcode-rebuild.ts` at line 17, The prepared DCode
rebuild path is hardcoding the agent name instead of reusing the shared
constant. Update the `DCODE_AGENT` definition in `prepared-dcode-rebuild` to
reference `DCODE_AGENT_NAME` from `rebuild-dcode-target.ts`, so both
`prepared-dcode-rebuild` and `rebuild-dcode-target` use the same source of
truth.
🤖 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/prepared-dcode-rebuild.test.ts`:
- Around line 79-95: The test in createPreparedDcodeRebuildRuntime’s
bindCreateSandbox mock currently uses a manual conditional inside the create fn,
which violates the growth-guardrail. Replace the branching on attempt with
explicit mockImplementationOnce chaining on the vi.fn in
prepared-dcode-rebuild.test.ts so the first call throws and the second resolves,
while still asserting the contexts array behavior.

In `@src/lib/onboard/prepared-dcode-rebuild.ts`:
- Around line 136-151: resolveSandboxBuildId currently trusts
preparedBuildContext.buildId without enforcing the same target check as
resolveSandboxBuildContext. Mirror that guard in resolveSandboxBuildId by
validating the input’s agent.name and fromDockerfile before returning
preparedBuildContext.buildId, and reject mismatched/non-dcode targets the same
way as resolveSandboxBuildContext. Use the existing resolveSandboxBuildId and
resolveSandboxBuildContext flow in prepared-dcode-rebuild to keep the
atomic-rebuild path consistent and prevent stale build IDs from being accepted.

---

Nitpick comments:
In `@src/lib/onboard/prepared-dcode-rebuild.ts`:
- Line 17: The prepared DCode rebuild path is hardcoding the agent name instead
of reusing the shared constant. Update the `DCODE_AGENT` definition in
`prepared-dcode-rebuild` to reference `DCODE_AGENT_NAME` from
`rebuild-dcode-target.ts`, so both `prepared-dcode-rebuild` and
`rebuild-dcode-target` use the same source of truth.
🪄 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: 8796da7f-6d7e-43c2-afcc-f664e02fa5ff

📥 Commits

Reviewing files that changed from the base of the PR and between 32053ad and 4f6e71e.

📒 Files selected for processing (7)
  • src/lib/actions/sandbox/rebuild-flow.test.ts
  • src/lib/actions/sandbox/rebuild-gpu-opt-out.ts
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/onboard.ts
  • src/lib/onboard/build-context-stage.ts
  • src/lib/onboard/prepared-dcode-rebuild.test.ts
  • src/lib/onboard/prepared-dcode-rebuild.ts
💤 Files with no reviewable changes (1)
  • src/lib/onboard/build-context-stage.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/lib/actions/sandbox/rebuild-gpu-opt-out.ts
  • src/lib/onboard.ts
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/actions/sandbox/rebuild-flow.test.ts

cv added 2 commits July 3, 2026 03:17
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv

cv commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 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: Carlos Villela <cvillela@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.

🧹 Nitpick comments (2)
test/helpers/rebuild-flow-harness.ts (1)

291-299: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

getSandbox mock silently falls back once sandboxEntryReads is exhausted.

The mock consumes overrides.sandboxEntryReads strictly by call order/count and falls back to sandboxEntry after the list is exhausted, with no distinction between "not overridden" and "read past expected count." If a future implementation change adds/removes an intermediate registry.getSandbox() call, consuming tests (which hard-code exact read sequences, e.g. in rebuild-dcode-flow.test.ts) would silently receive drifted/stale data instead of failing loudly, undermining confidence that the "drift" assertions are actually exercising the intended call site.

Consider throwing (or returning an obviously-invalid sentinel) once reads exceed configuredReads.length when sandboxEntryReads was explicitly provided, so an unexpected extra/missing call fails the test instead of passing silently.

🤖 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 `@test/helpers/rebuild-flow-harness.ts` around lines 291 - 299, The getSandbox
mock in rebuild-flow-harness currently falls back to sandboxEntry after
consuming all configured sandboxEntryReads, which can hide unexpected extra or
missing calls. Update the mock around registry.getSandbox so that when
overrides.sandboxEntryReads is explicitly provided, any read beyond the
configured sequence fails loudly (for example by throwing or returning a clearly
invalid sentinel) instead of silently returning sandboxEntry. Keep the normal
fallback only when sandboxEntryReads was not supplied, so tests like
rebuild-dcode-flow.test.ts continue to validate the intended call order.
src/lib/actions/sandbox/rebuild-dcode-flow.test.ts (1)

149-175: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Tests are coupled to the exact number/order of internal getSandbox() calls.

Both tests hard-code the precise sequence of registry.getSandbox() reads via sandboxEntryReads, annotated with comments describing internal orchestration steps ("Messaging-conflict gateway selection", "Prepared DCode target capture", "Delete-edge target verification input", etc.). This asserts drift-detection by exploiting the harness's positional-array fallback (see companion comment in rebuild-flow-harness.ts) rather than through an observable public-boundary signal. If the implementation's internal read count changes for unrelated reasons, these tests would silently consume the wrong drifted/undrifted entry at the wrong step instead of failing with a clear signal.

As per path instructions, "Prefer observable outcomes through the public boundary over source-text, private-shape, or mock-call assertions" for **/*.test.{ts,js,mts,mjs,cts,cjs}. Consider having the drift live only on the read(s) that matter and letting the harness fail loudly if the read count assumption is exceeded (see companion refactor suggestion in the harness file).

Also applies to: 200-234

🤖 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/rebuild-dcode-flow.test.ts` around lines 149 - 175,
The DCode rebuild tests are coupling drift detection to the exact sequence of
internal getSandbox reads via sandboxEntryReads, which makes them brittle.
Update the test to assert the observable failure at the public rebuildSandbox
boundary and avoid relying on positional fallback comments for orchestration
steps; keep drift only on the read that matters, and let
createRebuildFlowHarness or the registry access fail loudly if extra reads
occur. Use the existing rebuildSandbox, configureDcodeSession, and
createRebuildFlowHarness setup to preserve the same behavior without hard-coding
internal call order.

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.

Nitpick comments:
In `@src/lib/actions/sandbox/rebuild-dcode-flow.test.ts`:
- Around line 149-175: The DCode rebuild tests are coupling drift detection to
the exact sequence of internal getSandbox reads via sandboxEntryReads, which
makes them brittle. Update the test to assert the observable failure at the
public rebuildSandbox boundary and avoid relying on positional fallback comments
for orchestration steps; keep drift only on the read that matters, and let
createRebuildFlowHarness or the registry access fail loudly if extra reads
occur. Use the existing rebuildSandbox, configureDcodeSession, and
createRebuildFlowHarness setup to preserve the same behavior without hard-coding
internal call order.

In `@test/helpers/rebuild-flow-harness.ts`:
- Around line 291-299: The getSandbox mock in rebuild-flow-harness currently
falls back to sandboxEntry after consuming all configured sandboxEntryReads,
which can hide unexpected extra or missing calls. Update the mock around
registry.getSandbox so that when overrides.sandboxEntryReads is explicitly
provided, any read beyond the configured sequence fails loudly (for example by
throwing or returning a clearly invalid sentinel) instead of silently returning
sandboxEntry. Keep the normal fallback only when sandboxEntryReads was not
supplied, so tests like rebuild-dcode-flow.test.ts continue to validate the
intended call order.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: bd08cdf5-d27b-4a20-9cb0-32cc1723221a

📥 Commits

Reviewing files that changed from the base of the PR and between 8230437 and b28d7df.

📒 Files selected for processing (7)
  • src/lib/actions/sandbox/rebuild-dcode-flow.test.ts
  • src/lib/actions/sandbox/rebuild-dcode-orchestrator.ts
  • src/lib/actions/sandbox/rebuild-flow.test.ts
  • src/lib/actions/sandbox/rebuild-managed-image-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-managed-image-preflight.ts
  • src/lib/actions/sandbox/rebuild.ts
  • test/helpers/rebuild-flow-harness.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/actions/sandbox/rebuild.ts

cv added 2 commits July 3, 2026 04:00
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv

cv commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 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/actions/sandbox/rebuild-dcode-flow.test.ts (1)

297-349: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract shared assertion helper for "preserved after backup" tests.

These two tests (and per the line-range summary, likely 230-296 as well) repeat the same block: openShieldsSpy/backupSandboxStateSpy called once, runOpenshellSpy not called with delete, removeSandboxRegistryEntrySpy/onboardSpy not called, relockSpy called with fixed args, and disposePreparedDcodeRebuildImageSpy called with the prepared context. Consider factoring this into a helper alongside the existing expectNoDcodeMutation (lines 15-59) to reduce duplication and keep future assertion changes in one place.

♻️ Suggested helper
+function expectDcodePreservedAfterBackup(harness: ReturnType<typeof createRebuildFlowHarness>): void {
+  expect(harness.openShieldsSpy).toHaveBeenCalledOnce();
+  expect(harness.backupSandboxStateSpy).toHaveBeenCalledOnce();
+  expect(harness.runOpenshellSpy).not.toHaveBeenCalledWith(
+    ["sandbox", "delete", "alpha"],
+    expect.anything(),
+  );
+  expect(harness.removeSandboxRegistryEntrySpy).not.toHaveBeenCalled();
+  expect(harness.onboardSpy).not.toHaveBeenCalled();
+  expect(harness.relockSpy).toHaveBeenCalledWith("alpha", expect.any(Object), true, "nemoclaw");
+  expect(harness.disposePreparedDcodeRebuildImageSpy).toHaveBeenCalledWith(
+    harness.preparedDcodeBuildContext,
+  );
+}
🤖 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/rebuild-dcode-flow.test.ts` around lines 297 - 349,
The “preserved after backup” rebuild tests are duplicating the same assertion
block for the no-mutation path. Factor those repeated checks into a shared
helper near the existing expectNoDcodeMutation helper, and have the affected
tests call it instead of inlining the repeated assertions. Keep the helper
centered on the rebuildSandbox harness spies: openShieldsSpy,
backupSandboxStateSpy, runOpenshellSpy, removeSandboxRegistryEntrySpy,
onboardSpy, relockSpy, and disposePreparedDcodeRebuildImageSpy.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/lib/actions/sandbox/rebuild-dcode-flow.test.ts`:
- Around line 297-349: The “preserved after backup” rebuild tests are
duplicating the same assertion block for the no-mutation path. Factor those
repeated checks into a shared helper near the existing expectNoDcodeMutation
helper, and have the affected tests call it instead of inlining the repeated
assertions. Keep the helper centered on the rebuildSandbox harness spies:
openShieldsSpy, backupSandboxStateSpy, runOpenshellSpy,
removeSandboxRegistryEntrySpy, onboardSpy, relockSpy, and
disposePreparedDcodeRebuildImageSpy.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: bc507122-133b-4972-8b2c-1c883d37a011

📥 Commits

Reviewing files that changed from the base of the PR and between b28d7df and dd43445.

📒 Files selected for processing (6)
  • src/lib/actions/sandbox/rebuild-dcode-flow.test.ts
  • src/lib/actions/sandbox/rebuild-managed-image-preflight.test.ts
  • src/lib/actions/sandbox/rebuild-managed-image-preflight.ts
  • src/lib/onboard/prepared-dcode-rebuild.test.ts
  • src/lib/onboard/prepared-dcode-rebuild.ts
  • test/helpers/rebuild-flow-harness.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/lib/onboard/prepared-dcode-rebuild.ts
  • src/lib/onboard/prepared-dcode-rebuild.test.ts
  • test/helpers/rebuild-flow-harness.ts

@cv

cv commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

Final-head review disposition for dd43445c:

  • CI: 41 passed, 0 failed, 0 pending; the aggregate CodeQL gate and both JavaScript/Python analyses pass.
  • DCO/signing: all 18 PR commits report verified=true, reason valid.
  • Local verification: normal commit/push hooks pass; the final DCode/managed-image/prepared-handoff set is 27/27, and the earlier full make check plus npm test run passed.
  • GPT-5.5 advisor: merge_as_is, with 0 required items, warnings, or suggestions.
  • CodeRabbit: success; its only final note is the optional extraction of repeated fail-closed test assertions. The assertions remain explicit in each distinct drift scenario; there is no production or correctness finding.

Nemotron's remaining required classifications do not identify a current defect:

  • OPENSHELL_GATEWAY and the GPU patch-network variable are scoped to one single-command CLI process and restored on every success/error boundary. The flow tests prove restoration on messaging failure, unhealthy-gateway abort, and later preflight failures. A generalized explicit execution context belongs with Epic: stabilize onboarding and runtime lifecycle contracts before public extensibility #6224/refactor(onboard): capture sandbox create intent before destructive effects #6226, not this DCode-only fix.
  • The inference probe targets the constant in-sandbox host https://inference.local; the sandbox name is an argv value and shell-quoted where embedded. Registry, resolved target, route, gateway schema, base-image identity, and retained context are revalidated at the mutation edge.
  • The onboarding handoff is already extracted into prepared-dcode-rebuild.ts and leaves onboard.ts four lines smaller than main. DCode lifecycle orchestration is extracted into rebuild-dcode-orchestrator.ts; rebuild.ts fell from the prior 1,599-line candidate to 1,460 lines, and the repository growth guard passes.
  • connected_other means the CLI is currently connected to a gateway other than the sandbox's recorded target; selecting the recorded named gateway is the intended recovery behavior, not cross-sandbox ownership transfer.
  • The stale/prepared-backup path already proves live-route skipping with image preparation and recreation. Whitespace-only gateway names trim to "" and fail the exact expected-gateway comparison.

The final tests independently cover an unrecoverable recorded gateway, retained-context drift after backup, and pinned-base-image identity drift after backup; all preserve the live sandbox and block delete/recreate.

@cv
cv merged commit 2c4bf19 into main Jul 3, 2026
55 checks passed
@cv
cv deleted the codex/fix-6195-atomic-dcode-rebuild branch July 3, 2026 19:30
@ericksoa ericksoa mentioned this pull request Jul 4, 2026
9 of 21 tasks
ericksoa added a commit that referenced this pull request Jul 4, 2026
<!-- markdownlint-disable MD041 -->
## Summary
This PR prepares the user-facing documentation for v0.0.74 before the
release plan is frozen.
It expands the release notes across the 56-commit train and closes
durable documentation gaps found during the pre-tag commit scan.

## Changes
- Expand the `v0.0.74` release notes to cover OpenShell 0.0.72, managed
MCP, progressive tool disclosure, LangChain Deep Agents Code,
onboarding, local inference, messaging, recovery, and contributor
workflows.
- Correct the `destroy` contract for retained per-name volumes,
gateway-unreachable `--force` cleanup, managed MCP ownership, and
same-name recovery.
- Document separate remediation for an unreachable container DNS
resolver versus one that answers with `NXDOMAIN` or `REFUSED`.
- Document the Windows on Arm N1X automatic Ollama safeguard and its
remaining large-model limitations.
- State that messaging conflicts abort rebuild before backup or
deletion, leaving the original sandbox intact.
- Link the agent-runnable value benchmark from the contributor task
index.
- Synchronize generated agent command variants.
- Validate with `npm run docs:sync-agent-variants` and `npm run docs`;
Fern completed with 0 errors and 2 existing warnings.
- Source summary:
- [#6020](#6020) and
[#5876](#5876) ->
`docs/about/release-notes.mdx`: Consolidate the OpenShell 0.0.72 policy
boundary and managed MCP lifecycle.
- [#6251](#6251) and
[#5989](#5989) ->
`docs/about/release-notes.mdx`: Summarize progressive tool disclosure
and sandbox-first inference controls.
- [#6232](#6232),
[#6082](#6082),
[#6219](#6219),
[#6214](#6214),
[#6215](#6215),
[#6230](#6230), and
[#6260](#6260) ->
`docs/about/release-notes.mdx`: Summarize the experimental LangChain
Deep Agents Code status, secret, version, rebuild, snapshot, and MCP
boundaries.
- [#6166](#6166),
[#6254](#6254),
[#6265](#6265),
[#6164](#6164), and
[#6017](#6017) ->
`docs/about/release-notes.mdx`: Summarize BuildKit prebuild, validated
image reuse, bounded readiness, and preflight improvements.
- [#6150](#6150) ->
`docs/about/release-notes.mdx` and `docs/reference/troubleshooting.mdx`:
Separate unreachable-resolver remediation from reachable-but-rejected
DNS responses.
- [#6234](#6234) ->
`docs/about/release-notes.mdx`,
`docs/inference/use-local-inference.mdx`, and
`docs/get-started/windows-preparation.mdx`: Document N1X automatic 9B
selection and the remaining explicit-large-model boundary.
- [#6129](#6129),
[#5987](#5987),
[#5955](#5955), and
[#6220](#6220) ->
`docs/about/release-notes.mdx`,
`docs/manage-sandboxes/messaging-channels.mdx`,
`docs/reference/commands.mdx`, and
`docs/reference/commands-nemohermes.mdx`: Document messaging policy
persistence, status, and the pre-destructive conflict check.
- [#5963](#5963),
[#6050](#6050),
[#6094](#6094),
[#6238](#6238),
[#5988](#5988),
[#6235](#6235),
[#6181](#6181), and
[#5986](#5986) ->
`docs/about/release-notes.mdx`, `docs/reference/commands.mdx`, and
`docs/reference/commands-nemohermes.mdx`: Summarize day-two recovery and
clarify retained-volume and local-only destroy semantics.
- [#6200](#6200),
[#6248](#6248),
[#6168](#6168),
[#6270](#6270), and
[#5649](#5649) ->
`docs/about/release-notes.mdx` and `CONTRIBUTING.md`: Summarize
contributor setup and verification improvements and expose the advisory
value benchmark.

## Type of Change

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

## Quality Gates
<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [ ] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [x] Tests not applicable — justification: documentation-only release
preparation; generated-variant synchronization and the Fern docs build
validate the changed pages and routes.
- [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:

## Verification
<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [x] PR description includes the DCO sign-off declaration 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: tests
are not applicable to this documentation-only change; `npm run docs`
validates the source and generated routes.
- [ ] 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)
- [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)

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>


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

## Summary by CodeRabbit

* **Documentation**
* Expanded setup guidance for Windows on Arm devices with safer default
local model selection.
* Clarified local inference and sandbox messaging behavior, including
conflict checks before rebuilds and safer recovery steps.
* Updated destroy/rebuild/reference docs with more detailed warnings,
failure handling, and volume-retention guidance.
* Improved troubleshooting instructions for Docker DNS issues with
clearer paths for unreachable vs. blocked resolvers.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
<!-- markdownlint-disable MD041 -->
## Summary
<!-- 1-3 sentences: what this PR does and why. -->

Prevents a Ready LangChain Deep Agents Code sandbox from being deleted
until its recorded OpenShell gateway, live inference route, managed base
image, and staged build context have been validated and sealed. A
DCode-only, process-local orchestration facade carries that verified
gateway/context pair through recreation, fixing the post-delete
port-8080 false conflict from NVIDIA#6195 without adding generalized persisted
replay or FSM state.

## Related Issue
<!-- Fixes #NNN or Closes #NNN. Remove this section if none. -->

Fixes NVIDIA#6195
Refs NVIDIA#6218
Refs NVIDIA#6224
Refs NVIDIA#6226
Refs NVIDIA#5801

## Changes
<!-- Bullet list of key changes. -->

- Bind DCode rebuilds to the sandbox's recorded gateway before
credential validation, probe the live `https://inference.local` route
without exposing credentials, and revalidate the gateway, registry
entry, route, schema, base image, and staged context immediately before
deletion.
- Build a disposable DCode image and fingerprint its managed build
context before mutation, then pass the retained context and recorded
gateway as a one-shot handoff through the create-intent seam landed in
NVIDIA#6218. Ordinary onboarding and non-DCode rebuild behavior remain
unchanged.
- Put the DCode lifecycle in `rebuild-dcode-orchestrator.ts`: it owns
scoped gateway cleanup, target preflight, replacement preparation,
mutation-edge revalidation, recovery behavior, and one-shot handoff
cleanup while depending on injected generic rebuild callbacks. This
reduced `rebuild.ts` from 1,599 to 1,460 lines and split its 1,477-line
flow spec into focused generic and DCode suites plus a shared harness.
- Extract the onboarding handoff into `prepared-dcode-rebuild.ts`, which
owns gateway validation, ordinary-versus-prepared staging/patching, and
consume-before-call semantics. The top-level `onboard.ts` entrypoint is
four lines smaller than `main`.
- Preserve the existing sandbox when initial preflight fails; if
mutation-edge validation fails after backup, keep the harmless backup
but do not delete or recreate the sandbox. Prepared-backup recovery
skips unavailable live-route probes and does not take a second backup.
Messaging-preflight aborts also restore the prior process gateway.
- Harden managed-context sealing against symlink substitution, pathname
replacement, and in-place mutation by reading regular files through
`O_NOFOLLOW` file descriptors and checking identity/metadata before and
after each read.
- Add a live DCode lifecycle fixture that rotates the credential stored
in the real gateway, waits for a 401, runs rebuild without a host
credential, and proves there was no backup/delete/create while container
identity, readiness, and a workspace marker remain intact.
- Document the DCode rebuild preflight and backup boundary in the
quickstart. A documentation review confirmed no further user-facing
changes are needed.
- Keep generalized FSM/session capture and cross-agent replay out of
scope for NVIDIA#6224/NVIDIA#6226; true build/health/swap replacement remains NVIDIA#5801.

## Type of Change

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

## Quality Gates
<!-- Check all that apply. For any "covered by existing tests", "not
applicable", or waiver entry, add a brief justification on the same line
or in the Changes section. -->
- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification: independent final
architecture and nine-category security reviews both passed the narrowed
DCode-only design with no blocker; the security review included the
final orchestration extraction and managed-context TOCTOU hardening.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
<!-- Check each item you ran and confirmed. Leave unchecked items you
skipped. Doc-only changes do not require npm test unless you ran it. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Git hooks passed during commit and push, or `npx prek run
--from-ref main --to-ref HEAD` passes
- [x] Targeted tests pass for changed behavior
- [x] Full `npm test` passes (broad runtime changes only)
- [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)
- [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)

Commands and evidence:

- `make check` — passed on the final narrowed implementation under the
repository-expected `umask 022`.
- `npm test` — 998 files passed, 2 platform suites skipped; 11,277 tests
passed, 35 skipped.
- Post-merge focused/current-main set — 96/96 passed across generic
rebuild flow, DCode flow, managed-image preflight, prepared handoff,
inference-provider compatibility, and messaging setup; the final
descriptor-pinning, target-guard, and fail-closed review deltas passed
27/27 focused cases.
- Additional affected environment-failure audit — 121/121 passed; E2E
support — 26/26 passed.
- `npm run build:cli` and `npm run typecheck:cli` — passed.
- Current-tree Vitest project-membership check — 1,075 files across
eight disjoint projects; source-shape, test-file-size, and changed-test
conditional budgets passed.
- `npm run docs` — passed with 0 errors and 2 pre-existing warnings.
- Pre-push plugin and CLI TypeScript gates — passed.
- GitHub commit verification — 18/18 commits report `verified=true`,
reason `valid`.

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Carlos Villela <cvillela@nvidia.com>



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

## Summary by CodeRabbit

* **New Features**
* Added support for a new DCode rebuild flow during onboarding and
sandbox recreation.
* Introduced a preflight check for inference routing before rebuilds
proceed.

* **Bug Fixes**
* Rebuilds now fail safely when credentials or routing are invalid,
helping prevent unsafe sandbox changes.
* Improved rebuild handling so existing sandboxes and backups are
preserved if a later check fails.

* **Documentation**
* Updated the quickstart guide with clearer rebuild and backup behavior
for managed DCode sandboxes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
<!-- markdownlint-disable MD041 -->
## Summary
This PR prepares the user-facing documentation for v0.0.74 before the
release plan is frozen.
It expands the release notes across the 56-commit train and closes
durable documentation gaps found during the pre-tag commit scan.

## Changes
- Expand the `v0.0.74` release notes to cover OpenShell 0.0.72, managed
MCP, progressive tool disclosure, LangChain Deep Agents Code,
onboarding, local inference, messaging, recovery, and contributor
workflows.
- Correct the `destroy` contract for retained per-name volumes,
gateway-unreachable `--force` cleanup, managed MCP ownership, and
same-name recovery.
- Document separate remediation for an unreachable container DNS
resolver versus one that answers with `NXDOMAIN` or `REFUSED`.
- Document the Windows on Arm N1X automatic Ollama safeguard and its
remaining large-model limitations.
- State that messaging conflicts abort rebuild before backup or
deletion, leaving the original sandbox intact.
- Link the agent-runnable value benchmark from the contributor task
index.
- Synchronize generated agent command variants.
- Validate with `npm run docs:sync-agent-variants` and `npm run docs`;
Fern completed with 0 errors and 2 existing warnings.
- Source summary:
- [NVIDIA#6020](NVIDIA#6020) and
[NVIDIA#5876](NVIDIA#5876) ->
`docs/about/release-notes.mdx`: Consolidate the OpenShell 0.0.72 policy
boundary and managed MCP lifecycle.
- [NVIDIA#6251](NVIDIA#6251) and
[NVIDIA#5989](NVIDIA#5989) ->
`docs/about/release-notes.mdx`: Summarize progressive tool disclosure
and sandbox-first inference controls.
- [NVIDIA#6232](NVIDIA#6232),
[NVIDIA#6082](NVIDIA#6082),
[NVIDIA#6219](NVIDIA#6219),
[NVIDIA#6214](NVIDIA#6214),
[NVIDIA#6215](NVIDIA#6215),
[NVIDIA#6230](NVIDIA#6230), and
[NVIDIA#6260](NVIDIA#6260) ->
`docs/about/release-notes.mdx`: Summarize the experimental LangChain
Deep Agents Code status, secret, version, rebuild, snapshot, and MCP
boundaries.
- [NVIDIA#6166](NVIDIA#6166),
[NVIDIA#6254](NVIDIA#6254),
[NVIDIA#6265](NVIDIA#6265),
[NVIDIA#6164](NVIDIA#6164), and
[NVIDIA#6017](NVIDIA#6017) ->
`docs/about/release-notes.mdx`: Summarize BuildKit prebuild, validated
image reuse, bounded readiness, and preflight improvements.
- [NVIDIA#6150](NVIDIA#6150) ->
`docs/about/release-notes.mdx` and `docs/reference/troubleshooting.mdx`:
Separate unreachable-resolver remediation from reachable-but-rejected
DNS responses.
- [NVIDIA#6234](NVIDIA#6234) ->
`docs/about/release-notes.mdx`,
`docs/inference/use-local-inference.mdx`, and
`docs/get-started/windows-preparation.mdx`: Document N1X automatic 9B
selection and the remaining explicit-large-model boundary.
- [NVIDIA#6129](NVIDIA#6129),
[NVIDIA#5987](NVIDIA#5987),
[NVIDIA#5955](NVIDIA#5955), and
[NVIDIA#6220](NVIDIA#6220) ->
`docs/about/release-notes.mdx`,
`docs/manage-sandboxes/messaging-channels.mdx`,
`docs/reference/commands.mdx`, and
`docs/reference/commands-nemohermes.mdx`: Document messaging policy
persistence, status, and the pre-destructive conflict check.
- [NVIDIA#5963](NVIDIA#5963),
[NVIDIA#6050](NVIDIA#6050),
[NVIDIA#6094](NVIDIA#6094),
[NVIDIA#6238](NVIDIA#6238),
[NVIDIA#5988](NVIDIA#5988),
[NVIDIA#6235](NVIDIA#6235),
[NVIDIA#6181](NVIDIA#6181), and
[NVIDIA#5986](NVIDIA#5986) ->
`docs/about/release-notes.mdx`, `docs/reference/commands.mdx`, and
`docs/reference/commands-nemohermes.mdx`: Summarize day-two recovery and
clarify retained-volume and local-only destroy semantics.
- [NVIDIA#6200](NVIDIA#6200),
[NVIDIA#6248](NVIDIA#6248),
[NVIDIA#6168](NVIDIA#6168),
[NVIDIA#6270](NVIDIA#6270), and
[NVIDIA#5649](NVIDIA#5649) ->
`docs/about/release-notes.mdx` and `CONTRIBUTING.md`: Summarize
contributor setup and verification improvements and expose the advisory
value benchmark.

## Type of Change

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

## Quality Gates
<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [ ] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [x] Tests not applicable — justification: documentation-only release
preparation; generated-variant synchronization and the Fern docs build
validate the changed pages and routes.
- [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:

## Verification
<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [x] PR description includes the DCO sign-off declaration 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: tests
are not applicable to this documentation-only change; `npm run docs`
validates the source and generated routes.
- [ ] 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)
- [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)

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>


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

## Summary by CodeRabbit

* **Documentation**
* Expanded setup guidance for Windows on Arm devices with safer default
local model selection.
* Clarified local inference and sandbox messaging behavior, including
conflict checks before rebuilds and safer recovery steps.
* Updated destroy/rebuild/reference docs with more detailed warnings,
failure handling, and volume-retention guidance.
* Improved troubleshooting instructions for Docker DNS issues with
clearer paths for unreachable vs. blocked resolvers.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@coderabbitai coderabbitai Bot mentioned this pull request Jul 20, 2026
12 of 23 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression integration: dcode LangChain Deep Code integration behavior

Projects

None yet

3 participants