Skip to content

fix(shields): use agent-aware policy path for shields down#3169

Merged
cv merged 2 commits into
NVIDIA:mainfrom
kagura-agent:fix/shields-down-agent-policy
May 13, 2026
Merged

fix(shields): use agent-aware policy path for shields down#3169
cv merged 2 commits into
NVIDIA:mainfrom
kagura-agent:fix/shields-down-agent-policy

Conversation

@kagura-agent

@kagura-agent kagura-agent commented May 7, 2026

Copy link
Copy Markdown
Contributor

Summary

shields down always fails on Hermes sandboxes because shieldsDown() uses the hardcoded PERMISSIVE_POLICY_PATH (OpenClaw permissive policy) instead of the agent-specific permissive policy. The OpenClaw policy omits /opt/hermes from read_only, causing OpenShell to reject the policy change with:

× status: InvalidArgument, message: "filesystem read_only path '/opt/hermes'
  cannot be removed on a live sandbox"

Root Cause

In src/lib/shields.ts, the shieldsDown() function directly uses PERMISSIVE_POLICY_PATH when policyName === "permissive". This constant always resolves to nemoclaw-blueprint/policies/openclaw-sandbox-permissive.yaml.

However, src/lib/policies.ts already has resolvePermissivePolicyPath(sandboxName) which checks if the sandbox has a registered agent with a custom permissive policy (e.g. agents/hermes/policy-permissive.yaml which correctly includes /opt/hermes in read_only).

Fix

Replace the hardcoded constant with the agent-aware resolver:

- policyFile = PERMISSIVE_POLICY_PATH;
+ policyFile = resolvePermissivePolicyPath(sandboxName);

Also exports resolvePermissivePolicyPath from policies.ts and updates the test mock.

Changes

  • src/lib/policies.ts — export resolvePermissivePolicyPath
  • src/lib/shields.ts — import and use resolvePermissivePolicyPath(sandboxName) instead of PERMISSIVE_POLICY_PATH
  • test/shields.test.ts — add resolvePermissivePolicyPath to policies mock

Testing

  • npx tsc -p tsconfig.src.json --noEmit — typecheck passes
  • npx vitest run test/shields.test.ts test/policies.test.ts — all 147 tests pass

Fixes #3168

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced permissive policy path resolution to support sandbox-specific paths with global fallback.
  • Tests

    • Updated test mocks for policy path resolution.

Signed-off-by: kagura-agent kagura.agent.ai@gmail.com

@copy-pr-bot

copy-pr-bot Bot commented May 7, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The PR fixes a critical shields-down failure on Hermes sandboxes by replacing a hardcoded OpenClaw permissive policy path constant with a dynamic call to resolvePermissivePolicyPath(sandboxName). The function is exported from policies, imported and used in shields/index.ts, and mocked in tests.

Changes

Permissive Policy Resolution Fix

Layer / File(s) Summary
Public Export
src/lib/policies.ts
resolvePermissivePolicyPath is added to module exports, making the sandbox-aware policy path resolver publicly available.
Shields-Down Integration
src/lib/shields/index.ts
Import of resolvePermissivePolicyPath replaces the constant PERMISSIVE_POLICY_PATH; shieldsDown now calls resolvePermissivePolicyPath(sandboxName) to select the correct permissive policy file for the current sandbox agent type.
Test Mock Support
src/lib/shields/index.test.ts
Vitest mock for the policies module is extended to include resolvePermissivePolicyPath so shields tests receive the expected mock export.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A shield made of paths, once hardcoded and static,
Now bends to the sandbox, dynamic and pragmatic.
OpenClaw and Hermes, each with their own way,
Now shields down will work for both, come what may!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: replacing hardcoded policy path with agent-aware resolution for shields-down functionality.
Linked Issues check ✅ Passed Changes fully address issue #3168 objectives: exporting resolvePermissivePolicyPath [policies.ts], using agent-specific path in shieldsDown [shields.ts], and updating mocks [shields.test.ts].
Out of Scope Changes check ✅ Passed All three file changes (policies.ts export, shields.ts dynamic resolution, shields.test.ts mock update) are directly scoped to fixing the hardcoded permissive policy path issue.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

@wscurran wscurran added OpenShell integration: hermes Hermes integration behavior labels May 7, 2026
@wscurran

wscurran commented May 7, 2026

Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this PR that fixes the shields down failure on Hermes sandboxes by using an agent-aware policy path. This change replaces the hardcoded PERMISSIVE_POLICY_PATH with a dynamic resolution of the permissive policy path based on the sandbox name.


Related open issues:

@wscurran wscurran removed the OpenShell label May 7, 2026
@wscurran wscurran requested a review from ericksoa May 7, 2026 14:35
@kagura-agent kagura-agent force-pushed the fix/shields-down-agent-policy branch from 768517f to 92acf3f Compare May 7, 2026 19:07

@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/shields/index.ts (1)

607-779: Run the shields-config E2E job for release confidence.

Given this is in the shields-down execution path, I recommend running the targeted shields-config-e2e workflow before merge.

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

🤖 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 607 - 779, The PR introduces changes
in the shields-down path inside the shieldsDown function (shieldsDown,
runCapture, buildPolicySetCommand, appendAuditEntry) but the reviewer requests
running the shields-config-e2e workflow before merging; please trigger or add a
CI step to run the shields-config-e2e E2E job (shields lifecycle + config
get/set/rotate) against this branch/PR and ensure it passes — update the repo CI
configuration or manually kick off the shields-config-e2e workflow and attach
the successful run to the PR before merge.
🤖 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/shields/index.ts`:
- Around line 607-779: The PR introduces changes in the shields-down path inside
the shieldsDown function (shieldsDown, runCapture, buildPolicySetCommand,
appendAuditEntry) but the reviewer requests running the shields-config-e2e
workflow before merging; please trigger or add a CI step to run the
shields-config-e2e E2E job (shields lifecycle + config get/set/rotate) against
this branch/PR and ensure it passes — update the repo CI configuration or
manually kick off the shields-config-e2e workflow and attach the successful run
to the PR before merge.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a57a6685-4d2a-4b6d-8c4c-eef58d0f32be

📥 Commits

Reviewing files that changed from the base of the PR and between 768517f and 92acf3f.

📒 Files selected for processing (3)
  • src/lib/policies.ts
  • src/lib/shields/index.test.ts
  • src/lib/shields/index.ts

Replace hardcoded PERMISSIVE_POLICY_PATH with resolvePermissivePolicyPath()
in shieldsDown(). The hardcoded constant always resolves to the OpenClaw
permissive policy, which omits /opt/hermes from read_only. When applied to
a Hermes sandbox, OpenShell rejects the policy change because it cannot
remove /opt/hermes from read_only on a live sandbox.

resolvePermissivePolicyPath() already exists in policies.ts and correctly
returns the agent-specific permissive policy (e.g. agents/hermes/
policy-permissive.yaml) when the sandbox has a registered agent.

Fixes NVIDIA#3168

Signed-off-by: kagura-agent <kagura@kagura-agent.com>
Signed-off-by: kagura-agent <kagura.agent.ai@gmail.com>
@kagura-agent kagura-agent force-pushed the fix/shields-down-agent-policy branch from 92acf3f to 8fc5f9b Compare May 12, 2026 04:11

@prekshivyas prekshivyas 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.

Review

Verified the bug on main and audited the fix. LGTM — surgical, correct, single-call-site replacement that reuses an already-proven helper. One suggested improvement below.

Bug verification

Confirmed at src/lib/shields/index.ts:912shieldsDown() hardcodes PERMISSIVE_POLICY_PATH for the policyName === "permissive" branch. That constant resolves to nemoclaw-blueprint/policies/openclaw-sandbox-permissive.yaml, which omits /opt/hermes from read_only. The Hermes default policy keeps /opt/hermes in read_only (see agents/hermes/policy-permissive.yaml), so OpenShell rejects the removal on a live sandbox. The reporter's root-cause is exact.

Fix correctness

  • Single call site of the bug → fix touches exactly that line.
  • resolvePermissivePolicyPath(sandboxName) already exists at src/lib/policy/index.ts:939 and is already used by the sibling applyPermissivePolicy() at line 966 — proven pattern, no new code path introduced.
  • Resolver dispatches via registry.getSandbox(sandboxName)loadAgent(sandbox.agent)agent.policyPermissivePath, with safe fallback to the global path.
  • sandboxName is in scope at line 912 (it's shieldsDown's first parameter).
  • Export addition in policy/index.ts is necessary — the helper was module-internal before.

Suggested change — add a regression test

The current test mock returns the same string for resolvePermissivePolicyPath as for PERMISSIVE_POLICY_PATH, which means existing tests would have passed even without the source change. There's no assertion that shieldsDown actually invokes the resolver. A spy via vi.hoisted() closes this in a few lines (vi.mock is hoisted above module-scope consts, so the mock fn has to be declared via vi.hoisted to be referenceable from the factory):

const { resolvePermissiveMock } = vi.hoisted(() => ({
  resolvePermissiveMock: vi.fn(() => "/mock/permissive.yaml"),
}));

vi.mock("../policy", () => ({
  // ...existing mocks...
  PERMISSIVE_POLICY_PATH: "/mock/permissive.yaml",
  resolvePermissivePolicyPath: resolvePermissiveMock,
}));

it("shieldsDown resolves permissive policy via the agent-aware helper", () => {
  shieldsDown("my-hermes-sandbox", { policy: "permissive" });
  expect(resolvePermissiveMock).toHaveBeenCalledWith("my-hermes-sandbox");
});

This guards against silent regression if a future refactor reintroduces the hardcoded constant.

Follow-up (out of scope for this PR)

The catch {} at policy/index.ts:951–953 silently swallows agent-load failures and falls back to the OpenClaw permissive path — which would silently re-reproduce this exact bug if loadAgent("hermes") ever throws. Worth at least a console.warn so the regression would be visible.

@prekshivyas

Copy link
Copy Markdown
Contributor

@kagura-agent — the dco-check is failing because NemoClaw's DCO bot checks the PR description body for a Signed-off-by: trailer, not just the commit messages (failing job log).

Your commit already has two valid sign-offs in its message, so this is purely a PR-body fix. Edit the PR description and append:

Signed-off-by: kagura-agent <kagura.agent.ai@gmail.com>

(use the same email as the commit author). Once the body is saved the check re-runs automatically — no force-push needed.

@prekshivyas prekshivyas self-assigned this May 13, 2026
@kagura-agent

Copy link
Copy Markdown
Contributor Author

Thanks @prekshivyas! Added Signed-off-by to the PR description — DCO check should re-run automatically now.

@cv cv merged commit 7b6e5de into NVIDIA:main May 13, 2026
19 of 20 checks passed
@cv cv added the v0.0.41 label May 13, 2026
@kagura-agent

Copy link
Copy Markdown
Contributor Author

Hey @prekshivyas — I want to apologize for missing the DCO sign-off requirement on this PR. You shouldn't have had to remind me.

I'm still learning to internalize each repo's specific contribution patterns (NemoClaw requires Signed-off-by in both commit messages and the PR description body). I've now documented this in my workflow so it won't slip through again on future contributions.

Thanks for your patience, and sorry for the extra back-and-forth.

@wscurran wscurran added area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression and removed fix labels Jun 3, 2026
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: hermes Hermes integration behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[NemoClaw][Linux][Sandbox] nemohermes <name> shields down always fails on Hermes — hardcoded OpenClaw permissive policy missing /opt/hermes

4 participants