Skip to content

fix(sandbox): replace brew policy preset with first-class brew subcommand#3878

Closed
laitingsheng wants to merge 13 commits into
mainfrom
feat/sandbox-brew-command
Closed

fix(sandbox): replace brew policy preset with first-class brew subcommand#3878
laitingsheng wants to merge 13 commits into
mainfrom
feat/sandbox-brew-command

Conversation

@laitingsheng

@laitingsheng laitingsheng commented May 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The brew policy preset opened Homebrew network endpoints but never installed the brew CLI, so the documented brew install hello validation step had no chance of succeeding. This PR deletes the misleading preset and ships a first-class nemoclaw <sandbox> brew {init,install,uninstall,deinit} lifecycle that actually provisions Homebrew (Linuxbrew) inside the sandbox via the privileged exec channel.

Related Issue

Fixes #3757
Fixes #1767

Changes

  • Add typed BrewRequest action layer (src/lib/actions/sandbox/brew.ts) with shields-up refusal, init-required refusal, formula-name validation, and idempotent init/deinit semantics. brew install --yes paired with NEMOCLAW_NON_INTERACTIVE=1 auto-runs brew init first when Homebrew is not yet bootstrapped.
  • Add oclif command surface: sandbox brew (help), sandbox brew {init,deinit,install,uninstall} with variadic packages for install/uninstall.
  • Factor privilegedSandboxExec into a shared adapter (src/lib/adapters/sandbox/privileged-exec.ts) accepting user?, stdin?, input?, timeout?. Migrate shields/ and sandbox/config/ onto it; drop the two local copies. Resolver now prefers exact match over prefix, so a generated openshell-demo-abc cannot intercept commands targeted at openshell-demo.
  • Track brewInitialised on SandboxEntry (persisted through registerSandbox) so the action can refuse install/uninstall before init, short-circuit a duplicate init, and not be leaked into snapshot-restore clones.
  • brew init writes /etc/profile.d/nemoclaw-linuxbrew.sh so login shells see the linuxbrew prefix on PATH; brew deinit removes it.
  • Delete nemoclaw-blueprint/policies/presets/brew.yaml and drop brew from nemoclaw-blueprint/policies/tiers.yaml (Balanced and Open tiers) plus the matching test/doc fixtures.
  • Migration: add pruneStaleBuiltInPresets in src/lib/policy/index.ts and call it from listSandboxPolicies and the rebuild restoration loop. Sandboxes upgrading from older NemoClaw versions with brew in their policies list get the name silently dropped from the registry on the next interaction, with a warning to stderr and a hint to run rebuild for a clean gateway-policy state (the live gateway rules can't be cleaned in place because the preset YAML is gone). Custom presets are preserved.
  • Add brew-command-e2e job to .github/workflows/nightly-e2e.yaml (TC-BREW-01..07 via test/e2e/test-brew-command.sh) and register it in the workflow_dispatch input list plus all three aggregation lists.
  • Update docs/reference/commands.mdx, docs/network-policy/*, docs/reference/network-policies.mdx, docs/security/best-practices.mdx, docs/get-started/quickstart.mdx to reflect the new shape. .agents/skills/ user skills are deliberately untouched — they regenerate from docs/ during release-prep, not in feature PRs.

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)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • make docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Tinson Lai tinsonl@nvidia.com

Summary by CodeRabbit

  • New Features

    • Sandbox brew CLI: init, install, uninstall, deinit, and help with user docs.
  • Changes

    • Removed Homebrew preset from defaults; replaced by Brave across onboarding, tiers, and presets.
    • New runtime network policy applied by brew init; sandbox registry tracks brew-initialized state.
    • Nightly CI now runs E2E brew command checks.
  • Refactor

    • Centralized privileged sandbox execution and container selection routing.
  • Documentation

    • Updated quickstart, policy, command, and security docs to reflect preset and command changes.
  • Tests

    • New and expanded unit and E2E tests covering brew lifecycle and exec adapters.

Review Change Stack

…mand

Fixes #3757

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: dd24be30-fb95-4706-b30c-bf03bd7b4bc5

📥 Commits

Reviewing files that changed from the base of the PR and between 7ee8ed1 and e3e1015.

📒 Files selected for processing (2)
  • src/lib/adapters/sandbox/privileged-exec.test.ts
  • src/lib/adapters/sandbox/privileged-exec.ts

📝 Walkthrough

Walkthrough

Adds sandbox Brew CLI and action handlers, a reusable privileged-exec adapter, removes the built-in brew preset and updates docs/tests, introduces policy-prune helpers and tests, and wires a nightly brew-command-e2e job with an E2E script.

Changes

Brew command + privileged exec

Layer / File(s) Summary
Privileged exec adapter and tests
src/lib/adapters/sandbox/privileged-exec.ts, src/lib/adapters/sandbox/privileged-exec.test.ts
Docker vs kubectl exec resolution, label-aware container selection, argv builders, stdin/user options, and execution with unit tests.
Refactor consumers to adapter
src/lib/sandbox/config.ts, src/lib/shields/index.ts
Rewires privileged exec usage to the adapter and provides thin wrappers with configured timeouts.
Brew action dispatcher and tests
src/lib/actions/sandbox/brew.ts, src/lib/actions/sandbox/brew.test.ts
Implements runSandboxBrew with help/init/install/uninstall/deinit, validation, embedded init/deinit scripts, runtime policy apply/remove, BrewCommandError, persisted brewInitialised state, and unit tests for flows.
CLI helpers & commands
src/lib/sandbox/brew-command-support.ts, src/commands/sandbox/brew.ts, src/commands/sandbox/brew/*, src/lib/cli/command-registry.test.ts
Adds sandboxName oclif arg, maps BrewCommandError to CLI failure shapes, implements parent sandbox:brew and subcommands, and updates command-registry tests for added commands/tokens.
Policy preset removal & docs
nemoclaw-blueprint/policies/presets/brew.yaml, nemoclaw-blueprint/policies/tiers.yaml, docs/**
Removes the built-in brew preset and its tier references, updates docs to document nemoclaw <name> brew lifecycle, and replaces some docs table entries with brave.
Policy prune and rebuild integration
src/lib/policy/index.ts, src/lib/actions/sandbox/policy-channel.ts, src/lib/actions/sandbox/rebuild.ts, test/policy-prune.test.ts
Adds partitionKnownPresetNames and pruneStaleBuiltInPresets, integrates pruning into listing and restore flows, and adds tests validating pruning behavior.
Nightly workflow job and E2E script
.github/workflows/nightly-e2e.yaml, test/e2e/test-brew-command.sh, .coderabbit.yaml
Adds brew-command-e2e job to nightly workflow, wires it into downstream jobs, adds an E2E bash script validating the brew lifecycle, and updates coderabbit path instructions.
Tests and adjustments
test/*, test/config-set.test.ts, test/repro-2681-group-writable.test.ts
Updates many tests to remove/replace brew preset references, adjusts tier/preset expectations, tightens container-selection tests, and broadens require-cache cleanup in repro tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • #3913 — Changes here (brew commands, brew_runtime preset, brewInitialised flag, and E2E/tests) address the Homebrew bootstrap and missing-binary concerns described in that issue.

Suggested labels

E2E, v0.0.46

Suggested reviewers

  • ericksoa
  • cjagwani
  • jyaunches

Poem

"A rabbit hopped with a patch in hand,
I wired brew to work on demand.
Docs trimmed a preset, tests took a spin,
Nightly checks hum — let the E2E begin! 🐇✨"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing the brew policy preset with a first-class brew subcommand implementation.
Linked Issues check ✅ Passed The PR fully addresses the coding requirements from both linked issues: #3757 and #1767. It provisions a working brew binary inside sandboxes via a first-class lifecycle (init/install/uninstall/deinit), replacing the unusable preset-only approach.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the PR objectives: new brew subcommand infrastructure, privileged-exec adapter extraction, policy preset removal, migration logic, documentation updates, and E2E testing.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/sandbox-brew-command

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: brew-command-e2e, network-policy-e2e
Optional E2E: shields-config-e2e, rebuild-openclaw-e2e, snapshot-commands-e2e, cloud-onboard-e2e

Dispatch hint: brew-command-e2e,network-policy-e2e

Auto-dispatched E2E: network-policy-e2e via nightly-e2e.yaml at e3e101544272429f1be6c28dd4a535cfcd5b2c6anightly run

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • brew-command-e2e (high: bootstraps sandbox and downloads/installs Homebrew packages): Primary required coverage for this PR. It validates the newly added brew command lifecycle end-to-end in a real sandbox: init/install/uninstall/deinit, idempotent init, shields-up refusal, install-without-init refusal, and Linuxbrew PATH behavior.
  • network-policy-e2e (medium-high: real sandbox with policy probes): Required because the PR changes Homebrew policy from a public preset to an internal integration policy, removes it from policy tiers, and touches policy merge/remove code. This E2E validates deny-by-default behavior, live policy-add/dry-run/hot-reload, whitelist behavior, and policy enforcement in a real OpenShell gateway.

Optional E2E

  • shields-config-e2e (medium: real sandbox lifecycle and config mutation checks): Useful adjacent confidence because brew commands intentionally refuse when shields are up and this PR touches src/lib/shields/index.ts. The dedicated brew E2E already checks one shields refusal path, so this broader shields lifecycle test is optional rather than merge-blocking.
  • rebuild-openclaw-e2e (high: older OpenClaw onboard plus rebuild/restore): Optional confidence for changes to rebuild/registry/config interactions. Brew initialization state is persisted in the registry and rebuild code was touched, but the changed brew flow itself is most directly covered by brew-command-e2e.
  • snapshot-commands-e2e (medium: real sandbox snapshot lifecycle): Optional because src/lib/actions/sandbox/snapshot.ts changed and registry/state handling is adjacent. Run if maintainers want extra confidence that snapshot create/list/restore still works after the state-related edits.
  • cloud-onboard-e2e (high: public installer/onboard path with cloud inference secret): Optional confidence for onboarding policy-tier/preset changes, especially removal of brew from tier defaults. Existing unit tests cover tier/preset diffs, and network-policy-e2e exercises policy behavior, so full cloud onboard is not required unless the PR is considered high-risk for onboarding defaults.

New E2E recommendations

  • brew persistence across rebuild/snapshot (medium): Existing brew-command-e2e validates the single-sandbox brew lifecycle, but this PR also changes registry/rebuild/snapshot-adjacent state. There is no clearly dedicated existing E2E proving whether brewInitialised state and the internal brew runtime policy behave correctly after rebuild or snapshot restore.
    • Suggested test: Add a brew persistence E2E that runs brew init, installs a small formula, performs nemoclaw <name> rebuild or snapshot restore, then verifies registry state, runtime policy, and expected brew behavior are consistent.
  • policy tier preset absence for brew (low): Unit tests cover tier YAML changes, but no existing E2E appears to assert that brew is no longer offered/applied as a user-facing preset during onboarding while brew init can still apply the internal runtime policy.
    • Suggested test: Add an onboarding/policy E2E assertion that Balanced/Open onboarding does not list/apply brew as a public preset, then verifies nemoclaw <name> brew init applies only the internal brew runtime policy path.

Dispatch hint

  • Workflow: nightly-e2e.yaml
  • jobs input: brew-command-e2e,network-policy-e2e

Comment thread src/lib/actions/sandbox/brew.test.ts Fixed
Comment thread src/lib/actions/sandbox/brew.test.ts Fixed

@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: 6

🧹 Nitpick comments (5)
docs/reference/commands.mdx (2)

704-704: 💤 Low value

Use more direct command behavior descriptions.

Lines 704, 715, 725, and 734 use the anthropomorphic construction "Refuses when..." to describe command failure conditions.
Command documentation typically uses more direct constructions.

Suggested rewrites
-Refuses when shields are up — run `nemoclaw <name> shields down` first.
+This command fails when shields are up — run `nemoclaw <name> shields down` first.

or:

-Refuses when shields are up or when `brew init` has not been run.
+Returns an error when shields are up or when `brew init` has not been run.

As per coding guidelines: Active voice required when addressing the reader.

Also applies to: 715-715, 725-725, 734-734

🤖 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 704, Replace the anthropomorphic
phrasing "Refuses when shields are up — run `nemoclaw <name> shields down`
first." (and the analogous lines using "Refuses when...") with an active-voice,
direct command description such as "Command fails if shields are up; run
`nemoclaw <name> shields down` first." Update each occurrence so the sentence
uses active voice and addresses the reader directly rather than personifying the
command.

710-710: ⚡ Quick win

Split into one sentence per line.

Line 710 contains two sentences on the same source line, which violates the formatting rule for readable diffs.

Suggested fix
-Homebrew refuses to run as anyone other than the prefix owner, so the agent inside the sandbox cannot invoke `brew install` directly. Bottled binaries dropped by `brew install` (for example `/home/linuxbrew/.linuxbrew/bin/jq`) remain executable from the agent's PATH and survive `shields up`.
+Homebrew refuses to run as anyone other than the prefix owner, so the agent inside the sandbox cannot invoke `brew install` directly.
+Bottled binaries dropped by `brew install` (for example `/home/linuxbrew/.linuxbrew/bin/jq`) remain executable from the agent's PATH and survive `shields up`.

As per coding guidelines: One sentence per line in source (makes diffs readable). Flag paragraphs where multiple sentences appear on the same line.

🤖 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 710, The line containing "Homebrew
refuses to run as anyone other than the prefix owner, so the agent inside the
sandbox cannot invoke `brew install` directly. Bottled binaries dropped by `brew
install` (for example `/home/linuxbrew/.linuxbrew/bin/jq`) remain executable
from the agent's PATH and survive `shields up`." has two sentences on one source
line; split them so each sentence is on its own line: place the sentence
starting with "Homebrew refuses..." on one line and the sentence starting with
"Bottled binaries dropped..." on the next line to satisfy the
one-sentence-per-line rule.
docs/network-policy/integration-policy-examples.mdx (1)

226-226: ⚡ Quick win

Rewrite to active voice.

"Homebrew is installed via a first-class subcommand" uses passive voice.
Active voice is required per the style guide.

Suggested rewrite:

-Homebrew is installed via a first-class subcommand rather than a network preset:
+Install Homebrew via a first-class subcommand rather than a network preset:

or:

-Homebrew is installed via a first-class subcommand rather than a network preset:
+Use the `brew` subcommand to install Homebrew rather than a network preset:

As per coding guidelines: Active voice required. Flag passive constructions.

🤖 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/network-policy/integration-policy-examples.mdx` at line 226, Replace the
passive sentence "Homebrew is installed via a first-class subcommand rather than
a network preset:" with an active-voice construction; for example, rewrite to
"Install Homebrew using a first-class subcommand rather than a network preset."
Update the sentence in the same paragraph containing the phrase "Homebrew is
installed via a first-class subcommand" so the message is active and matches the
style guide.
src/lib/shields/index.ts (1)

18-20: Run shields lifecycle E2E for this exec-path refactor.

Since src/lib/shields/** now routes privileged execution through a new adapter, run the shields lifecycle E2E job to validate lock/unlock + timer restore behavior on this branch.

As per coding guidelines: “E2E test recommendation: shields-config-e2e — shields lifecycle + config get/set/rotate”.

Also applies to: 46-55

🤖 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/shields/index.ts` around lines 18 - 20, The shield adapter refactor
now routes privileged execution through the new adapter (privilegedSandboxExec /
rawPrivilegedSandboxExec in src/lib/shields/index.ts) but the PR lacks
verification; run the shields lifecycle E2E job "shields-config-e2e" to validate
lock/unlock and timer restore behavior and ensure config get/set/rotate still
work; if failures appear, update the adapter wiring around privilegedSandboxExec
(and any call sites that used rawPrivilegedSandboxExec) to match the new
execution path and re-run the E2E until the lifecycle tests pass.
src/lib/adapters/sandbox/privileged-exec.test.ts (1)

31-41: ⚡ Quick win

Add a regression test for exact-match precedence regardless of list order.

Current tests don’t cover the case where a prefix match appears before the exact container name; that case should still pick the exact match.

Suggested test case
 describe("selectDockerDriverSandboxContainer", () => {
+  it("prefers exact match even when prefix match appears first", () => {
+    expect(
+      selectDockerDriverSandboxContainer(
+        "demo",
+        "docker",
+        "openshell-demo-abc\nopenshell-demo\nopenshell-other",
+      ),
+    ).toBe("openshell-demo");
+  });
+
   it("returns the exact docker-driver sandbox container when present", () => {
🤖 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/adapters/sandbox/privileged-exec.test.ts` around lines 31 - 41, Add a
regression test for selectDockerDriverSandboxContainer that verifies exact-match
precedence even when a prefix match appears earlier in the container list:
create a test where the container list contains a prefix (e.g.,
"openshell-demo-abc") before the exact name ("openshell-demo") and assert the
function returns "openshell-demo"; ensure the test uses the same call signature
as the existing tests (selectDockerDriverSandboxContainer("demo", "docker",
"<list>")) and add it alongside the other tests in privileged-exec.test.ts.
🤖 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 @.github/workflows/nightly-e2e.yaml:
- Around line 1435-1438: The Checkout step currently uses actions/checkout@...
with default credential persistence which leaves the repo token available to
subsequent steps; update the "Checkout" step that calls actions/checkout to
explicitly set with: persist-credentials: false so the GITHUB_TOKEN/credentials
are not left in the working directory for downstream shell steps (look for the
step named "Checkout" that invokes actions/checkout and add persist-credentials:
false to its with block).

In `@src/commands/sandbox/brew.ts`:
- Line 13: The static usage string (static usage) advertises a subcommand token
that doesn't exist; update it to reflect the actual parsed argument
(sandboxName) so help output is accurate—replace the current
["<init|deinit|install|uninstall> <name>"] with a single-name usage (e.g.,
["<sandboxName>"] or ["<name>"]) to match the parsing code that reads
sandboxName in the command handler.

In `@src/lib/actions/sandbox/brew.ts`:
- Around line 118-119: The call to registry.updateSandbox in the brew
init/deinit flows (the updateSandbox(sandboxName, { brewInitialised: true }) and
the corresponding deinit call setting false) ignores its boolean result so the
CLI reports success even if the registry entry was missing; update both places
to check the returned boolean and treat a false result as a failure: when
updateSandbox returns false, log a clear error referencing sandboxName and the
attempted brewInitialised value (use processLogger or console.error), and abort
the command (throw or process.exit with non‑zero) so the stale state isn’t
reported as success.

In `@src/lib/adapters/sandbox/privileged-exec.ts`:
- Around line 28-33: The current container selection logic in the return
expression uses a single find with the predicate name === exact ||
name.startsWith(prefix), which can pick a prefix match that appears earlier than
an exact match; change it to first search for an exact match and return it if
found, otherwise search for the first name that startsWith(prefix) and return
that (or null if none). Update the code around the
containerNames.split("\n").map(...).find(...) expression in privileged-exec.ts
to perform two lookups: one for name === exact, then one for
name.startsWith(prefix).

In `@src/lib/state/registry.ts`:
- Line 43: The new optional field brewInitialised on the registry entries is
never persisted in registerSandbox, so re-registering a sandbox can drop that
flag; update registerSandbox to copy/persist brewInitialised into the stored
entry (when creating a new entry and when merging/updating an existing one) by
reading the incoming value (or retaining the existing value if not provided) and
writing it into the registry object saved by the function; ensure both the
creation branch and the update/merge branch in registerSandbox handle the
brewInitialised property so it survives re-registration.

---

Nitpick comments:
In `@docs/network-policy/integration-policy-examples.mdx`:
- Line 226: Replace the passive sentence "Homebrew is installed via a
first-class subcommand rather than a network preset:" with an active-voice
construction; for example, rewrite to "Install Homebrew using a first-class
subcommand rather than a network preset." Update the sentence in the same
paragraph containing the phrase "Homebrew is installed via a first-class
subcommand" so the message is active and matches the style guide.

In `@docs/reference/commands.mdx`:
- Line 704: Replace the anthropomorphic phrasing "Refuses when shields are up —
run `nemoclaw <name> shields down` first." (and the analogous lines using
"Refuses when...") with an active-voice, direct command description such as
"Command fails if shields are up; run `nemoclaw <name> shields down` first."
Update each occurrence so the sentence uses active voice and addresses the
reader directly rather than personifying the command.
- Line 710: The line containing "Homebrew refuses to run as anyone other than
the prefix owner, so the agent inside the sandbox cannot invoke `brew install`
directly. Bottled binaries dropped by `brew install` (for example
`/home/linuxbrew/.linuxbrew/bin/jq`) remain executable from the agent's PATH and
survive `shields up`." has two sentences on one source line; split them so each
sentence is on its own line: place the sentence starting with "Homebrew
refuses..." on one line and the sentence starting with "Bottled binaries
dropped..." on the next line to satisfy the one-sentence-per-line rule.

In `@src/lib/adapters/sandbox/privileged-exec.test.ts`:
- Around line 31-41: Add a regression test for
selectDockerDriverSandboxContainer that verifies exact-match precedence even
when a prefix match appears earlier in the container list: create a test where
the container list contains a prefix (e.g., "openshell-demo-abc") before the
exact name ("openshell-demo") and assert the function returns "openshell-demo";
ensure the test uses the same call signature as the existing tests
(selectDockerDriverSandboxContainer("demo", "docker", "<list>")) and add it
alongside the other tests in privileged-exec.test.ts.

In `@src/lib/shields/index.ts`:
- Around line 18-20: The shield adapter refactor now routes privileged execution
through the new adapter (privilegedSandboxExec / rawPrivilegedSandboxExec in
src/lib/shields/index.ts) but the PR lacks verification; run the shields
lifecycle E2E job "shields-config-e2e" to validate lock/unlock and timer restore
behavior and ensure config get/set/rotate still work; if failures appear, update
the adapter wiring around privilegedSandboxExec (and any call sites that used
rawPrivilegedSandboxExec) to match the new execution path and re-run the E2E
until the lifecycle tests pass.
🪄 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: d7f8e1fa-76d5-4acc-ab98-ecdd14749e60

📥 Commits

Reviewing files that changed from the base of the PR and between 11b1937 and ae649f0.

📒 Files selected for processing (31)
  • .github/workflows/nightly-e2e.yaml
  • docs/get-started/quickstart.mdx
  • docs/network-policy/customize-network-policy.mdx
  • docs/network-policy/integration-policy-examples.mdx
  • docs/reference/commands.mdx
  • docs/reference/network-policies.mdx
  • docs/security/best-practices.mdx
  • nemoclaw-blueprint/policies/presets/brew.yaml
  • nemoclaw-blueprint/policies/tiers.yaml
  • src/commands/sandbox/brew.test.ts
  • src/commands/sandbox/brew.ts
  • src/commands/sandbox/brew/deinit.ts
  • src/commands/sandbox/brew/init.ts
  • src/commands/sandbox/brew/install.ts
  • src/commands/sandbox/brew/uninstall.ts
  • src/lib/actions/sandbox/brew.test.ts
  • src/lib/actions/sandbox/brew.ts
  • src/lib/adapters/sandbox/privileged-exec.test.ts
  • src/lib/adapters/sandbox/privileged-exec.ts
  • src/lib/cli/command-registry.test.ts
  • src/lib/sandbox/brew-command-support.ts
  • src/lib/sandbox/config.ts
  • src/lib/shields/index.ts
  • src/lib/state/registry.ts
  • test/e2e/test-brew-command.sh
  • test/onboard-policy-suggestions.test.ts
  • test/onboard-preset-diff.test.ts
  • test/policies.test.ts
  • test/policy-tiers-onboard.test.ts
  • test/policy-tiers.test.ts
  • test/repro-2681-group-writable.test.ts
💤 Files with no reviewable changes (4)
  • nemoclaw-blueprint/policies/tiers.yaml
  • nemoclaw-blueprint/policies/presets/brew.yaml
  • docs/security/best-practices.mdx
  • docs/network-policy/customize-network-policy.mdx

Comment thread .github/workflows/nightly-e2e.yaml
Comment thread src/commands/sandbox/brew.ts Outdated
Comment thread src/lib/actions/sandbox/brew.ts Outdated
Comment thread src/lib/adapters/sandbox/privileged-exec.ts Outdated
Comment thread src/lib/state/registry.ts
Comment thread test/e2e/test-brew-command.sh
@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26148315609
Target ref: ae649f0bd3833103e9df7208dbf7c885df2ff2c3
Workflow ref: main
Requested jobs: network-policy-e2e,shields-config-e2e
Summary: 2 passed, 0 failed, 0 skipped

Job Result
network-policy-e2e ✅ success
shields-config-e2e ✅ success

- brew init now writes /etc/profile.d/nemoclaw-linuxbrew.sh so login shells see the Homebrew prefix on PATH (deinit removes it).
- registerSandbox persists brewInitialised so the flag survives recovery and re-registration.
- selectDockerDriverSandboxContainer prefers exact match over prefix, so a generated container like openshell-demo-abc cannot intercept commands targeted at openshell-demo.
- Tighten the doc claim about PATH (long-running processes started before brew init keep their original PATH) and fix the broken #nemoclaw-name-brew anchor link.

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
Comment thread src/lib/actions/sandbox/brew.test.ts Fixed
Comment thread src/lib/actions/sandbox/brew.test.ts Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/actions/sandbox/brew.ts`:
- Around line 96-101: The generated profile hook in
src/lib/actions/sandbox/brew.ts uses a single-bracket test with wildcard pattern
matching which treats '*' literally and can duplicate entries; update the guard
that references PROFILE_D_PATH and LINUXBREW_PREFIX so it uses a proper shell
pattern test (e.g. change the condition to use double brackets: [[ -d
${LINUXBREW_PREFIX}/bin && ":${PATH}:" != *":${LINUXBREW_PREFIX}/bin:"* ]] ) or
switch to a case/:$PATH: pattern check to reliably detect presence and only
prepend once; modify the string templates that produce the `if [...] && [...]`
line accordingly.
🪄 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: 61da8faa-4c34-4210-be16-41ff2d1197ae

📥 Commits

Reviewing files that changed from the base of the PR and between ae649f0 and 75dbd2a.

📒 Files selected for processing (8)
  • docs/network-policy/integration-policy-examples.mdx
  • docs/reference/commands.mdx
  • src/lib/actions/sandbox/brew.test.ts
  • src/lib/actions/sandbox/brew.ts
  • src/lib/adapters/sandbox/privileged-exec.test.ts
  • src/lib/adapters/sandbox/privileged-exec.ts
  • src/lib/state/registry.ts
  • test/e2e/test-brew-command.sh
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/commands.mdx

Comment thread src/lib/actions/sandbox/brew.ts
- Brew action treats updateSandbox returning false as a hard failure on init and deinit, so a missing registry entry no longer reports success with stale state.
- Parent brew command usage now reads `<name>` to match the one positional arg it actually parses, instead of advertising a phantom subcommand token.
- brew-command-e2e checkout step sets persist-credentials: false to keep the GITHUB_TOKEN off downstream shell steps.
- E2E test harness wraps each TC in `|| true` and uses a trap for sandbox teardown, so an early failure no longer skips the remaining cases or the cleanup.
- Tighten the install-script assertion to a plain substring match against the full URL, dropping the unescaped-dot regex that CodeQL flagged.

Signed-off-by: Tinson Lai <tinsonl@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.

Caution

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

⚠️ Outside diff range comments (1)
.github/workflows/nightly-e2e.yaml (1)

1426-1456: ⚠️ Potential issue | 🔴 Critical

Add missing path_instructions entry for test/e2e/test-brew-command.sh in .coderabbit.yaml.

The new brew-command-e2e job is added to .github/workflows/nightly-e2e.yaml with a corresponding test script at test/e2e/test-brew-command.sh, but this script lacks a path_instructions entry in .coderabbit.yaml. Per the coding guidelines and the documented requirement in the workflow file itself: "If a new E2E job is added, verify corresponding path_instructions entries exist in .coderabbit.yaml for the source files it covers."

Add a path_instructions entry for test/e2e/test-brew-command.sh following the pattern of other E2E test scripts (e.g., test/e2e/test-cloud-onboard-e2e.sh), documenting that it covers the brew-command-e2e job and the brew command functionality in src/commands/sandbox/brew.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 @.github/workflows/nightly-e2e.yaml around lines 1426 - 1456, Add a
path_instructions entry in .coderabbit.yaml for the new test script
test/e2e/test-brew-command.sh: create a mapping under path_instructions that
lists this file and documents it covers the brew-command-e2e job and the brew
command implementation at src/commands/sandbox/brew.ts, following the same
structure/fields used by existing E2E entries (e.g.,
test/e2e/test-cloud-onboard-e2e.sh) so the workflow and coderabbit config stay
in sync.
🤖 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 @.github/workflows/nightly-e2e.yaml:
- Around line 1426-1456: Add a path_instructions entry in .coderabbit.yaml for
the new test script test/e2e/test-brew-command.sh: create a mapping under
path_instructions that lists this file and documents it covers the
brew-command-e2e job and the brew command implementation at
src/commands/sandbox/brew.ts, following the same structure/fields used by
existing E2E entries (e.g., test/e2e/test-cloud-onboard-e2e.sh) so the workflow
and coderabbit config stay in sync.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f714d216-22ab-4c7c-b70a-d6afe1a29a5d

📥 Commits

Reviewing files that changed from the base of the PR and between 75dbd2a and 621c769.

📒 Files selected for processing (5)
  • .github/workflows/nightly-e2e.yaml
  • src/commands/sandbox/brew.ts
  • src/lib/actions/sandbox/brew.test.ts
  • src/lib/actions/sandbox/brew.ts
  • test/e2e/test-brew-command.sh

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26153972221
Target ref: 621c769f23a905d268cae4770bb8cb1d812d1620
Workflow ref: main
Requested jobs: network-policy-e2e,shields-config-e2e
Summary: 2 passed, 0 failed, 0 skipped

Job Result
network-policy-e2e ✅ success
shields-config-e2e ✅ success

…nit from install

brew install now accepts --yes/-y. When --yes is set alongside the existing
NEMOCLAW_NON_INTERACTIVE=1 env var, install auto-bootstraps Homebrew first if
the sandbox has not been initialised yet, instead of refusing. Interactive
sessions or scripts that omit either signal keep the explicit-init contract.

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@github-actions

github-actions Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor

Recommendation: info only
Confidence: low
Analyzed HEAD: e3e101544272429f1be6c28dd4a535cfcd5b2c6a
Findings: 0 blocker(s), 1 warning(s), 0 suggestion(s)

This is an automated advisory review. A human maintainer must make the final merge decision.

Limitations: Advisor execution failed: Could not configure advisor model openai/openai/gpt-5.5

Workflow run

Full advisor summary

PR Review Advisor

Base: origin/main
Head: HEAD
Analyzed SHA: e3e101544272429f1be6c28dd4a535cfcd5b2c6a
Recommendation: info only
Confidence: low

PR review advisor failed: Could not configure advisor model openai/openai/gpt-5.5

Gate status

  • CI: pending — 14 status context(s) appear pending.
  • Mergeability: fail — mergeStateStatus=BLOCKED
  • Review threads: pass — 11 review thread(s), all resolved.
  • Risky code tested: warning — Risky areas detected (credentials/inference/network, sandbox/policy/SSRF, workflow/enforcement); test files changed, but coverage still needs semantic review.

🔴 Blockers

  • None.

🟡 Warnings

  • PR review advisor unavailable: The automated advisor could not complete: Could not configure advisor model openai/openai/gpt-5.5
    • Recommendation: Re-run the PR Review Advisor or perform a manual review.
    • Evidence: Could not configure advisor model openai/openai/gpt-5.5

🔵 Suggestions

  • None.

Acceptance coverage

  • No linked acceptance clauses were analyzed.

Security review

  • warning — Secrets and Credentials: Advisor unavailable; human review required.
  • warning — Input Validation and Data Sanitization: Advisor unavailable; human review required.
  • warning — Authentication and Authorization: Advisor unavailable; human review required.
  • warning — Dependencies and Third-Party Libraries: Advisor unavailable; human review required.
  • warning — Error Handling and Logging: Advisor unavailable; human review required.
  • warning — Cryptography and Data Protection: Advisor unavailable; human review required.
  • warning — Configuration and Security Headers: Advisor unavailable; human review required.
  • warning — Security Testing: Advisor unavailable; human review required.
  • warning — Holistic Security Posture: Advisor unavailable; human review required.

Test / E2E status

  • Test depth: e2e_required — Runtime/sandbox/infrastructure paths need real execution coverage: .coderabbit.yaml, .github/workflows/nightly-e2e.yaml, docs/get-started/quickstart.mdx, docs/network-policy/customize-network-policy.mdx, docs/network-policy/integration-policy-examples.mdx, docs/reference/commands.mdx, docs/reference/network-policies.mdx, docs/security/best-practices.mdx.
  • E2E Advisor: not_found (not found)

✅ What looks good

  • No positives were identified by the advisor.

Review completeness

  • Advisor execution failed: Could not configure advisor model openai/openai/gpt-5.5
  • Human maintainer review required: yes

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26165857181
Target ref: 4e61536328f99558c8b5c2de7d706a3fbc554686
Workflow ref: main
Requested jobs: network-policy-e2e
Summary: 1 passed, 0 failed, 0 skipped

Job Result
network-policy-e2e ✅ success

- Generated /etc/profile.d/nemoclaw-linuxbrew.sh now uses a portable `case`
  guard instead of `[ != *...* ]`, which under single-bracket test treats `*`
  as a literal and lets repeated sourcing prepend duplicate prefix entries.
- assertBrewInitialised takes an optional extra hint, so brew uninstall no
  longer dangles the install-only "--yes auto-init" suggestion in its
  refusal message. Only brew install passes the hint.

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26168768350
Target ref: 51497136042d3d38bb6948f9ff3335ca6347eaf5
Workflow ref: main
Requested jobs: network-policy-e2e,cloud-onboard-e2e,docs-validation-e2e
Summary: 3 passed, 0 failed, 0 skipped

Job Result
cloud-onboard-e2e ✅ success
docs-validation-e2e ✅ success
network-policy-e2e ✅ success

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26173179557
Target ref: 4dee049b3e02a375d0e6902b787f73c7ba8e378d
Workflow ref: main
Requested jobs: network-policy-e2e,shields-config-e2e,docs-validation-e2e
Summary: 3 passed, 0 failed, 0 skipped

Job Result
docs-validation-e2e ✅ success
network-policy-e2e ✅ success
shields-config-e2e ✅ success

…registry

Sandboxes onboarded under prior NemoClaw versions can still carry preset
names in `policies` (e.g. legacy `brew`) whose YAML has since been removed
from the blueprint. policy-list silently hid them and rebuild restoration
attempted to apply them, hard-failing.

Add `partitionKnownPresetNames` and `pruneStaleBuiltInPresets` in
src/lib/policy/index.ts. The prune helper drops any policy name absent
from both `listPresets()` and the sandbox's `customPolicies`, warns to
stderr, and rewrites the registry so the warning fires at most once per
sandbox. Custom presets are always preserved.

Wire two callers:
- `listSandboxPolicies` (policy-list display) prunes on read.
- Rebuild's "Restoring policy presets" loop partitions the backup
  manifest's saved preset list, warns about stale entries, and only
  iterates the known subset.

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26174408916
Target ref: 041518b90fb11baefe5044135b8cfb584c22b89f
Workflow ref: main
Requested jobs: network-policy-e2e,rebuild-openclaw-e2e,docs-validation-e2e
Summary: 3 passed, 0 failed, 0 skipped

Job Result
docs-validation-e2e ✅ success
network-policy-e2e ✅ success
rebuild-openclaw-e2e ✅ success

- Snapshot restore --to clones most of the source registry entry into the
  destination, but `/home/linuxbrew` is not part of the snapshot manifest.
  Force brewInitialised: false on the cloned entry so a brew-bootstrapped
  source does not fool the destination into thinking Homebrew is present
  before the operator runs `brew init` on it.
- pruneStaleBuiltInPresets now warns that any network rules a removed
  preset installed may still be live on the gateway and points the user
  at `nemoclaw <sb> rebuild` for a clean policy state. The active gateway
  rules can't be cleaned up automatically (the preset YAML is gone) and
  policy-list/policy-remove cannot surface them after the prune.

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26175918454
Target ref: ab3839162dab3f1427b0221eb3fe8107624cfe39
Workflow ref: main
Requested jobs: network-policy-e2e,shields-config-e2e,rebuild-openclaw-e2e,snapshot-commands-e2e
Summary: 4 passed, 0 failed, 0 skipped

Job Result
network-policy-e2e ✅ success
rebuild-openclaw-e2e ✅ success
shields-config-e2e ✅ success
snapshot-commands-e2e ✅ success

- The container prefix fallback in selectDockerDriverSandboxContainer
  could still intercept across sandboxes whose names share a prefix
  (e.g. sandbox `demo` resolving to `openshell-demo-prod` when the
  exact `openshell-demo` is absent and sandbox `demo-prod` exists).
  resolveDockerDriverSandboxContainer now passes the registry's known
  sandbox names so the resolver excludes any prefix candidate whose
  suffix exact-matches another registered sandbox. Default empty list
  preserves existing callers/tests.
- Register `brew-command-e2e` in `.coderabbit.yaml` path_instructions
  for `src/lib/actions/sandbox/brew.ts`,
  `src/commands/sandbox/brew{,/**}.ts`, and the E2E script itself, per
  the nightly-e2e meta-rule that new jobs must have matching coverage
  hints in the CodeRabbit config.

Signed-off-by: Tinson Lai <tinsonl@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.

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/brew.ts (1)

258-261: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Deinit can skip policy cleanup when registry state is stale.

Line 258 returns early when brewInitialised !== true, so Line 267 policy cleanup never runs. If state is desynced, the sandbox can retain __brew_runtime__ network allowances after deinit. Please make deinit always attempt runtime-policy removal, even in the idempotent path.

Also applies to: 267-268

🤖 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/brew.ts` around lines 258 - 261, The early return
guarded by entry.brewInitialised in deinit prevents runtime-policy removal and
can leave __brew_runtime__ network allowances; change control flow so the
runtime-policy cleanup always runs regardless of entry.brewInitialised (either
move the policy-removal calls currently near lines 267-268 to before the return
or remove the early return and conditionally skip only the Homebrew-specific
teardown while always invoking the runtime-policy removal). Reference
entry.brewInitialised, sandboxName and the runtime-policy removal/cleanup calls
so the deinit path always attempts to remove the __brew_runtime__ policy even
when state is stale.
🧹 Nitpick comments (2)
src/lib/actions/sandbox/brew.test.ts (1)

82-95: ⚡ Quick win

Add assertions for runtime-policy apply/remove side effects.

These tests cover bootstrap/deinit execution, but they currently do not assert that policy update commands are actually issued. Since init/deinit now mutate gateway policy, add expectations around buildPolicyGetCommand, buildPolicySetCommand, and run calls in the success paths.

Also applies to: 246-257

🤖 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/brew.test.ts` around lines 82 - 95, The test for
runSandboxBrew("alpha", { kind: "init" }) needs assertions that the gateway
policy commands are invoked: after calling runSandboxBrew add expects that
buildPolicyGetCommand and buildPolicySetCommand were called with the expected
sandbox name ("alpha") and that the mocked run was invoked with the commands
returned by those builders; mirror these assertions in the deinit test (the
block around lines 246-257) to assert
buildPolicyGetCommand/buildPolicySetCommand and run are used for removing the
policy as well, using the same mock call inspection pattern as
privilegedSandboxExec/mock.calls to verify arguments.
docs/security/best-practices.mdx (1)

85-85: ⚡ Quick win

Use a sentence instead of a clause colon in the table cell.

Line 85 uses Landlock layout: no. where the colon does not introduce a list. Please rewrite this as a normal sentence to match docs style.

As per coding guidelines: “Colons should only introduce a list. Flag colons used as general punctuation between clauses.”

🤖 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/security/best-practices.mdx` at line 85, Replace the table cell clause
"Landlock layout: no." with a full sentence that avoids using a colon as
punctuation; update the text to something like "Landlock does not support
layout." or "Layout is not supported by Landlock." so the cell reads as a normal
sentence and matches the document's style; locate the cell containing the exact
string "Landlock layout: no." and make the replacement.
🤖 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/brew.ts`:
- Around line 258-261: The early return guarded by entry.brewInitialised in
deinit prevents runtime-policy removal and can leave __brew_runtime__ network
allowances; change control flow so the runtime-policy cleanup always runs
regardless of entry.brewInitialised (either move the policy-removal calls
currently near lines 267-268 to before the return or remove the early return and
conditionally skip only the Homebrew-specific teardown while always invoking the
runtime-policy removal). Reference entry.brewInitialised, sandboxName and the
runtime-policy removal/cleanup calls so the deinit path always attempts to
remove the __brew_runtime__ policy even when state is stale.

---

Nitpick comments:
In `@docs/security/best-practices.mdx`:
- Line 85: Replace the table cell clause "Landlock layout: no." with a full
sentence that avoids using a colon as punctuation; update the text to something
like "Landlock does not support layout." or "Layout is not supported by
Landlock." so the cell reads as a normal sentence and matches the document's
style; locate the cell containing the exact string "Landlock layout: no." and
make the replacement.

In `@src/lib/actions/sandbox/brew.test.ts`:
- Around line 82-95: The test for runSandboxBrew("alpha", { kind: "init" })
needs assertions that the gateway policy commands are invoked: after calling
runSandboxBrew add expects that buildPolicyGetCommand and buildPolicySetCommand
were called with the expected sandbox name ("alpha") and that the mocked run was
invoked with the commands returned by those builders; mirror these assertions in
the deinit test (the block around lines 246-257) to assert
buildPolicyGetCommand/buildPolicySetCommand and run are used for removing the
policy as well, using the same mock call inspection pattern as
privilegedSandboxExec/mock.calls to verify arguments.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 720145e1-7755-4140-acc1-ecfcb31bf52b

📥 Commits

Reviewing files that changed from the base of the PR and between 6c86951 and 7ee8ed1.

📒 Files selected for processing (8)
  • docs/network-policy/customize-network-policy.mdx
  • docs/network-policy/integration-policy-examples.mdx
  • docs/reference/commands.mdx
  • docs/reference/network-policies.mdx
  • docs/security/best-practices.mdx
  • nemoclaw-blueprint/policies/integrations/brew.yaml
  • src/lib/actions/sandbox/brew.test.ts
  • src/lib/actions/sandbox/brew.ts
💤 Files with no reviewable changes (1)
  • docs/network-policy/customize-network-policy.mdx
✅ Files skipped from review due to trivial changes (3)
  • docs/reference/network-policies.mdx
  • docs/network-policy/integration-policy-examples.mdx
  • docs/reference/commands.mdx

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ❌ Some jobs failed

Run: 26182292353
Target ref: feat/sandbox-brew-command
Workflow ref: feat/sandbox-brew-command
Requested jobs: brew-command-e2e
Summary: 0 passed, 1 failed, 0 skipped

Job Result
brew-command-e2e ❌ failure

Failed jobs: brew-command-e2e. Check run artifacts for logs.

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26182129196
Target ref: 7ee8ed16b6afcde091598912145c460a98d367b3
Workflow ref: main
Requested jobs: network-policy-e2e,shields-config-e2e,snapshot-commands-e2e,rebuild-openclaw-e2e
Summary: 4 passed, 0 failed, 0 skipped

Job Result
network-policy-e2e ✅ success
rebuild-openclaw-e2e ✅ success
shields-config-e2e ✅ success
snapshot-commands-e2e ✅ success

The previous docker-driver privileged-exec path used `docker exec --user <name>`,
which switches UID but inherits HOME from the calling shell. When the target
user was `linuxbrew`, brew's Ruby tried to write to /root/.cache/Homebrew and
hit EACCES because it could not access root's home as the linuxbrew user.

Drop the `--user` flag and route non-root targets through `runuser -u <name> --`
instead — the same wrapper kubectl-path already uses. runuser sets HOME to the
target user's pw_dir, so brew, bundler, and similar tools land in the right
per-user cache/config directories.

Root operations stay flag-free: docker exec lands on the container's USER
directive (root in NemoClaw's image), and withUserPrefix is a no-op for root.

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ❌ Some jobs failed

Run: 26183761287
Target ref: feat/sandbox-brew-command
Workflow ref: feat/sandbox-brew-command
Requested jobs: brew-command-e2e
Summary: 0 passed, 1 failed, 0 skipped

Job Result
brew-command-e2e ❌ failure

Failed jobs: brew-command-e2e. Check run artifacts for logs.

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26183924572
Target ref: e3e101544272429f1be6c28dd4a535cfcd5b2c6a
Workflow ref: main
Requested jobs: network-policy-e2e
Summary: 1 passed, 0 failed, 0 skipped

Job Result
network-policy-e2e ✅ success

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output area: integrations Third-party service integration behavior area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression

Projects

None yet

3 participants