feat(cli): add read-only host mounts - #8280
Conversation
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe PR adds repeated read-only host-directory mounts to onboarding. It validates mounts and platforms, enables managed Docker bind mounts, propagates mounts through creation and persistence, preserves them during rebuilds, reports them in status output, and documents their lifecycle. ChangesRead-only host mount onboarding
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested labels: Sequence Diagram(s)sequenceDiagram
participant NemoClawCLI
participant OnboardCommand
participant DockerDriverGateway
participant SandboxCreatePlan
participant SandboxRegistry
NemoClawCLI->>OnboardCommand: submit --host-mount declarations
OnboardCommand->>DockerDriverGateway: enable managed bind mounts
OnboardCommand->>SandboxCreatePlan: pass validated read-only mounts
SandboxCreatePlan->>DockerDriverGateway: generate read-only bind configuration
SandboxCreatePlan->>SandboxRegistry: store cloned mount metadata
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
|
🌿 Preview your docs: https://nvidia-preview-pr-8280.docs.buildwithfern.com/nemoclaw |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage in commit 974066d in the TypeScript / code-coverage/cliThe overall coverage in commit 974066d in the Show a code coverage summary of the most impacted files.
Updated |
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
PR Review Advisor — No blocking findings reportedAdvisor assessment: No blocking advisor findings reported Model lanes
5 terminology differences from the second opinionAdvisory only. These are normalized differences from the primary terminology receipt.
2 additional E2E selections from the second opinionAdvisory only. The primary lane did not select these E2E jobs or targets.
Second-opinion terminology and E2E selections are advisory. They do not change the primary assessment or E2E / PR Gate. 4 semantic terminology decisionsTerminology decisions are advisory. They affect the assessment only when a separate finding identifies concrete semantic impact.
E2E guidanceAdvisory only. E2E / PR Gate selects and runs jobs independently. Recommended E2E: 1 optional E2E recommendation
This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge. |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 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/command.test.ts`:
- Around line 113-125: The test around resolve must actually pass two distinct
valid host-mount values through the public resolver boundary. Create two
temporary source directories, provide both mappings in the Linux input, and
assert both read-only hostMounts are returned; retain the Darwin rejection using
that same two-mount input and clean up both directories.
In `@src/lib/onboard/docker-driver-gateway-env.ts`:
- Line 227: Update the gateway runtime drift validation to require
NEMOCLAW_DOCKER_ENABLE_BIND_MOUNTS to be absent or disabled when the current
onboarding has no host mounts, so a reused mount-enabled process is rejected.
Add a regression test covering reuse from mount-enabled to mount-disabled
configuration, while preserving the enabled case in
docker-driver-gateway-env.ts.
In `@src/lib/onboard/machine/handlers/sandbox.ts`:
- Line 1204: Update resolveResumeDecision() and the reuseSandbox() flow to
normalize and compare this.options.hostMounts against the persisted host-mount
declaration before returning reuse. Ensure matching mounts continue reuse, while
added, removed, or changed mounts trigger recreate or fail closed, and cover all
cases in resume tests so reuse converges with buildSandboxCreateIntent().
In `@src/lib/onboard/sandbox-create-intent.ts`:
- Line 147: Preserve checkpoint fingerprint compatibility in
resolveSandboxCreateIntent by omitting hostMounts when no mounts are requested,
or by canonicalizing absent and empty hostMounts in
SandboxStateFlow.currentSandboxCreateFingerprint and
assertCheckpointCreateInputsStillMatch. Add a resume test using a pre-mount
checkpoint to verify interrupted onboarding does not report input drift.
In `@src/lib/state/onboard-session.ts`:
- Around line 470-486: Update persisted host-mount handling around the
hostMounts normalization and prepareResumeSession so invalid declarations are
rejected rather than silently filtered out. Preserve validation failures through
persisted-state recovery and make resume reject sessions containing malformed,
missing, or symlinked mounts when no replacement mounts are supplied; ensure
replacement mounts still follow the normal validation path. Add coverage for
each invalid persisted-mount case during resume.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 05ba0d39-62c4-4226-99b3-df0a184aef4e
📒 Files selected for processing (39)
docs/manage-sandboxes/workspace-files.mdxdocs/reference/commands.mdxsrc/lib/actions/sandbox/rebuild-gpu-opt-out.test.tssrc/lib/actions/sandbox/rebuild-gpu-opt-out.tssrc/lib/actions/sandbox/status-flow.test.tssrc/lib/actions/sandbox/status-snapshot.tssrc/lib/actions/sandbox/status-text.tssrc/lib/actions/sandbox/status.test.tssrc/lib/onboard.tssrc/lib/onboard/command-support.tssrc/lib/onboard/command.test.tssrc/lib/onboard/command.tssrc/lib/onboard/docker-driver-gateway-config.tssrc/lib/onboard/docker-driver-gateway-env.tssrc/lib/onboard/docker-driver-gateway-launch.test.tssrc/lib/onboard/docker-driver-gateway-runtime.tssrc/lib/onboard/host-mount/host-mount.test.tssrc/lib/onboard/host-mount/index.tssrc/lib/onboard/machine/core-flow-phases.tssrc/lib/onboard/machine/handlers/gateway.tssrc/lib/onboard/machine/handlers/sandbox.tssrc/lib/onboard/machine/initial-flow-phases.tssrc/lib/onboard/sandbox-create-intent-resolution.tssrc/lib/onboard/sandbox-create-intent-types.tssrc/lib/onboard/sandbox-create-intent.tssrc/lib/onboard/sandbox-create-plan-materialization.tssrc/lib/onboard/sandbox-create-plan.test.tssrc/lib/onboard/sandbox-create-plan.tssrc/lib/onboard/sandbox-registration.test.tssrc/lib/onboard/sandbox-registration.tssrc/lib/onboard/session-bootstrap.test.tssrc/lib/onboard/session-bootstrap.tssrc/lib/onboard/types.tssrc/lib/state/onboard-session.test.tssrc/lib/state/onboard-session.tssrc/lib/state/registry.tssrc/lib/state/registry/host-mount.tssrc/lib/state/registry/types.tstest/support/status-flow-test-harness.ts
| hermesToolGateways, | ||
| extraProviders, | ||
| staleExtraProviders, | ||
| hostMounts: this.options.hostMounts, |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Revalidate host mounts before sandbox reuse.
hostMounts reaches buildSandboxCreateIntent() only after resolveResumeDecision() selects create or recreate. When the decision is reuse, reuseSandbox() does not compare this.options.hostMounts with the persisted declaration. A new --host-mount can be silently ignored, and a changed mount declaration is not revalidated.
Compare normalized host mounts before returning reuse. Recreate or fail closed when they differ. Include matching, added, removed, and changed mount cases in resume tests.
As per path instructions, resume and rebuild flows must revalidate durable state and converge with the authoritative creation path.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/lib/onboard/machine/handlers/sandbox.ts` at line 1204, Update
resolveResumeDecision() and the reuseSandbox() flow to normalize and compare
this.options.hostMounts against the persisted host-mount declaration before
returning reuse. Ensure matching mounts continue reuse, while added, removed, or
changed mounts trigger recreate or fail closed, and cover all cases in resume
tests so reuse converges with buildSandboxCreateIntent().
Source: Path instructions
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/onboard/gateway-health-wait.ts`:
- Around line 10-29: Remove the duplicate getGatewayHealthWaitConfig
implementation from onboard.ts and import the authoritative function from
gateway-health-wait.ts, preserving a re-export only if the existing public API
requires it. Update all onboarding call sites to use this imported symbol, and
add public-boundary coverage proving fresh, resumed, repair, and failure flows
reach the module implementation.
In `@src/lib/onboard/host-mount/index.ts`:
- Around line 13-35: Update the onboarding cleanup flow around
finishOnboardTrace() to wrap trace-finalization and related cleanup in a nested
try/finally, ensuring hostMountScope.restore() is always executed even when
trace writing fails. Preserve the existing restoration behavior and use the
scope returned by beginHostMountScope().
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: ad63b8df-3f5d-4f3a-bc5a-7d23fb1175a0
📒 Files selected for processing (5)
src/lib/onboard.tssrc/lib/onboard/gateway-health-wait.tssrc/lib/onboard/host-mount/host-mount.test.tssrc/lib/onboard/host-mount/index.tssrc/lib/onboard/session-bootstrap.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- src/lib/onboard/session-bootstrap.ts
- src/lib/onboard/host-mount/host-mount.test.ts
- src/lib/onboard.ts
| export function getGatewayHealthWaitConfig(_startStatus = 0, containerState = "") { | ||
| const isArm64 = process.arch === "arm64"; | ||
| const standardCount = envInt("NEMOCLAW_HEALTH_POLL_COUNT", isArm64 ? 30 : 12); | ||
| const standardInterval = envInt("NEMOCLAW_HEALTH_POLL_INTERVAL", isArm64 ? 10 : 5); | ||
| const extendedCount = envInt("NEMOCLAW_GATEWAY_START_POLL_COUNT", standardCount); | ||
| const extendedInterval = envInt("NEMOCLAW_GATEWAY_START_POLL_INTERVAL", standardInterval); | ||
| const normalizedState = String(containerState || "") | ||
| .trim() | ||
| .toLowerCase(); | ||
| const normalizedContainerState = normalizedState || "missing"; | ||
| const useExtendedWait = normalizedContainerState !== "missing"; | ||
|
|
||
| return { | ||
| count: useExtendedWait ? extendedCount : standardCount, | ||
| interval: useExtendedWait ? extendedInterval : standardInterval, | ||
| extended: useExtendedWait, | ||
| containerState: normalizedContainerState, | ||
| }; | ||
| } | ||
|
|
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Remove the superseded health-wait implementation.
The supplied graph context shows getGatewayHealthWaitConfig also defined in src/lib/onboard.ts, Lines 10-28. The onboarding call sites at Lines 1793-1796 and 2155-2158 therefore can resolve to the old local implementation instead of this module.
Keep src/lib/onboard/gateway-health-wait.ts authoritative. Import it from src/lib/onboard.ts and delete the old implementation. If the legacy export must remain, make it a re-export. Add a public-boundary test that proves onboarding reaches this module.
As per path instructions, a replacement path must be authoritative, and fresh, resumed, repair, and failure flows must prove the cutover.
🤖 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/gateway-health-wait.ts` around lines 10 - 29, Remove the
duplicate getGatewayHealthWaitConfig implementation from onboard.ts and import
the authoritative function from gateway-health-wait.ts, preserving a re-export
only if the existing public API requires it. Update all onboarding call sites to
use this imported symbol, and add public-boundary coverage proving fresh,
resumed, repair, and failure flows reach the module implementation.
Source: Path instructions
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/state/registry/host-mount.ts`:
- Around line 112-119: Update both canonicalHostMounts implementations to use a
locale-independent binary total-order comparator for the combined source and
target keys instead of localeCompare(). Ensure distinct Unicode paths never
compare equal, so equivalent mount sets serialize identically regardless of
declaration order.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 40dd5930-1526-418f-b1a0-c1460fd0edf8
📒 Files selected for processing (17)
src/lib/onboard/command.test.tssrc/lib/onboard/docker-driver-gateway-runtime.test.tssrc/lib/onboard/docker-driver-gateway-runtime.tssrc/lib/onboard/host-mount/host-mount.test.tssrc/lib/onboard/machine/handlers/gateway.test.tssrc/lib/onboard/machine/handlers/sandbox-resume.test.tssrc/lib/onboard/machine/handlers/sandbox-resume.tssrc/lib/onboard/machine/handlers/sandbox.tssrc/lib/onboard/resume-config.test.tssrc/lib/onboard/resume-config.tssrc/lib/onboard/sandbox-create-intent.tssrc/lib/onboard/sandbox-create-plan.test.tssrc/lib/onboard/session-bootstrap.test.tssrc/lib/onboard/session-bootstrap.tssrc/lib/state/onboard-session.test.tssrc/lib/state/onboard-session.tssrc/lib/state/registry/host-mount.ts
🚧 Files skipped from review as they are similar to previous changes (7)
- src/lib/onboard/sandbox-create-plan.test.ts
- src/lib/onboard/sandbox-create-intent.ts
- src/lib/onboard/host-mount/host-mount.test.ts
- src/lib/onboard/command.test.ts
- src/lib/onboard/session-bootstrap.ts
- src/lib/onboard/machine/handlers/sandbox.ts
- src/lib/onboard/session-bootstrap.test.ts
| function canonicalHostMounts(mounts: readonly SandboxHostMount[]): string { | ||
| return JSON.stringify( | ||
| mounts | ||
| .map(({ source, target }) => ({ source, target, readOnly: true as const })) | ||
| .sort((left, right) => | ||
| `${left.source}\0${left.target}`.localeCompare(`${right.source}\0${right.target}`), | ||
| ), | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
node - <<'NODE'
const first = "\u00F1";
const second = "n\u0303";
console.log({
localeCompare: first.localeCompare(second),
binaryCompare: first < second ? -1 : first > second ? 1 : 0,
});
NODERepository: NVIDIA/NemoClaw
Length of output: 192
🏁 Script executed:
set -euo pipefail
file="$(fd -t f 'host-mount\.ts$' . | head -n 1)"
printf '%s\n' "== $file =="
cat -n "$file" | sed -n '90,145p'
printf '%s\n' '== equality callers and tests =='
rg -n -C 3 'canonicalHostMounts|persistedSandboxHostMountsEqual|SandboxHostMount' --glob '!node_modules' --glob '!dist' --glob '!build' . | head -n 240
printf '%s\n' '== deterministic probe =='
node - <<'NODE'
const inputs = [
["\u00F1", "n\u0303"],
["A", "a"],
["\u212B", "\u00C5"],
];
for (const [left, right] of inputs) {
const locale = left.localeCompare(right);
const binary = left < right ? -1 : left > right ? 1 : 0;
console.log(JSON.stringify({left, right, locale, binary}));
}
NODERepository: NVIDIA/NemoClaw
Length of output: 19865
🏁 Script executed:
set -euo pipefail
file="$(fd -t f 'host-mount\.ts$' . | head -n 1)"
cat -n "$file" | sed -n '100,140p'
rg -n -C 3 'canonicalHostMounts|persistedSandboxHostMountsEqual' . \
--glob '!node_modules' --glob '!dist' --glob '!build'
node - <<'NODE'
const first = "\u00F1";
const second = "n\u0303";
console.log({
localeCompare: first.localeCompare(second),
binaryCompare: first < second ? -1 : first > second ? 1 : 0,
});
NODERepository: NVIDIA/NemoClaw
Length of output: 7306
🏁 Script executed:
set -euo pipefail
printf '%s\n' '== host-mount validation and tests =='
cat -n src/lib/state/registry/host-mount.ts | sed -n '1,90p'
cat -n src/lib/onboard/host-mount/host-mount.test.ts | sed -n '88,112p'
printf '%s\n' '== second canonicalization path and resume decision =='
cat -n src/lib/onboard/resume-config.ts | sed -n '20,50p'
cat -n src/lib/onboard/resume-config.ts | sed -n '165,200p'
cat -n src/lib/onboard/machine/handlers/sandbox-resume.ts | sed -n '25,45p'
rg -n -C 4 'hasHostMountConfigDrift|resolveResume|resume.*config|host mounts' src/lib/onboard --glob '*.ts' | head -n 260
printf '%s\n' '== exact ordering failure model =='
node - <<'NODE'
function canonical(mounts) {
return JSON.stringify(
mounts
.map(({source, target}) => ({source, target, readOnly: true}))
.sort((left, right) =>
`${left.source}\0${left.target}`.localeCompare(`${right.source}\0${right.target}`),
),
);
}
const first = {source: "/tmp/\u00F1", target: "/sandbox/a", readOnly: true};
const second = {source: "/tmp/n\u0303", target: "/sandbox/b", readOnly: true};
const left = [first, second];
const right = [second, first];
console.log({
left: canonical(left),
right: canonical(right),
equal: canonical(left) === canonical(right),
});
NODERepository: NVIDIA/NemoClaw
Length of output: 30002
🏁 Script executed:
set -euo pipefail
cat -n src/lib/state/registry/host-mount.ts | sed -n '1,90p'
cat -n src/lib/onboard/host-mount/host-mount.test.ts | sed -n '88,112p'
cat -n src/lib/onboard/resume-config.ts | sed -n '20,50p;165,200p'
cat -n src/lib/onboard/machine/handlers/sandbox-resume.ts | sed -n '25,45p'
node - <<'NODE'
function canonical(mounts) {
return JSON.stringify(
mounts
.map(({source, target}) => ({source, target, readOnly: true}))
.sort((a, b) =>
`${a.source}\0${a.target}`.localeCompare(`${b.source}\0${b.target}`),
),
);
}
const first = {source: "/tmp/\u00F1", target: "/sandbox/a", readOnly: true};
const second = {source: "/tmp/n\u0303", target: "/sandbox/b", readOnly: true};
const left = [first, second];
const right = [second, first];
console.log(canonical(left) === canonical(right));
NODERepository: NVIDIA/NemoClaw
Length of output: 8995
🏁 Script executed:
set -euo pipefail
node - <<'NODE'
const cases = [
[
{source: "/tmp/\u00F1", target: "/sandbox/\u00E9"},
{source: "/tmp/n\u0303", target: "/sandbox/e\u0301"},
],
[
{source: "/tmp/\u00E5", target: "/sandbox/\u00E9"},
{source: "/tmp/a\u030A", target: "/sandbox/e\u0301"},
],
[
{source: "/tmp/\u00F1", target: "/sandbox/a"},
{source: "/tmp/n\u0303", target: "/sandbox/a\u0301"},
],
];
function key(mount) {
return `${mount.source}\0${mount.target}`;
}
function canonical(mounts) {
return JSON.stringify(
mounts
.map(({source, target}) => ({source, target, readOnly: true}))
.sort((left, right) => key(left).localeCompare(key(right))),
);
}
for (const mounts of cases) {
const comparison = key(mounts[0]).localeCompare(key(mounts[1]));
console.log(JSON.stringify({
keys: mounts.map(key),
comparison,
swappedCanonicalEqual: canonical(mounts) === canonical([...mounts].reverse()),
canonical: canonical(mounts),
}));
}
NODE
printf '%s\n' '== all localeCompare call sites =='
rg -n -C 2 '\.localeCompare\(' src/lib/state/registry/host-mount.ts src/lib/onboard/resume-config.tsRepository: NVIDIA/NemoClaw
Length of output: 1796
🏁 Script executed:
set -euo pipefail
node - <<'NODE'
const mounts = [
{source: "/tmp/\u00F1", target: "/sandbox/\u00E9"},
{source: "/tmp/n\u0303", target: "/sandbox/e\u0301"},
];
const key = ({source, target}) => `${source}\0${target}`;
const canonical = (items) =>
JSON.stringify(
items
.map(({source, target}) => ({source, target, readOnly: true}))
.sort((a, b) => key(a).localeCompare(key(b))),
);
console.log({
comparison: key(mounts[0]).localeCompare(key(mounts[1])),
swappedCanonicalEqual: canonical(mounts) === canonical([...mounts].reverse()),
canonical: canonical(mounts),
});
NODE
rg -n -C 2 '\.localeCompare\(' \
src/lib/state/registry/host-mount.ts \
src/lib/onboard/resume-config.tsRepository: NVIDIA/NemoClaw
Length of output: 1163
Use a locale-independent total order for host-mount keys.
localeCompare() can return 0 for distinct Unicode paths. Stable sorting can preserve different declaration orders, so the same mount set can produce different JSON and trigger false sandbox recreation. Apply the binary comparison in both canonicalHostMounts implementations: src/lib/state/registry/host-mount.ts and src/lib/onboard/resume-config.ts.
🤖 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/state/registry/host-mount.ts` around lines 112 - 119, Update both
canonicalHostMounts implementations to use a locale-independent binary
total-order comparator for the combined source and target keys instead of
localeCompare(). Ensure distinct Unicode paths never compare equal, so
equivalent mount sets serialize identically regardless of declaration order.
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
|
Addressed the remaining automated review findings on the current head
|
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
… into feat/read-only-host-mounts-8274
Summary
Add repeatable, read-only host directory mounts to NemoClaw onboarding on Linux and WSL2. Before this change, users had to copy host project files into a sandbox; after it, they can explicitly expose selected existing directories below
/sandboxwhile NemoClaw validates, records, reports, and safely reuses the declarations.Related Issue
Closes #8274
Changes
nemoclaw onboard --host-mount <absolute-host-directory:/sandbox/directory>with duplicate, path-normalization, directory-existence, and symlink-component validation.read_only: trueand enable OpenShell's Docker bind-mount gate only for runs that require it. This gateway capability switch is needed because OpenShell rejects driver bind mounts unless the managed gateway explicitly opts in; creation-plan tests cover the merged bind/tmpfs driver configuration.Type of Change
Quality Gates
Documentation Writer Review
docs-updateddocs/manage-sandboxes/workspace-files.mdx;docs/reference/commands.mdx;src/lib/onboard.ts;src/lib/onboard/host-mount/index.ts;src/lib/state/registry/host-mount.ts;src/lib/state/registry/types.ts;src/lib/onboard/resume-config.ts;src/lib/onboard/machine/handlers/sandbox-resume.ts;src/lib/onboard/docker-driver-gateway-runtime.ts;src/lib/actions/sandbox/rebuild-gpu-opt-out.ts;src/lib/actions/sandbox/status-text.tsDGX Station Hardware Evidence
Verification
Signed-off-by:line and every commit appears asVerifiedin GitHubpre-commit,commit-msg, andpre-pushhooks passed, ornpm run validate:prpassed after refreshingorigin/mainwhen hooks were skipped or unavailablenpm testbuilt successfully; its all-project parallel run was stopped after widespread unrelated five-second timeouts under local host saturation, so CI is the broad test signalnpm run docsbuilds without warnings (doc changes only) — passed with 0 errors and 2 pre-existing Fern warningsSigned-off-by: Aaron Erickson aerickson@nvidia.com