Skip to content

feat(hermes): request network access through policy local#3750

Open
cheese-head wants to merge 9 commits into
NVIDIA:mainfrom
cheese-head:policy-access/installer-docker-preflight
Open

feat(hermes): request network access through policy local#3750
cheese-head wants to merge 9 commits into
NVIDIA:mainfrom
cheese-head:policy-access/installer-docker-preflight

Conversation

@cheese-head

@cheese-head cheese-head commented May 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Defers Docker daemon reachability failures to onboarding preflight in non-interactive installer runs. This lets the installer finish setup and report Docker remediation through the same preflight path instead of failing early in ensure_docker.

Changes

  • Update scripts/install.sh so root and non-root non-interactive flows warn and continue when Docker is installed but not reachable.
  • Add installer regression coverage for non-interactive root and non-root Docker reachability handling.
  • Add e2e parity-map entries and regenerate the legacy assertion inventory for the new policy.local e2e scripts.
  • Fix test fixtures for provider plugin copy behavior and slow gateway cleanup tests.

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: Patrick Riel priel@nvidia.com

Summary by CodeRabbit

Release Notes

  • New Features

    • Added network access request system enabling agents to request temporary access to resources via OpenShell policy proposals
    • Introduced provider access management for credential-backed integrations
    • Added support for 10+ provider profiles (GitHub, Slack, Discord, Telegram, Jira, PyPI, Brave, Brew, HuggingFace, NPM, Local Inference)
    • New OpenClaw tools: openshell_network_access and openshell_provider_access for access workflow management
    • Provider tools installer for CLI utilities (gh, glab, etc.)
  • Documentation

    • New guides on provider access requests workflow and OpenShell integration
  • Improvements

    • Enhanced config file handling with robust JSON parsing
    • Docker image optimizations and provider profile caching

Review Change Stack

@copy-pr-bot

copy-pr-bot Bot commented May 18, 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 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR introduces OpenShell network and provider access request capabilities across NemoClaw. A new TypeScript access-client library communicates with OpenShell's policy.local endpoint via HTTP proxy, manages provider profiles as dynamic presets, and handles access proposal workflows. Two new OpenClaw tools (openshell_provider_access and openshell_network_access) are registered in both NemoClaw SDK and Hermes plugin. Ten provider profile configurations define access rules. E2E tests validate the full request-approval-applied lifecycle.

Changes

Access & Policy System

Layer / File(s) Summary
Build & Installation Infrastructure
Dockerfile, Dockerfile.base, scripts/install-provider-tools.sh, scripts/generate-openclaw-config.py, src/lib/sandbox/build-context.ts, test/sandbox-build-context.test.ts
Runtime Docker stages copy and execute provider-tools installer; OpenClaw config enables nemoclaw plugin path by default; build-context staging includes the installer script.
Access Client Library & HTTP Transport
nemoclaw/src/access-client.ts
TypeScript library defining access request/response types, preset discovery from OpenShell provider profiles or env overrides, HTTP proxy transport to policy.local with custom socket handling, and status mapping for access proposals (pending_approval, applied, denied, failed).
Access Client Tests & Validation
nemoclaw/src/access-client.test.ts
Comprehensive Vitest suite with local HTTP server fixtures validating preset loading, provider profile overrides, request submission, approval polling, proxy transport, error recovery, and status transitions.
Dynamic Provider Profile Presets
src/lib/policy/index.ts
Policy integration reads OpenShell provider profiles via subprocess, converts to presets with endpoint/binary rules, merges with built-in presets, de-duplicates by name, caches in-memory with TTL, and exposes generator/loader helpers.
Provider Profile Onboarding & Sync
src/lib/onboard/provider-profiles.ts
Synchronizes local provider profile YAMLs into OpenShell environment; computes missing profiles; runs lint then import subcommands; handles unsupported gracefully; returns imported/already-present/unsupported status with cleanup.
Provider Profile Onboarding Tests
test/provider-profile-onboard.test.ts
Tests verify shipped provider IDs match fixed list, selective import of missing profiles, already-present detection, lint/import subprocess sequencing, and unsupported fallback.
Provider Profile Configurations
nemoclaw-blueprint/provider-profiles/*.yaml
Ten new provider profiles: brave (Brave Search), brew (Homebrew), discord (Discord messaging), huggingface (HuggingFace ML Hub), jira (Jira/Atlassian), local-inference (local LLM), npm (NPM registry), pypi (PyPI), slack (Slack), telegram (Telegram).
GitHub Preset Enhanced Access
nemoclaw-blueprint/policies/presets/github.yaml, test/validate-blueprint.test.ts
GitHub preset expanded to advertise and allow gh, curl, node binaries alongside existing git access; test updated to expect broader allowlist.
Policy Integration Tests
test/policies.test.ts
Tests validate provider-profile-backed preset listing/loading, merging with built-in presets, and filtering of profiles without valid endpoints.

OpenClaw Tool Registration & Implementation

Layer / File(s) Summary
NemoClaw SDK Tool Registration & Helpers
nemoclaw/src/index.ts
Exports PluginToolResult/PluginToolDefinition types; defines provider/credential hint metadata for tooling; implements helpers for parameter validation, request body building, polling to terminal state, and result conversion for both access tools.
NemoClaw Tool Tests
nemoclaw/src/register.test.ts
Tests verify both openshell_provider_access and openshell_network_access tool registration and action flows: list_presets/check/request with approval wait, provider reuse without duplicates, credential guidance without secrets, and input validation.
Hermes Plugin Network Access Handler
agents/hermes/plugin/__init__.py
Hermes-specific network-only access implementation with provider preset discovery/caching, preset normalization, chunked HTTP response handling, and tool handlers for list_presets/check/request with polling and status mapping.
Hermes Plugin Manifest
agents/hermes/plugin/plugin.yaml
Declares openshell_network_access as provided tool/capability.
Hermes Plugin Handler Tests
test/hermes-plugin-handlers.test.ts
Tests verify Hermes register(ctx) behavior for openshell_network_access tool with multiple action flows and expected HTTP call patterns to policy.local.

Runtime Guidance & Agent Instructions

Layer / File(s) Summary
Runtime Context & Access Workflow
nemoclaw/src/runtime-context.ts
Adds ACCESS_WORKFLOW_LINES constant with provider/network access guidance and injects into standard and fallback <nemoclaw-runtime> context for agent instruction.
Runtime Context Instruction Tests
nemoclaw/src/runtime-context.test.ts
Tests verify access workflow guidance is injected into context with specific tool calls, placeholder expectations, proxy mentions, and provider examples.

Onboarding Integration

Layer / File(s) Summary
Onboarding Orchestration
src/lib/onboard.ts
Adds ensureProviderProfilesAvailable() helper that invokes provider profile sync, logs outcomes, and clears cache; wired into onboarding flow after gateway step completion.
Non-Interactive Docker Reachability
scripts/install.sh
Modifies ensure_docker() to defer Docker daemon unreachability errors to onboarding preflight when running in non-interactive mode instead of failing immediately (both root and non-root paths).
Onboard Config Parsing
nemoclaw/src/onboard/config.ts, nemoclaw/src/onboard/config.test.ts
Adds trimming and returns null for empty/malformed JSON in loadOnboardConfig() with test coverage.

End-to-End Testing & Validation

Layer / File(s) Summary
NemoClaw Policy.local E2E Validation
test/e2e/test-nemoclaw-policy-local-plugin.sh, test/e2e/nemoclaw-policy-local-runner.mjs
Complete Bash test script and Node.js runner validating NemoClaw network access requests from submission through approval and applied status confirmation.
Hermes Policy.local E2E Validation
test/e2e/test-hermes-policy-local-plugin.sh, test/e2e/hermes-policy-local-runner.py
Complete Bash test script and Python runner validating Hermes plugin network access request workflow with sandbox provisioning and status transitions.
E2E Test Parity Documentation
test/e2e/docs/parity-inventory.generated.json, test/e2e/docs/parity-map.yaml
Documents two new zero-assertion E2E test scripts and their status in parity tracking.
Existing Test Suite Updates
test/cli.test.ts, test/generate-openclaw-config.test.ts, test/sandbox-build-context.test.ts, test/sandbox-provisioning.test.ts, test/install-preflight.test.ts
Updates test timeouts, expectations, and fixtures to accommodate new access features and Docker config changes.

Documentation

Layer / File(s) Summary
Provider Access Request Documentation
docs/network-policy/provider-access-requests.md
New page explaining when and how agents request provider-backed credentials via openshell_provider_access, including workflows, placeholder handling, operator boundaries, and related links.
NemoClaw-OpenShell Integration Reference
docs/reference/nemoclaw-openshell-integration.md
Comprehensive reference documenting provider-access vs network-only access models, request flows, credential placeholder behavior with proxy usage patterns, and rationale for tooling architecture.
Documentation Index & Cross-References
docs/index.md, docs/network-policy/approve-network-requests.md
Updates docs index toctree and related-topics links to connect new access documentation.

🎯 4 (Complex) | ⏱️ ~60 minutes

OpenShell, Integration: Hermes, Sandbox

  • ericksoa

🐰 A network of new presets blooms,
Provider access doors unlock with keys,
Policy.local whispers secrets safe,
Tools guide agents through the trees,
No gatekeeping, just boundaries with grace.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.03% 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 'feat(hermes): request network access through policy local' directly reflects the primary feature added—the Hermes plugin's new network access request capability through policy.local integration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch policy-access/installer-docker-preflight

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

@wscurran

Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this detailed PR to enhance the policy for requesting network access through a local policy, which improves the reliability of the Docker setup and E2E testing. This change aims to defer Docker daemon reachability failures to onboarding preflight in non-interactive installer runs, allowing the installer to finish setup and report Docker remediation through the same preflight path.

@cheese-head cheese-head marked this pull request as ready for review May 21, 2026 19:21

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

Caution

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

⚠️ Outside diff range comments (1)
test/e2e/docs/parity-map.yaml (1)

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

Add required SPDX license header to this YAML file.

This file is missing the required SPDX copyright + Apache-2.0 header.

As per coding guidelines: "**/*.{js,ts,tsx,jsx,sh,yaml,yml,json,md,mdx}: Every source file must include an SPDX license header for copyright and Apache-2.0 license".

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

In `@test/e2e/docs/parity-map.yaml` at line 1, Add the required SPDX copyright +
Apache-2.0 header to the top of the YAML file so the file begins with the
standard SPDX license header comment (e.g. a YAML comment block containing the
copyright and "SPDX-License-Identifier: Apache-2.0") above the existing
top-level "scripts:" key in parity-map.yaml.
🧹 Nitpick comments (8)
src/lib/onboard.ts (1)

9751-9751: Run the onboarding E2E set for this orchestration change.

Since this call changes post-gateway onboarding flow, run the recommended jobs before merge:
cloud-e2e, sandbox-operations-e2e, rebuild-openclaw-e2e, channels-stop-start-e2e, messaging-compatible-endpoint-e2e, hermes-discord-e2e, hermes-slack-e2e, and openshell-gateway-upgrade-e2e.

As per coding guidelines src/lib/onboard.ts: “E2E test recommendation: cloud-e2e, sandbox-operations-e2e, rebuild-openclaw-e2e, channels-stop-start-e2e, messaging-compatible-endpoint-e2e, hermes-discord-e2e, hermes-slack-e2e, openshell-gateway-upgrade-e2e.”

🤖 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.ts` at line 9751, This change invokes
ensureProviderProfilesAvailable() and alters post-gateway onboarding flow in
src/lib/onboard.ts; before merging, run the recommended E2E job set: cloud-e2e,
sandbox-operations-e2e, rebuild-openclaw-e2e, channels-stop-start-e2e,
messaging-compatible-endpoint-e2e, hermes-discord-e2e, hermes-slack-e2e, and
openshell-gateway-upgrade-e2e to validate the orchestration change affecting
ensureProviderProfilesAvailable().
Dockerfile (1)

32-33: Run container E2E coverage for these image-layer changes.

These changes alter sandbox image contents and startup-time behavior; run the Docker-targeted E2E jobs before merge to validate runtime parity.

As per coding guidelines: "Dockerfile: This file affects the sandbox container image... E2E test recommendation: cloud-e2e, sandbox-survival-e2e, hermes-e2e, rebuild-openclaw-e2e."

Also applies to: 62-67, 254-255, 633-634

🤖 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 `@Dockerfile` around lines 32 - 33, The Dockerfile change adding COPY of
scripts/install-provider-tools.sh (and the other modified sections around lines
62-67, 254-255, 633-634) modifies sandbox image contents and startup behavior,
so before merging run the full Docker-targeted E2E suite: cloud-e2e,
sandbox-survival-e2e, hermes-e2e, and rebuild-openclaw-e2e to validate runtime
parity and startup behavior; if any test fails, iterate on the Dockerfile and
scripts/install-provider-tools.sh changes until the E2E jobs pass.
Dockerfile.base (1)

75-77: Validate base-image rebuild path with the recommended E2E subset.

Because this changes the cached base image layer content, run the base-impact E2Es to catch regressions that unit tests cannot see.

As per coding guidelines: "Dockerfile.base: This file affects the sandbox container image... E2E test recommendation: cloud-e2e, sandbox-survival-e2e, hermes-e2e, rebuild-openclaw-e2e."

🤖 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 `@Dockerfile.base` around lines 75 - 77, This change modifies Dockerfile.base
by copying and running /usr/local/lib/nemoclaw/install-provider-tools.sh which
affects the cached base image layer; validate the base-image rebuild path by
running the recommended E2E subset (cloud-e2e, sandbox-survival-e2e, hermes-e2e,
rebuild-openclaw-e2e) either locally or by triggering the corresponding CI job,
record the results in the PR (pass/fail and any logs), and if failures appear,
revert or fix the Dockerfile.base / install-provider-tools.sh change and re-run
the same E2E subset until green.
docs/reference/nemoclaw-openshell-integration.md (2)

6-8: ⚡ Quick win

Remove routine bold emphasis in prose (LLM pattern detected).

Bold on basic descriptive bullets should be avoided unless it is a UI label, parameter name, or true warning.

As per coding guidelines: "Unnecessary bold on routine instructions ... should be flagged. LLM pattern detected."

🤖 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/nemoclaw-openshell-integration.md` around lines 6 - 8, The two
bullets use unnecessary bold markup for routine prose; remove the Markdown bold
from the phrases "**Provider access**" and "**Network access**" so they read as
normal text (Provider access and Network access) unless those phrases are actual
UI labels—update the doc content in nemoclaw-openshell-integration.md by
replacing the bolded instances in those list items while keeping the rest of the
sentences unchanged.

3-12: ⚡ Quick win

Apply one-sentence-per-line formatting across prose.

Many sentences are wrapped across multiple lines; please reflow to one sentence per source line.

As per coding guidelines: "One sentence per line in source (makes diffs readable)."

Also applies to: 64-71, 167-169, 178-180

🤖 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/nemoclaw-openshell-integration.md` around lines 3 - 12, The
prose in the NemoClaw/OpenClaw integration section contains multiple sentences
wrapped across lines (e.g., the opening paragraph starting "NemoClaw gives
OpenClaw agents a provider-first way..." and the bullets "Provider access
attaches a host-managed credential..." and "Network access only opens a resource
path..."), violating the one-sentence-per-line guideline; fix by reflowing the
paragraph and each bullet so each individual sentence is placed on its own
source line (preserve existing wording and punctuation), and apply the same
reflow to the other prose blocks noted in the review.
docs/network-policy/provider-access-requests.md (1)

24-31: ⚡ Quick win

Normalize to one sentence per source line.

Several sentences are split across multiple lines, which breaks the docs diff/readability rule.

As per coding guidelines: "One sentence per line in source (makes diffs readable)."

Also applies to: 63-64, 75-77, 114-116, 125-129

🤖 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/provider-access-requests.md` around lines 24 - 31, Split
multi-sentence paragraphs so each sentence occupies its own source line: reflow
the block that begins "NemoClaw agents can ask OpenShell for additional
access..." and place the sentence starting "The sandbox can submit a request,
but it cannot approve its own request." on its own line, do the same for the
sentence starting "OpenShell records the proposal, waits for operator approval,
and only then attaches provider credentials or updates network policy.", and
ensure the "Use provider access when the task needs an account..." vs "Use
network-only access when the task only needs unauthenticated reachability."
sentences are each on separate lines; apply the same one-sentence-per-line
normalization to the other identified groups that start with "Use provider..." /
"Use network-only..." and the sentence groups around the phrases found in the
other noted ranges.
test/hermes-plugin-handlers.test.ts (1)

109-145: ⚡ Quick win

Assert the proposal uses the provider-profile rule, not just the preset name.

This fixture gives github a provider-profile-specific rule, but the test only checks provider_profile metadata and the request path. A merge bug can ignore the provider profile’s rule entirely and still pass. Please assert on calls[0].payload.operations[0].addRule.rule.

Also applies to: 180-185

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

In `@test/hermes-plugin-handlers.test.ts` around lines 109 - 145, The test
currently only checks provider_profile metadata and request path but doesn't
assert that the provider-profile-specific rule was applied; update the test
after invoking ctx.tools["openshell_network_access"]["handler"] so it asserts
calls[0].payload.operations[0].addRule.rule equals the expected provider-profile
rule (the rule defined in module._read_provider_profiles for "github"); also add
the same assertion for the similar assertions around lines 180-185 to ensure the
merge didn't omit the provider profile's rule.
nemoclaw/src/register.test.ts (1)

254-297: ⚡ Quick win

Add a mismatched provider_type regression here.

This fast path is only exercised with a matching type, so the current implementation can still return applied for the wrong attached provider without failing CI. Please add a case where provider_name matches but provider_type differs and verify we do not short-circuit.

🤖 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 `@nemoclaw/src/register.test.ts` around lines 254 - 297, Add a test that covers
the mismatched provider_type fast-path: call the openshell_provider_access tool
via getRegisteredTool(...).execute with provider_name "github" but provider_type
"gitlab" while mockedGetProviderAccess.resolveValue returns an attached provider
with provider_name "github" and provider_type "github", then assert that
mockedGetProviderAccess was still called, that mockedCreateProviderAccessRequest
was invoked (i.e. we did not short-circuit), and that the returned result does
not report status "applied" (assert status is "requested" or similar expected
request status) to ensure the code requires both name and type to match before
short-circuiting.
🤖 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 `@agents/hermes/plugin/__init__.py`:
- Around line 230-237: The merge in _all_presets() only copies provider_profile
into an existing fallback entry, so when a provider preset shares a name (e.g.,
"github") its rule and other fields never override the fallback; change the
merge so provider presets take precedence: when an existing entry is found for
preset["name"] update/replace its fields with the provider preset (e.g.,
existing.update(preset) or replace by_name[preset["name"]] = dict(preset) while
preserving any required metadata), ensuring _rule_for_access_request() will see
the provider preset's rule and not the stale fallback.

In `@docs/network-policy/provider-access-requests.md`:
- Around line 33-38: Add a one-sentence introduction immediately below the "##
Access Types" header that summarizes the table purpose (e.g., "The table below
describes the different access types and what each approval grants.") so readers
get context before the table; update the section containing the "## Access
Types" header and the rows for `openshell_provider_access` and
`openshell_network_access` to ensure the intro appears directly above the table.
- Around line 152-156: The bottom section currently titled "Related Pages"
should be renamed to "Next Steps" to conform to the guideline requiring a Next
Steps section; update the heading text from "Related Pages" to "Next Steps"
while preserving the existing links (Approve or Deny Network Requests, Customize
the Network Policy, NemoClaw Provider and Resource Access Flow) and formatting
so nothing else in the "Related Pages" block (link URLs and order) changes.

In `@docs/reference/nemoclaw-openshell-integration.md`:
- Around line 191-195: Update the CLI snippet block: change the code fence
language tag from "bash" to "console" and add "$ " prompt prefixes to each
command line in the snippet (the curl invocation, the -x "$HTTPS_PROXY" line,
the -H "Authorization: Bearer $GITHUB_TOKEN" line, and the URL line) so the
example follows the docs guideline for console blocks with $ prompts.
- Around line 200-220: Add a "Next Steps" section to the bottom of the document
following the "Why the CLIs Are in the Image" section; create a heading "Next
Steps" and include a short bulleted list linking to related pages such as the
provider approval policy, OpenShell policy reference, sandbox image docs, and
provider integration guide (use their existing internal doc titles/anchors), so
readers have direct navigation to provider policy, credential setup, sandbox
access model, and integration instructions.
- Around line 1-2: Add a YAML frontmatter block at the top of the document
(immediately before the "# NemoClaw Provider and Resource Access Flow" heading)
containing title, description, keywords, topics, tags, content type, difficulty,
audience, and status fields, and then add the SPDX license header block (e.g.,
"SPDX-License-Identifier: Apache-2.0" or the project standard) immediately after
the frontmatter; ensure the frontmatter keys match the repo guideline names and
the SPDX header appears directly following the frontmatter so the file complies
with the "SPDX license header is present after frontmatter" rule.

In `@nemoclaw/src/access-client.ts`:
- Around line 379-384: The cleanProviderEndpoint function currently converts
endpoint.port with Number(...) but lets NaN, non-integers, and out-of-range
values through; update its validation to only accept a numeric integer port in
the TCP range 1–65535 and return null otherwise. Specifically, in
cleanProviderEndpoint check that typeof endpoint.host === "string", parse the
port to a number (or integer) and ensure it is not NaN, is an integer, and 1 <=
port <= 65535 before constructing the NetworkEndpoint object (otherwise return
null); keep using the same function name cleanProviderEndpoint and the output
variable NetworkEndpoint so callers remain unchanged.

In `@nemoclaw/src/index.ts`:
- Around line 877-895: When checking provider readiness, honor an explicit
requested provider_type before reusing an attached provider: read the requested
provider_type with readStringProperty(params, "provider_type") and, after
fetching the attached provider via getProviderAccess(providerName,
clientOptions), verify that the fetched provider's type field (e.g.,
provider.provider_type or provider.type as used in your provider object) matches
the requested provider_type; if it does not match, return a failed
providerAccessToolResult (or a similar result object) explaining the mismatch
instead of reporting the provider as ready. Apply the same check in the
analogous fast-path later in the file (the block around lines referenced
898-910) so both code paths use readStringProperty, getProviderAccess, and
providerAccessToolResult and consistently validate provider_type before
reporting success.

In `@src/lib/onboard.ts`:
- Around line 1795-1805: The new wrapper logic in
ensureProviderProfilesAvailable (calling
providerProfileOnboard.ensureNemoClawProviderProfiles, logging via note, and
calling policies.clearProviderProfileCache) increased the file size and
triggered the entrypoint budget gate; extract that orchestration into a new
module named provider-profiles.ts under the same lib/onboard area, implement a
function (e.g., ensureProviderProfilesOrchestrator) that performs the call to
providerProfileOnboard.ensureNemoClawProviderProfiles, handles the result
branches (unsupported, already-present with skipped), logs with note, and calls
policies.clearProviderProfileCache, then replace the body of
ensureProviderProfilesAvailable with a single thin call that imports and invokes
the new orchestrator to keep onboard.ts net-neutral in size.

In `@test/e2e/docs/parity-inventory.generated.json`:
- Around line 5412-5418: The parity inventory currently lists
test/e2e/test-hermes-policy-local-plugin.sh and
test/e2e/test-nemoclaw-policy-local-plugin.sh as legacy zero-assertion entries;
update those test scripts to emit explicit PASS:/FAIL: (or pass "..." / fail
"...") markers so scripts/e2e/extract-legacy-assertions.ts can extract
assertions, then regenerate test/e2e/docs/parity-inventory.generated.json so
each entry has a non-empty assertions array and no zero_assertion_review.todo;
additionally add the required SPDX license header at the top of
parity-inventory.generated.json (replacing the current file-starting JSON) so
the file includes the SPDX identifier before the JSON payload.

In `@test/e2e/test-hermes-policy-local-plugin.sh`:
- Around line 25-28: Don't mutate global settings permanently: instead of
calling "${OPENSHELL_BIN}" settings set --global --key
agent_policy_proposals_enabled --value true --yes, either set it only for the
test scope by using --local (replace --global with --local) or, if a global
change is required, first capture the current value (e.g.
PREV=$("${OPENSHELL_BIN}" settings get --global --key
agent_policy_proposals_enabled) ), set the new value, and register a cleanup
trap to restore it via "${OPENSHELL_BIN}" settings set --global --key
agent_policy_proposals_enabled --value "$PREV" --yes so the script leaves no
persistent side effects.

In `@test/e2e/test-nemoclaw-policy-local-plugin.sh`:
- Around line 25-28: The test sets the global OpenShell setting
agent_policy_proposals_enabled using "${OPENSHELL_BIN}" settings set but never
restores the previous value; update the test to read and store the existing
value before changing it (e.g., via "${OPENSHELL_BIN}" settings get for
agent_policy_proposals_enabled), then set the value to true for the test, and
finally restore the original value in a teardown/cleanup block (or trap) so the
global setting is returned to its prior state after the test completes.

In `@test/sandbox-build-context.test.ts`:
- Around line 90-93: The test only checked the "others" write bit (0o002),
allowing modes like 0o775 to pass; update the assertion to verify the full
write-bit mask (0o222) so only the owner write bit remains set for
stagedManifestDir. Replace the current check that uses stagedManifestDirMode &
0o002 with a check that (stagedManifestDirMode & 0o222) equals 0o200 (owner
write only), referencing the stagedManifestDirMode variable and the 0o222 mask
to locate and fix the assertion.

---

Outside diff comments:
In `@test/e2e/docs/parity-map.yaml`:
- Line 1: Add the required SPDX copyright + Apache-2.0 header to the top of the
YAML file so the file begins with the standard SPDX license header comment (e.g.
a YAML comment block containing the copyright and "SPDX-License-Identifier:
Apache-2.0") above the existing top-level "scripts:" key in parity-map.yaml.

---

Nitpick comments:
In `@Dockerfile`:
- Around line 32-33: The Dockerfile change adding COPY of
scripts/install-provider-tools.sh (and the other modified sections around lines
62-67, 254-255, 633-634) modifies sandbox image contents and startup behavior,
so before merging run the full Docker-targeted E2E suite: cloud-e2e,
sandbox-survival-e2e, hermes-e2e, and rebuild-openclaw-e2e to validate runtime
parity and startup behavior; if any test fails, iterate on the Dockerfile and
scripts/install-provider-tools.sh changes until the E2E jobs pass.

In `@Dockerfile.base`:
- Around line 75-77: This change modifies Dockerfile.base by copying and running
/usr/local/lib/nemoclaw/install-provider-tools.sh which affects the cached base
image layer; validate the base-image rebuild path by running the recommended E2E
subset (cloud-e2e, sandbox-survival-e2e, hermes-e2e, rebuild-openclaw-e2e)
either locally or by triggering the corresponding CI job, record the results in
the PR (pass/fail and any logs), and if failures appear, revert or fix the
Dockerfile.base / install-provider-tools.sh change and re-run the same E2E
subset until green.

In `@docs/network-policy/provider-access-requests.md`:
- Around line 24-31: Split multi-sentence paragraphs so each sentence occupies
its own source line: reflow the block that begins "NemoClaw agents can ask
OpenShell for additional access..." and place the sentence starting "The sandbox
can submit a request, but it cannot approve its own request." on its own line,
do the same for the sentence starting "OpenShell records the proposal, waits for
operator approval, and only then attaches provider credentials or updates
network policy.", and ensure the "Use provider access when the task needs an
account..." vs "Use network-only access when the task only needs unauthenticated
reachability." sentences are each on separate lines; apply the same
one-sentence-per-line normalization to the other identified groups that start
with "Use provider..." / "Use network-only..." and the sentence groups around
the phrases found in the other noted ranges.

In `@docs/reference/nemoclaw-openshell-integration.md`:
- Around line 6-8: The two bullets use unnecessary bold markup for routine
prose; remove the Markdown bold from the phrases "**Provider access**" and
"**Network access**" so they read as normal text (Provider access and Network
access) unless those phrases are actual UI labels—update the doc content in
nemoclaw-openshell-integration.md by replacing the bolded instances in those
list items while keeping the rest of the sentences unchanged.
- Around line 3-12: The prose in the NemoClaw/OpenClaw integration section
contains multiple sentences wrapped across lines (e.g., the opening paragraph
starting "NemoClaw gives OpenClaw agents a provider-first way..." and the
bullets "Provider access attaches a host-managed credential..." and "Network
access only opens a resource path..."), violating the one-sentence-per-line
guideline; fix by reflowing the paragraph and each bullet so each individual
sentence is placed on its own source line (preserve existing wording and
punctuation), and apply the same reflow to the other prose blocks noted in the
review.

In `@nemoclaw/src/register.test.ts`:
- Around line 254-297: Add a test that covers the mismatched provider_type
fast-path: call the openshell_provider_access tool via
getRegisteredTool(...).execute with provider_name "github" but provider_type
"gitlab" while mockedGetProviderAccess.resolveValue returns an attached provider
with provider_name "github" and provider_type "github", then assert that
mockedGetProviderAccess was still called, that mockedCreateProviderAccessRequest
was invoked (i.e. we did not short-circuit), and that the returned result does
not report status "applied" (assert status is "requested" or similar expected
request status) to ensure the code requires both name and type to match before
short-circuiting.

In `@src/lib/onboard.ts`:
- Line 9751: This change invokes ensureProviderProfilesAvailable() and alters
post-gateway onboarding flow in src/lib/onboard.ts; before merging, run the
recommended E2E job set: cloud-e2e, sandbox-operations-e2e,
rebuild-openclaw-e2e, channels-stop-start-e2e,
messaging-compatible-endpoint-e2e, hermes-discord-e2e, hermes-slack-e2e, and
openshell-gateway-upgrade-e2e to validate the orchestration change affecting
ensureProviderProfilesAvailable().

In `@test/hermes-plugin-handlers.test.ts`:
- Around line 109-145: The test currently only checks provider_profile metadata
and request path but doesn't assert that the provider-profile-specific rule was
applied; update the test after invoking
ctx.tools["openshell_network_access"]["handler"] so it asserts
calls[0].payload.operations[0].addRule.rule equals the expected provider-profile
rule (the rule defined in module._read_provider_profiles for "github"); also add
the same assertion for the similar assertions around lines 180-185 to ensure the
merge didn't omit the provider profile's rule.
🪄 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: a55f6246-6e79-4059-992b-3ed2f9a0552a

📥 Commits

Reviewing files that changed from the base of the PR and between d7bae57 and 2d41377.

📒 Files selected for processing (49)
  • Dockerfile
  • Dockerfile.base
  • agents/hermes/plugin/__init__.py
  • agents/hermes/plugin/plugin.yaml
  • docs/index.md
  • docs/network-policy/approve-network-requests.md
  • docs/network-policy/provider-access-requests.md
  • docs/reference/nemoclaw-openshell-integration.md
  • nemoclaw-blueprint/policies/presets/github.yaml
  • nemoclaw-blueprint/provider-profiles/brave.yaml
  • nemoclaw-blueprint/provider-profiles/brew.yaml
  • nemoclaw-blueprint/provider-profiles/discord.yaml
  • nemoclaw-blueprint/provider-profiles/huggingface.yaml
  • nemoclaw-blueprint/provider-profiles/jira.yaml
  • nemoclaw-blueprint/provider-profiles/local-inference.yaml
  • nemoclaw-blueprint/provider-profiles/npm.yaml
  • nemoclaw-blueprint/provider-profiles/pypi.yaml
  • nemoclaw-blueprint/provider-profiles/slack.yaml
  • nemoclaw-blueprint/provider-profiles/telegram.yaml
  • nemoclaw/src/access-client.test.ts
  • nemoclaw/src/access-client.ts
  • nemoclaw/src/index.ts
  • nemoclaw/src/onboard/config.test.ts
  • nemoclaw/src/onboard/config.ts
  • nemoclaw/src/register.test.ts
  • nemoclaw/src/runtime-context.test.ts
  • nemoclaw/src/runtime-context.ts
  • scripts/generate-openclaw-config.py
  • scripts/install-provider-tools.sh
  • scripts/install.sh
  • src/lib/onboard.ts
  • src/lib/onboard/provider-profiles.ts
  • src/lib/policy/index.ts
  • src/lib/sandbox/build-context.ts
  • test/cli.test.ts
  • test/e2e/docs/parity-inventory.generated.json
  • test/e2e/docs/parity-map.yaml
  • test/e2e/hermes-policy-local-runner.py
  • test/e2e/nemoclaw-policy-local-runner.mjs
  • test/e2e/test-hermes-policy-local-plugin.sh
  • test/e2e/test-nemoclaw-policy-local-plugin.sh
  • test/generate-openclaw-config.test.ts
  • test/hermes-plugin-handlers.test.ts
  • test/install-preflight.test.ts
  • test/policies.test.ts
  • test/provider-profile-onboard.test.ts
  • test/sandbox-build-context.test.ts
  • test/sandbox-provisioning.test.ts
  • test/validate-blueprint.test.ts

Comment on lines +230 to +237
def _all_presets():
by_name = {preset["name"]: dict(preset) for preset in FALLBACK_PRESETS}
for preset in _provider_presets():
existing = by_name.get(preset["name"])
if existing:
existing["provider_profile"] = preset.get("provider_profile")
else:
by_name[preset["name"]] = preset

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Provider profiles never override the fallback rule on name collisions.

When a loaded provider profile reuses a built-in preset name like github, _all_presets() only copies provider_profile onto the fallback entry. _rule_for_access_request() then still builds the proposal from the stale fallback rule, so provider-profile-backed presets never actually drive the requested policy.

Suggested fix
 def _all_presets():
     by_name = {preset["name"]: dict(preset) for preset in FALLBACK_PRESETS}
     for preset in _provider_presets():
-        existing = by_name.get(preset["name"])
-        if existing:
-            existing["provider_profile"] = preset.get("provider_profile")
-        else:
-            by_name[preset["name"]] = preset
+        by_name[preset["name"]] = dict(preset)
     return [by_name[name] for name in sorted(by_name)]
🤖 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 `@agents/hermes/plugin/__init__.py` around lines 230 - 237, The merge in
_all_presets() only copies provider_profile into an existing fallback entry, so
when a provider preset shares a name (e.g., "github") its rule and other fields
never override the fallback; change the merge so provider presets take
precedence: when an existing entry is found for preset["name"] update/replace
its fields with the provider preset (e.g., existing.update(preset) or replace
by_name[preset["name"]] = dict(preset) while preserving any required metadata),
ensuring _rule_for_access_request() will see the provider preset's rule and not
the stale fallback.

Comment on lines +33 to +38
## Access Types

| Type | Tool | What approval grants |
| ---- | ---- | -------------------- |
| Provider access | `openshell_provider_access` | A host-managed provider attachment, provider policy, and credential placeholders when the provider has credentials. |
| Network-only access | `openshell_network_access` | Network reachability for an approved preset. It does not attach credentials or account identity. |

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add an introductory sentence under Access Types.

## Access Types starts directly with a table, but new docs sections should open with a brief introductory sentence.

As per coding guidelines: "Sections use H2 and H3, each starting with an introductory sentence."

🤖 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/provider-access-requests.md` around lines 33 - 38, Add a
one-sentence introduction immediately below the "## Access Types" header that
summarizes the table purpose (e.g., "The table below describes the different
access types and what each approval grants.") so readers get context before the
table; update the section containing the "## Access Types" header and the rows
for `openshell_provider_access` and `openshell_network_access` to ensure the
intro appears directly above the table.

Comment on lines +152 to +156
## Related Pages

- [Approve or Deny Network Requests](approve-network-requests.md) explains the operator approval workflow for access proposals.
- [Customize the Network Policy](customize-network-policy.md) explains persistent policy edits and presets.
- [NemoClaw Provider and Resource Access Flow](../reference/nemoclaw-openshell-integration.md) provides the architecture reference for this feature.

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use a Next Steps section name at the bottom.

This new page ends with Related Pages; the required bottom section label is Next Steps.

As per coding guidelines: "A 'Next Steps' section at the bottom links to related pages."

🤖 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/provider-access-requests.md` around lines 152 - 156, The
bottom section currently titled "Related Pages" should be renamed to "Next
Steps" to conform to the guideline requiring a Next Steps section; update the
heading text from "Related Pages" to "Next Steps" while preserving the existing
links (Approve or Deny Network Requests, Customize the Network Policy, NemoClaw
Provider and Resource Access Flow) and formatting so nothing else in the
"Related Pages" block (link URLs and order) changes.

Comment on lines +1 to +2
# NemoClaw Provider and Resource Access Flow

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add required frontmatter and SPDX header.

This new docs page is missing frontmatter metadata and the SPDX license header block.

As per coding guidelines: "When reviewing new pages, verify: SPDX license header is present after frontmatter" and "Frontmatter includes title, description, keywords, topics, tags, content type, difficulty, audience, and status fields."

🤖 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/nemoclaw-openshell-integration.md` around lines 1 - 2, Add a
YAML frontmatter block at the top of the document (immediately before the "#
NemoClaw Provider and Resource Access Flow" heading) containing title,
description, keywords, topics, tags, content type, difficulty, audience, and
status fields, and then add the SPDX license header block (e.g.,
"SPDX-License-Identifier: Apache-2.0" or the project standard) immediately after
the frontmatter; ensure the frontmatter keys match the repo guideline names and
the SPDX header appears directly following the frontmatter so the file complies
with the "SPDX license header is present after frontmatter" rule.

Comment on lines +191 to +195
```bash
curl -x "$HTTPS_PROXY" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/user
```

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Switch CLI example to console block with $ prompt.

This CLI snippet uses bash; docs CLI examples must use console and include $ prompt prefixes.

As per coding guidelines: "CLI code blocks must use the console language tag with $ prompt prefix. Flag bash or shell for CLI examples."

🤖 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/nemoclaw-openshell-integration.md` around lines 191 - 195,
Update the CLI snippet block: change the code fence language tag from "bash" to
"console" and add "$ " prompt prefixes to each command line in the snippet (the
curl invocation, the -x "$HTTPS_PROXY" line, the -H "Authorization: Bearer
$GITHUB_TOKEN" line, and the URL line) so the example follows the docs guideline
for console blocks with $ prompts.

Comment thread src/lib/onboard.ts
Comment on lines +1795 to +1805
function ensureProviderProfilesAvailable(): void {
const result = providerProfileOnboard.ensureNemoClawProviderProfiles(runOpenshell, {
log: note,
});
if (result.status === "unsupported") {
note(` ${result.message}`);
} else if (result.status === "already-present" && result.skipped.length > 0) {
note(` NemoClaw provider profiles already registered: ${result.skipped.join(", ")}`);
}
policies.clearProviderProfileCache();
}

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Entrypoint budget gate is failing with this helper in onboard.ts.

Line 1795 introduces additional orchestration in this file, and CI is already failing onboard-entrypoint-budget (+16 lines). Please move this wrapper logic into src/lib/onboard/provider-profiles.ts and keep only a thin call here so src/lib/onboard.ts returns to net-neutral growth.

🤖 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.ts` around lines 1795 - 1805, The new wrapper logic in
ensureProviderProfilesAvailable (calling
providerProfileOnboard.ensureNemoClawProviderProfiles, logging via note, and
calling policies.clearProviderProfileCache) increased the file size and
triggered the entrypoint budget gate; extract that orchestration into a new
module named provider-profiles.ts under the same lib/onboard area, implement a
function (e.g., ensureProviderProfilesOrchestrator) that performs the call to
providerProfileOnboard.ensureNemoClawProviderProfiles, handles the result
branches (unsupported, already-present with skipped), logs with note, and calls
policies.clearProviderProfileCache, then replace the body of
ensureProviderProfilesAvailable with a single thin call that imports and invokes
the new orchestrator to keep onboard.ts net-neutral in size.

Comment on lines +5412 to +5418
{
"script": "test/e2e/test-hermes-policy-local-plugin.sh",
"assertions": [],
"zero_assertion_review": {
"reason": "TODO: review legacy entrypoint for assertions not expressed as PASS/FAIL output"
}
},

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.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify whether the new E2E scripts emit recognizable PASS/FAIL markers
# and whether extraction logic can classify them.
echo "== Inspect policy.local scripts for assertion markers =="
rg -n -C2 'PASS|FAIL|\$PASS|\$FAIL|assert|check' test/e2e/test-hermes-policy-local-plugin.sh test/e2e/test-nemoclaw-policy-local-plugin.sh || true

echo
echo "== Inspect extractor heuristics used to populate parity inventory =="
rg -n -C3 'extract-legacy-assertions|PASS|FAIL|zero_assertion|assertions' scripts/e2e/extract-legacy-assertions.ts || true

echo
echo "== Confirm parity inventory entries for the two scripts =="
rg -n -C2 'test-hermes-policy-local-plugin.sh|test-nemoclaw-policy-local-plugin.sh|zero_assertion_review' test/e2e/docs/parity-inventory.generated.json

Repository: NVIDIA/NemoClaw

Length of output: 7420


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect extractor logic around how it detects PASS/FAIL markers
echo "== Extractor: find where PASS/FAIL are matched =="
rg -n "PASS|FAIL|pass|fail|extractDirectOutput|extractAssertionsFromText|mapped" scripts/e2e/extract-legacy-assertions.ts

echo
echo "== Extractor: show extractAssertionsFromText + helpers =="
sed -n '120,260p' scripts/e2e/extract-legacy-assertions.ts

echo
echo "== Scripts: search for PASS:/FAIL: markers (case-sensitive) =="
rg -n 'PASS:\s|FAIL:\s' test/e2e/test-hermes-policy-local-plugin.sh test/e2e/test-nemoclaw-policy-local-plugin.sh || true

echo
echo "== Scripts: search for PASS/FAIL words (case-insensitive) =="
rg -n -i '\bpass\b|\bfail\b' test/e2e/test-hermes-policy-local-plugin.sh test/e2e/test-nemoclaw-policy-local-plugin.sh || true

Repository: NVIDIA/NemoClaw

Length of output: 6068


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== File header check (first 30 lines) =="
sed -n '1,30p' test/e2e/docs/parity-inventory.generated.json | cat -n

Repository: NVIDIA/NemoClaw

Length of output: 1376


Avoid shipping new policy.local tests as zero-assertion legacy entries.

  • test/e2e/docs/parity-inventory.generated.json now lists test/e2e/test-hermes-policy-local-plugin.sh and test/e2e/test-nemoclaw-policy-local-plugin.sh as assertions: [] with the legacy zero_assertion_review.reason TODO (lines 5412-5418 and 8955-8961).
  • scripts/e2e/extract-legacy-assertions.ts only extracts legacy assertions from PASS:/FAIL: (and pass "..."/fail "..." calls); these scripts contain none of the expected markers, so they can’t be validated by the parity tooling as PASS/FAIL-covered entrypoints.
  • test/e2e/docs/parity-inventory.generated.json is missing the required SPDX license header (file starts immediately with { "generated_by": ... }).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/docs/parity-inventory.generated.json` around lines 5412 - 5418, The
parity inventory currently lists test/e2e/test-hermes-policy-local-plugin.sh and
test/e2e/test-nemoclaw-policy-local-plugin.sh as legacy zero-assertion entries;
update those test scripts to emit explicit PASS:/FAIL: (or pass "..." / fail
"...") markers so scripts/e2e/extract-legacy-assertions.ts can extract
assertions, then regenerate test/e2e/docs/parity-inventory.generated.json so
each entry has a non-empty assertions array and no zero_assertion_review.todo;
additionally add the required SPDX license header at the top of
parity-inventory.generated.json (replacing the current file-starting JSON) so
the file includes the SPDX identifier before the JSON payload.

Comment on lines +25 to +28
"${OPENSHELL_BIN}" settings set --global \
--key agent_policy_proposals_enabled \
--value true \
--yes

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid persistent global config side effects in this E2E script.

Line 25 enables agent_policy_proposals_enabled globally, but cleanup never restores the prior value. That can contaminate subsequent tests in shared environments.

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

In `@test/e2e/test-hermes-policy-local-plugin.sh` around lines 25 - 28, Don't
mutate global settings permanently: instead of calling "${OPENSHELL_BIN}"
settings set --global --key agent_policy_proposals_enabled --value true --yes,
either set it only for the test scope by using --local (replace --global with
--local) or, if a global change is required, first capture the current value
(e.g. PREV=$("${OPENSHELL_BIN}" settings get --global --key
agent_policy_proposals_enabled) ), set the new value, and register a cleanup
trap to restore it via "${OPENSHELL_BIN}" settings set --global --key
agent_policy_proposals_enabled --value "$PREV" --yes so the script leaves no
persistent side effects.

Comment on lines +25 to +28
"${OPENSHELL_BIN}" settings set --global \
--key agent_policy_proposals_enabled \
--value true \
--yes

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restore global settings after the test to avoid cross-test state leakage.

Line 25 mutates a global OpenShell setting and never restores it. This can make later E2E jobs pass/fail depending on execution order.

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

In `@test/e2e/test-nemoclaw-policy-local-plugin.sh` around lines 25 - 28, The test
sets the global OpenShell setting agent_policy_proposals_enabled using
"${OPENSHELL_BIN}" settings set but never restores the previous value; update
the test to read and store the existing value before changing it (e.g., via
"${OPENSHELL_BIN}" settings get for agent_policy_proposals_enabled), then set
the value to true for the test, and finally restore the original value in a
teardown/cleanup block (or trap) so the global setting is returned to its prior
state after the test completes.

Comment on lines +90 to 93
const stagedManifestDirMode = fs.statSync(stagedManifestDir).mode & 0o777;
expect(stagedManifestDirMode & 0o555).toBe(0o555);
expect(stagedManifestDirMode & 0o002).toBe(0);
expect((fs.statSync(stagedManifest).mode & 0o777).toString(8)).toBe("644");

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Permission mask check misses owner/group write bits.

Line 92 only asserts “others write” is off. Modes like 0o775 still pass, which weakens this regression guard. Use a full write-bit mask check instead.

Suggested fix
-    expect(stagedManifestDirMode & 0o002).toBe(0);
+    expect(stagedManifestDirMode & 0o222).toBe(0);
📝 Committable suggestion

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

Suggested change
const stagedManifestDirMode = fs.statSync(stagedManifestDir).mode & 0o777;
expect(stagedManifestDirMode & 0o555).toBe(0o555);
expect(stagedManifestDirMode & 0o002).toBe(0);
expect((fs.statSync(stagedManifest).mode & 0o777).toString(8)).toBe("644");
const stagedManifestDirMode = fs.statSync(stagedManifestDir).mode & 0o777;
expect(stagedManifestDirMode & 0o555).toBe(0o555);
expect(stagedManifestDirMode & 0o222).toBe(0);
expect((fs.statSync(stagedManifest).mode & 0o777).toString(8)).toBe("644");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/sandbox-build-context.test.ts` around lines 90 - 93, The test only
checked the "others" write bit (0o002), allowing modes like 0o775 to pass;
update the assertion to verify the full write-bit mask (0o222) so only the owner
write bit remains set for stagedManifestDir. Replace the current check that uses
stagedManifestDirMode & 0o002 with a check that (stagedManifestDirMode & 0o222)
equals 0o200 (owner write only), referencing the stagedManifestDirMode variable
and the 0o222 mask to locate and fix the assertion.

@cv cv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed with the PR review advisor rubric (static patch review only; not evaluating CI/mergeability). The incremental installer Docker reachability change looks reasonable, but I'm requesting changes because this PR is opened against main and carries the unresolved lower-stack blockers in its diff:

  1. Provider-profile import treats every non-zero openshell provider list-profiles -o json as unsupported instead of throwing for real OpenShell/runtime failures.
  2. Provider endpoint port validation accepts NaN in the policy and plugin client paths.
  3. policy.local proxy transport hardcodes http://policy.local:80... instead of honoring configured OPENSHELL_POLICY_LOCAL_URL host/port/path.
  4. Resource URL normalization only maps GitHub hosts, so non-GitHub URLs become unknown hostnames rather than advertised preset ids.
  5. /v1/providers errors are caught broadly and hidden as older-OpenShell fallback behavior.
  6. Provider-profile collisions preserve fallback rules instead of using provider-profile rules as the source of truth.

Please either retarget this PR onto its parent stack after those blockers are fixed, or include the fixes here. Also, the PR title currently says Hermes policy.local but the incremental change is installer Docker reachability deferral; please rename it to match the diff.

@wscurran wscurran added area: e2e End-to-end tests, nightly failures, or validation infrastructure area: packaging Packages, images, registries, installers, or distribution area: policy Network policy, egress rules, presets, or sandbox policy feature PR adds or expands user-visible functionality platform: container Affects Docker, containerd, Podman, or images and removed area: packaging Packages, images, registries, installers, or distribution Docker labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: e2e End-to-end tests, nightly failures, or validation infrastructure area: policy Network policy, egress rules, presets, or sandbox policy feature PR adds or expands user-visible functionality platform: container Affects Docker, containerd, Podman, or images

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants