fix(policies): cross-reference gateway state in policy-list to detect desync#2089
Conversation
📝 WalkthroughWalkthroughAdded Changes
Sequence DiagramsequenceDiagram
participant User as User / CLI
participant Nemoclaw as nemoclaw.ts
participant Registry as policies (local registry)
participant GatewayProc as Gateway process
User->>Nemoclaw: sandboxPolicyList(sandboxName)
Nemoclaw->>Registry: getAppliedPresets(sandboxName)
Registry-->>Nemoclaw: [local preset list]
Nemoclaw->>GatewayProc: getGatewayPresets(sandboxName) (runs `policy get --full`)
alt Gateway Reachable & YAML parseable
GatewayProc-->>Nemoclaw: [gateway preset list]
rect rgba(100, 200, 100, 0.5)
Nemoclaw->>Nemoclaw: compare local vs gateway → determine markers/suffixes
end
else Gateway Unreachable or parse failure
GatewayProc-->>Nemoclaw: null
rect rgba(200, 100, 100, 0.5)
Nemoclaw->>User: Warning: showing local state only
Nemoclaw->>Nemoclaw: use registry-only markers
end
end
Nemoclaw->>User: Render preset list with markers and mismatch suffixes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
1720c42 to
a88d15a
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
test/repro-2010.test.js (1)
106-133: Consider usingvi.spyOnfor more idiomatic Vitest mocking.The direct property mutation with try/finally restore works but
vi.spyOnwithmockImplementationwould integrate better with Vitest's cleanup and provide clearer intent:vi.spyOn(policies, "getAppliedPresets").mockReturnValue([]); vi.spyOn(policies, "getGatewayPresets").mockReturnValue(["telegram"]);Vitest automatically restores spies after each test when configured appropriately. Additionally, the
logSpyat line 106 is created but only used to suppress output—if assertions on log calls aren't needed,vi.spyOn(console, "log").mockImplementation(() => {})can be placed in abeforeEachhook.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/repro-2010.test.js` around lines 106 - 133, Replace direct mutation of policies.getAppliedPresets and policies.getGatewayPresets with Vitest spies: use vi.spyOn(policies, "getAppliedPresets").mockReturnValue([]) and vi.spyOn(policies, "getGatewayPresets").mockReturnValue(["telegram"]) so the test integrates with Vitest's spy lifecycle instead of manually saving/restoring origGetApplied/origGetGateway; also replace the manual logSpy creation/restore by using vi.spyOn(console, "log").mockImplementation(() => {}) in a per-test setup (beforeEach) or keep a local spy but use vi.spyOn to ensure automatic cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@test/repro-2010.test.js`:
- Around line 106-133: Replace direct mutation of policies.getAppliedPresets and
policies.getGatewayPresets with Vitest spies: use vi.spyOn(policies,
"getAppliedPresets").mockReturnValue([]) and vi.spyOn(policies,
"getGatewayPresets").mockReturnValue(["telegram"]) so the test integrates with
Vitest's spy lifecycle instead of manually saving/restoring
origGetApplied/origGetGateway; also replace the manual logSpy creation/restore
by using vi.spyOn(console, "log").mockImplementation(() => {}) in a per-test
setup (beforeEach) or keep a local spy but use vi.spyOn to ensure automatic
cleanup.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8f44cacc-c3e3-47bd-be5f-893f873dcf07
📒 Files selected for processing (3)
src/lib/policies.tssrc/nemoclaw.tstest/repro-2010.test.js
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@test/repro-2010.test.ts`:
- Around line 105-194: These tests currently only assert mocked return values
instead of exercising the real rendering logic in sandboxPolicyList; update each
test to either (A) call sandboxPolicyList() after setting the
policies.getAppliedPresets and policies.getGatewayPresets mocks and assert the
function's output contains the expected marker (●/○) and suffix text for
"telegram", or (B) extract the marker/suffix selection into a small helper
(e.g., determinePresetMarker(presetName, registryPresets, gatewayPresets)) and
replace the in-test boolean logic with direct unit tests of that helper that
assert the exact marker and suffix; reference policies.getAppliedPresets,
policies.getGatewayPresets, and sandboxPolicyList (or the new
determinePresetMarker) when locating where to change the tests.
- Around line 42-100: The tests currently reimplement parsing/key-matching
instead of exercising the exported function; update them to call
policies.getGatewayPresets() directly (stub any gateway response used by
getGatewayPresets, e.g., replace the call that fetches/parses the gateway YAML
or inject buildGatewayYaml(...) as the stubbed response) and assert its returned
array (or empty string) equals the expected presets for the given gateway;
reference policies.getGatewayPresets, buildGatewayYaml, and policies.loadPreset
when locating the test logic to replace so you remove the inline parsing/key
comparisons and instead assert the direct output of getGatewayPresets().
🪄 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: Pro Plus
Run ID: 53d2c3df-0aaa-4191-8c95-510d48cb7708
📒 Files selected for processing (3)
src/lib/policies.tssrc/nemoclaw.tstest/repro-2010.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/nemoclaw.ts
- src/lib/policies.ts
… desync policy-list previously read only from CLI-side registry metadata, not from the gateway actual loaded policy. After rebuild, this caused presets to show as not-applied while the gateway still enforced them. Add getGatewayPresets() which queries the gateway via openshell policy get, parses the response, and matches network_policies keys against known preset definitions. Update sandboxPolicyList() to compare both sources and display discrepancy markers when they disagree. Returns null (gateway unreachable) vs [] (reachable, no matches) so the UI falls back to registry-only display with a warning when the gateway cannot be queried. Closes NVIDIA#2010 Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
…feedback - Add type annotations to fix implicit-any and null-check TS errors - Replace in-process mocking with subprocess tests (CJS destructured imports make vi.spyOn ineffective for closed-over runCapture) - getGatewayPresets tests now exercise the actual matching logic via exported helpers (parseCurrentPolicy, extractPresetEntries, listPresets) - sandboxPolicyList tests now assert real CLI output via subprocess, verifying markers (●/○) and discrepancy suffixes end-to-end - Run prettier to match project formatting
a88d15a to
f7f2cb2
Compare
|
✨ Closes Possibly related open issues: |
ericksoa
left a comment
There was a problem hiding this comment.
LGTM. Clean approach to the registry/gateway desync problem.
- null vs [] semantic for unreachable vs empty is the right design
- All 4 display states (synced/desynced x both directions) plus unreachable fallback covered
- CLI subprocess tests exercise the real rendering logic end-to-end
- CI all green
One note: the getGatewayPresets matching-logic tests reimplement the algorithm in subprocess rather than calling the real function (due to CJS closure capture of runCapture). This is a known trade-off and acceptable — CodeRabbit flagged the same. The CLI output tests provide the real end-to-end coverage.
## Summary Catches up the user-facing reference and troubleshooting docs with the CLI and policy behavior changes that landed in v0.0.21. Drafted via the `nemoclaw-contributor-update-docs` skill against commits in `v0.0.20..v0.0.21`, filtered through `docs/.docs-skip`. ## Changes - **`docs/reference/commands.md`** - `nemoclaw list`: session indicator (●) for connected sandboxes (#2117). - `nemoclaw <name> connect`: active-session note; auto-recovery from SSH identity drift after a host reboot (#2117, #2064). - `nemoclaw <name> status`: three-state Inference line (`healthy` / `unreachable` / `not probed`) covering both local and remote providers; new `Connected` line (#2002, #2117). - `nemoclaw <name> destroy` and `rebuild`: active-session warning with second confirm; rebuild reapplies policy presets to the recreated sandbox (#2117, #2026). - `nemoclaw <name> policy-add` and `policy-remove`: positional preset argument and non-interactive flow via `--yes`/`--force`/`NEMOCLAW_NON_INTERACTIVE=1` (#2070). - `nemoclaw <name> policy-list`: registry-vs-gateway desync detection (#2089). - **`docs/reference/troubleshooting.md`** - `Reconnect after a host reboot`: now reflects automatic stale `known_hosts` pruning on `connect` (#2064). - `Running multiple sandboxes simultaneously`: onboard's forward-port collision guard (#2086). - New section: `openclaw config set` or `unset` is blocked inside the sandbox (#2081). - **`docs/network-policy/customize-network-policy.md`**: non-interactive `policy-add`/`policy-remove` form; preset preservation across rebuild (#2070, #2026). - **`docs/inference/use-local-inference.md`**: NIM section now covers the NGC API key prompt with masked input and `docker login nvcr.io --password-stdin` behavior (#2043). - **Generated skills regenerated** to pick up the source changes (`.agents/skills/nemoclaw-user-reference/references/{commands,troubleshooting}.md`, plus minor heading-flow deltas elsewhere). The pre-commit `Regenerate agent skills from docs` hook ran and confirmed source ↔ generated parity. Commits skipped per `docs/.docs-skip` or no doc impact: `bbbaa0fb` (skip-features), `7cb482cb` (skip-features), `8dee23fd` (skip-terms), plus the usual CI / test / refactor / install-plumbing churn. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [x] Doc only (includes code sample changes) ## Verification - [x] `npx prek run --all-files` passes for the modified files (the one failing test, `test/cli.test.ts > unknown command exits 1`, also fails on `origin/main` and is unrelated to these markdown-only changes) - [ ] `npm test` passes — skipped; same pre-existing CLI-dispatch test failure unrelated to docs - [ ] Tests added or updated for new or changed behavior — n/a, doc-only - [x] No secrets, API keys, or credentials committed - [x] Docs updated for user-facing behavior changes - [ ] `make docs` builds without warnings (doc changes only) — not run locally - [x] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) — n/a, no new pages ## AI Disclosure - [x] AI-assisted — tool: Claude Code --- Signed-off-by: Miyoung Choi <miyoungc@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Multi-session SSH connections with concurrent session support. * Three-state inference health reporting (healthy/unreachable/not probed) across all providers. * Automatic SSH host key rotation detection and recovery. * Non-interactive policy preset management via positional arguments. * Session indicators in sandbox list view. * **Bug Fixes** * Protected destructive operations with active-session warnings. * Policy presets now preserved during sandbox rebuilds. * **Documentation** * NGC registry authentication requirements for container images. * Multi-sandbox onboarding and reconnection guidance. * Troubleshooting updates for port conflicts and SSH issues. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
policy-listpreviously read only from CLI-side registry metadata (~/.nemoclaw/sandboxes.json), not from the gateway's actual loaded policy. Afterrebuild, this caused presets to show as ○ (not applied) while the gateway still enforced them. This PR adds gateway cross-referencing sopolicy-listshows the true state of both sources.Related Issue
Closes #2010
Changes
src/lib/policies.ts— AddgetGatewayPresets(sandboxName)which queries the gateway viaopenshell policy get --full, parses the YAML response, and matchesnetwork_policieskeys against known preset definitions. Returnsnullwhen gateway is unreachable vs[]when reachable but empty.src/nemoclaw.ts— UpdatesandboxPolicyList()to compare registry and gateway state, showing discrepancy markers:● (active on gateway, missing from local state)— the [All Platforms]nemoclaw rebuild: policy-list shows telegram as ○ (not applied) but gateway still allows telegram traffic — policy state inconsistency #2010 scenario○ (recorded locally, not active on gateway)— inverse desync⚠warning when gateway is unreachabletest/repro-2010.test.ts— 9 tests covering:getGatewayPresetsmatching logic via subprocess (telegram-only, multi-preset, npm exclusion, unreachable gateway, empty policy)sandboxPolicyListCLI output via subprocess (all 4 marker/suffix states including gateway-unreachable fallback)Type of Change
Testing
npx prek run --all-filespasses (or equivalentlymake check).npm testpasses.make docsbuilds without warnings. (for doc-only changes)71 tests pass (62 existing policy tests + 9 new). TypeScript typecheck, ESLint, and Prettier all clean.
Checklist
General
Signed-off-by: Yimo Jiang yimoj@nvidia.com