Skip to content

refactor(cli): tighten policy and channel parser validation - #2907

Merged
cv merged 17 commits into
mainfrom
refactor/oclif-ux-policy-channel-mutations
May 4, 2026
Merged

refactor(cli): tighten policy and channel parser validation#2907
cv merged 17 commits into
mainfrom
refactor/oclif-ux-policy-channel-mutations

Conversation

@cv

@cv cv commented May 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Tighten oclif-owned validation and help metadata for policy and messaging channel mutation commands. This moves missing custom policy path and channel-name validation into strict command parsing before action dispatch.

Stack Navigation

Changes

  • Added examples for policy-add, policy-remove, and channel add/remove/start/stop commands.
  • Removed the hidden raw policy-add adapter so missing --from-file and --from-dir values are handled by oclif.
  • Made channel mutation commands require a <channel> arg before dispatch.
  • Updated command registry metadata for channel mutation arguments.
  • Added unit and CLI coverage for missing parser-owned values.

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: Carlos Villela cvillela@nvidia.com

Summary by CodeRabbit

  • New Features

    • Added usage examples to channel and policy mutation command help documentation.
  • Bug Fixes

    • Channel mutation commands now enforce the channel argument as required.
    • Policy add command validates that --from-file flag includes a path value.
  • Chores

    • Removed legacy command variant for policy operations.

@cv cv self-assigned this May 3, 2026
@copy-pr-bot

copy-pr-bot Bot commented May 3, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR removes the legacy PolicyAddRawCommand and eliminates legacy dispatch paths for sandbox actions, replacing them with enforced required arguments validated at the CLI parsing level. The channel argument is made required, examples are added to mutation commands, and corresponding tests verify the new validation behavior.

Changes

Remove Legacy Policy Command & Enforce Required Arguments

Layer / File(s) Summary
Type Contract
src/lib/legacy-oclif-dispatch.ts
DispatchResult union type removes LegacyDispatch variant; legacy dispatch kind is no longer exported.
Argument Requirements
src/lib/channels-mutate-cli-commands.ts
channelArg changes from required: false to required: true, affecting all four channel mutation commands. PolicyAddRawCommand export removed from src/lib/policy-mutate-cli-commands.ts.
Dispatch Logic
src/lib/legacy-oclif-dispatch.ts
Removes legacy fallback path for policy-add; no longer checks for missing --from-file flag value. Sandbox actions (doctor, skill, snapshot) now resolve to standard oclif command IDs instead of legacy dispatch. logs help text expanded.
Command Wiring
src/lib/oclif-commands.ts, src/lib/command-registry.ts
PolicyAddRawCommand removed from imports and default command map. Channel/policy mutation command registry entries updated with required <channel> positional argument and optional [--dry-run] flag documentation.
Examples & Tests
src/lib/channels-mutate-cli-commands.ts, src/lib/policy-mutate-cli-commands.ts, src/lib/channels-mutate-cli-commands.test.ts, src/lib/policy-mutate-cli-commands.test.ts, src/lib/legacy-oclif-dispatch.test.ts, test/cli.test.ts, test/policies.test.ts
static examples arrays added to ChannelsAddCommand, ChannelsRemoveCommand, ChannelsStopCommand, ChannelsStartCommand, PolicyAddCommand, PolicyRemoveCommand. Test cases verify required argument validation and error messaging. policy-add test updated to expect sandbox:policy-add command ID (not :raw variant). Integration test verifies both policy and channel commands reject missing required values at parse time.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 A legacy path has hopped away,
Arguments now stand firm and stay,
No raw commands in the fray,
Examples guide the CLI way,
Dispatch is cleaner every day! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'refactor(cli): tighten policy and channel parser validation' accurately summarizes the main change: moving validation into strict oclif command parsing for policy and channel commands.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/oclif-ux-policy-channel-mutations

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

@cv cv added the v0.0.34 label May 4, 2026
cv added a commit that referenced this pull request May 4, 2026
## Summary
Improve the oclif metadata and parser-owned validation for read-only
sandbox diagnostic commands. This keeps public sandbox-scoped help
output stable while making `doctor` and `config get` validation stricter
in the command adapter.

## Stack Navigation
- Position: 14 of 60
- Previous PR: [#2905 — refactor(cli): validate logs flags with
oclif](#2905)
- Next PR: [#2907 — refactor(cli): tighten policy and channel parser
validation](#2907)

## Changes
- Added examples for `connect`, sandbox-scoped `status`, `doctor`,
`config get`, `policy-list`, and `channels list` adapters.
- Made `sandbox:doctor` a strict oclif command with a required sandbox
arg and `--json` flag.
- Moved `config get --format` validation into oclif with `json|yaml`
options and removed the adapter-level manual format check.
- Preserved public `doctor --help` output through legacy dispatch and
expanded diagnostics validation coverage.
- Updated the hidden command registry metadata for `config get` flags.

## Type of Change
- [x] 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
- [x] `npx prek run --all-files` passes
- [x] `npm test` passes
- [x] Tests added or updated for new or changed behavior
- [x] 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](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
Signed-off-by: Carlos Villela <cvillela@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added command usage examples to help documentation for configuration,
status, policy, and channels commands.
  * Enhanced doctor command with explicit `--json` flag support.

* **Improvements**
* Updated configuration command help text to clearly advertise available
options (`--key` and `--format json|yaml`).
* Restricted `--format` flag to `json` and `yaml` values with stricter
validation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv
cv requested a review from prekshivyas May 4, 2026 22:42
@cv
cv changed the base branch from refactor/oclif-ux-sandbox-diagnostics to main May 4, 2026 22:43
@cv
cv marked this pull request as ready for review May 4, 2026 22:43
@cv
cv enabled auto-merge (squash) May 4, 2026 22:43
Signed-off-by: Carlos Villela <cvillela@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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/channels-mutate-cli-commands.test.ts (1)

52-64: ⚡ Quick win

Consider testing all four commands for missing channel validation.

The new test correctly verifies that ChannelsAddCommand rejects when the required channel argument is missing. However, per the PR objectives, all four channel mutation commands (add/remove/start/stop) should enforce this requirement. Consider adding similar test cases for ChannelsRemoveCommand, ChannelsStartCommand, and ChannelsStopCommand to ensure comprehensive coverage of the new validation behavior.

📋 Suggested test additions
  it("requires a channel before dispatch", async () => {
    const runtime = {
      sandboxChannelsAdd: vi.fn().mockResolvedValue(undefined),
      sandboxChannelsRemove: vi.fn().mockResolvedValue(undefined),
      sandboxChannelsStart: vi.fn().mockResolvedValue(undefined),
      sandboxChannelsStop: vi.fn().mockResolvedValue(undefined),
    };
    setChannelsRuntimeBridgeFactoryForTest(() => runtime);

    await expect(ChannelsAddCommand.run(["alpha"], rootDir)).rejects.toThrow(/channel/i);
+   await expect(ChannelsRemoveCommand.run(["alpha"], rootDir)).rejects.toThrow(/channel/i);
+   await expect(ChannelsStartCommand.run(["alpha"], rootDir)).rejects.toThrow(/channel/i);
+   await expect(ChannelsStopCommand.run(["alpha"], rootDir)).rejects.toThrow(/channel/i);

    expect(runtime.sandboxChannelsAdd).not.toHaveBeenCalled();
+   expect(runtime.sandboxChannelsRemove).not.toHaveBeenCalled();
+   expect(runtime.sandboxChannelsStart).not.toHaveBeenCalled();
+   expect(runtime.sandboxChannelsStop).not.toHaveBeenCalled();
  });
🤖 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/channels-mutate-cli-commands.test.ts` around lines 52 - 64, Add
equivalent tests for ChannelsRemoveCommand, ChannelsStartCommand, and
ChannelsStopCommand mirroring the existing ChannelsAddCommand test: reuse
setChannelsRuntimeBridgeFactoryForTest to inject a runtime with
sandboxChannelsRemove/sandboxChannelsStart/sandboxChannelsStop mocked, call
<Command>.run([] or missing channel arg, rootDir) and assert it rejects with a
/channel/i error and that the respective runtime method (sandboxChannelsRemove,
sandboxChannelsStart, sandboxChannelsStop) was not called; follow the same
pattern used for ChannelsAddCommand to ensure consistent validation coverage
across all four commands.
🤖 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/command-registry.ts`:
- Around line 235-258: The command metadata for the four channel commands
currently puts the required positional <channel> into flags, so
canonicalUsageList() omits it; update each entry for "nemoclaw <name> channels
add", "nemoclaw <name> channels remove", "nemoclaw <name> channels stop", and
"nemoclaw <name> channels start" to include "<channel>" in the usage string
(e.g., "nemoclaw <name> channels add <channel>") and leave flags as
"[--dry-run]" only, ensuring canonicalUsageList() will serialize the required
positional correctly.

---

Nitpick comments:
In `@src/lib/channels-mutate-cli-commands.test.ts`:
- Around line 52-64: Add equivalent tests for ChannelsRemoveCommand,
ChannelsStartCommand, and ChannelsStopCommand mirroring the existing
ChannelsAddCommand test: reuse setChannelsRuntimeBridgeFactoryForTest to inject
a runtime with sandboxChannelsRemove/sandboxChannelsStart/sandboxChannelsStop
mocked, call <Command>.run([] or missing channel arg, rootDir) and assert it
rejects with a /channel/i error and that the respective runtime method
(sandboxChannelsRemove, sandboxChannelsStart, sandboxChannelsStop) was not
called; follow the same pattern used for ChannelsAddCommand to ensure consistent
validation coverage across all four commands.
🪄 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: a1dffe3f-9354-440e-a52d-a2c67f271730

📥 Commits

Reviewing files that changed from the base of the PR and between 6cb0e1a and b7a6e64.

📒 Files selected for processing (10)
  • src/lib/channels-mutate-cli-commands.test.ts
  • src/lib/channels-mutate-cli-commands.ts
  • src/lib/command-registry.ts
  • src/lib/legacy-oclif-dispatch.test.ts
  • src/lib/legacy-oclif-dispatch.ts
  • src/lib/oclif-commands.ts
  • src/lib/policy-mutate-cli-commands.test.ts
  • src/lib/policy-mutate-cli-commands.ts
  • test/cli.test.ts
  • test/policies.test.ts
💤 Files with no reviewable changes (1)
  • src/lib/legacy-oclif-dispatch.ts

Comment on lines 235 to +258
usage: "nemoclaw <name> channels add",
description: "Save credentials and rebuild",
flags: "<channel> [--dry-run]",
group: "Messaging Channels",
scope: "sandbox",
},
{
usage: "nemoclaw <name> channels remove",
description: "Clear credentials and rebuild",
flags: "<channel> [--dry-run]",
group: "Messaging Channels",
scope: "sandbox",
},
{
usage: "nemoclaw <name> channels stop",
description: "Disable channel (keeps credentials)",
flags: "<channel> [--dry-run]",
group: "Messaging Channels",
scope: "sandbox",
},
{
usage: "nemoclaw <name> channels start",
description: "Re-enable a previously stopped channel",
flags: "<channel> [--dry-run]",

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

Move required <channel> into usage.

canonicalUsageList() only serializes usage, so these entries still advertise channels add/remove/start/stop as if they had no required positional argument. Put <channel> in usage and leave flags for [--dry-run].

✏️ Suggested metadata update
   {
-    usage: "nemoclaw <name> channels add",
+    usage: "nemoclaw <name> channels add <channel>",
     description: "Save credentials and rebuild",
-    flags: "<channel> [--dry-run]",
+    flags: "[--dry-run]",
     group: "Messaging Channels",
     scope: "sandbox",
   },
   {
-    usage: "nemoclaw <name> channels remove",
+    usage: "nemoclaw <name> channels remove <channel>",
     description: "Clear credentials and rebuild",
-    flags: "<channel> [--dry-run]",
+    flags: "[--dry-run]",
     group: "Messaging Channels",
     scope: "sandbox",
   },
   {
-    usage: "nemoclaw <name> channels stop",
+    usage: "nemoclaw <name> channels stop <channel>",
     description: "Disable channel (keeps credentials)",
-    flags: "<channel> [--dry-run]",
+    flags: "[--dry-run]",
     group: "Messaging Channels",
     scope: "sandbox",
   },
   {
-    usage: "nemoclaw <name> channels start",
+    usage: "nemoclaw <name> channels start <channel>",
     description: "Re-enable a previously stopped channel",
-    flags: "<channel> [--dry-run]",
+    flags: "[--dry-run]",
     group: "Messaging Channels",
     scope: "sandbox",
   },
🤖 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/command-registry.ts` around lines 235 - 258, The command metadata for
the four channel commands currently puts the required positional <channel> into
flags, so canonicalUsageList() omits it; update each entry for "nemoclaw <name>
channels add", "nemoclaw <name> channels remove", "nemoclaw <name> channels
stop", and "nemoclaw <name> channels start" to include "<channel>" in the usage
string (e.g., "nemoclaw <name> channels add <channel>") and leave flags as
"[--dry-run]" only, ensuring canonicalUsageList() will serialize the required
positional correctly.

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

LGTM. 10 files / +64 / -32 — tightens policy and channel parser-owned validation and removes the temporary PolicyAddRawCommand scaffolding from #2899.

Three intentional public-surface tightenings, all declared and tested:

  1. channelArg flips to required: true<name> channels add (no channel) now rejected at parse time.
  2. PolicyAddRawCommand removed — <name> policy-add --from-file (no path) now caught by oclif's strict parser instead of the hidden raw adapter. Slight error-wording change (oclif's --from-file ... value/argument/path vs the old hand-rolled "--from-file requires a path argument"); same non-zero exit. test/policies.test.ts:1499-1500 relaxes the regex accordingly.
  3. hasMissingFlagValue helper and the missing-value-routing branch in resolveSandboxOclifDispatch are gone — coherent end-to-end cleanup.

Tests: +14/+14/+13 across channels-mutate-cli-commands.test.ts, policy-mutate-cli-commands.test.ts, test/cli.test.ts. The two-line legacy-oclif-dispatch.test.ts adjustment correctly flips the expected commandId from sandbox:policy-add:rawsandbox:policy-add.

CI: pr.yaml fully green including CodeRabbit; pr-self-hosted builds + wsl-e2e still in flight at review time. src/nemoclaw.ts untouched — cumulative orphan debt unchanged.

@cv
cv merged commit 4476042 into main May 4, 2026
12 of 13 checks passed
cv added a commit that referenced this pull request May 5, 2026
## Summary
Improve the oclif shape for sandbox snapshot commands by adding examples
and making the parent command a strict adapter. This keeps public
snapshot help stable while allowing unknown snapshot subcommands to fail
before reaching the snapshot action.

## Stack Navigation
- Position: 16 of 60
- Previous PR: [#2907 — refactor(cli): tighten policy and channel parser
validation](#2907)
- Next PR: [#2909 — refactor(cli): require skill install path in
oclif](#2909)

## Changes
- Added examples for the snapshot parent plus `create`, `list`, and
`restore` subcommands.
- Made the parent `sandbox:snapshot` command strict and sandbox-arg
aware.
- Routed `nemoclaw <name> snapshot --help` through the parent adapter
while preserving public usage text.
- Added adapter and CLI coverage for parent usage and unknown snapshot
subcommands.

## Type of Change
- [x] 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
- [x] `npx prek run --all-files` passes
- [x] `npm test` passes
- [x] Tests added or updated for new or changed behavior
- [x] 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](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
Signed-off-by: Carlos Villela <cvillela@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added usage examples and clearer help documentation for snapshot
commands.

* **Bug Fixes**
* Help flag now shows snapshot usage instead of being treated as an
argument.
* Invalid snapshot subcommands are rejected earlier (extra/unknown args
now error).
* Installer: tightened non-interactive check to fail fast when stdin is
not a TTY.

* **Tests**
* Added tests verifying snapshot help display and invalid-subcommand
error handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com>
latenighthackathon added a commit to latenighthackathon/NemoClaw that referenced this pull request May 5, 2026
…aging (NVIDIA#1691)

When a sandbox is created without enabling Telegram (or Discord, or
Slack) during `nemoclaw onboard`, applying the matching policy preset
via `nemoclaw <name> policy-add` only opens network egress to the
channel API. The bot token, channel configuration, and in-sandbox
bridge are wired up at onboard time, so users who apply the preset
after onboarding without having enabled the channel can reasonably
believe they have enabled messaging when only the firewall has been
widened.

Add `getMessagingPresetWarning()` in `src/lib/policies.ts` and surface
it in `addSandboxPolicy()` (now in `src/lib/policy-channel-actions.ts`
after the recent CLI dispatch refactor) before the apply confirmation
so users see, for example, that the `telegram` preset alone does not
enable Telegram bots and that re-running `nemoclaw onboard` with
Telegram selected is the path to actually enabling the channel.

This is a rebase of an earlier branch onto current main:

- Hook moved from the legacy `src/nemoclaw.ts` dispatcher to the new
  `src/lib/policy-channel-actions.ts:addSandboxPolicy` after NVIDIA#2899 /
  NVIDIA#2901 / NVIDIA#2907 extracted dispatch.
- `getMessagingPresetWarning` got an explicit TS signature
  (`presetName: string): string | null`) to match the rest of
  `src/lib/policies.ts`.
- Replaced the em dash in the warning message with a period for
  consistency with project style.

Originally three commits (warning logic + ordering assertion + tmpDir
cleanup) on the prior branch; consolidated here because the rebase
needed the dispatcher hook ported to a new file.

Closes NVIDIA#1691

Re-ran `npx vitest run test/policies.test.ts` after rebase: 120/120 pass.

Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
@cv
cv deleted the refactor/oclif-ux-policy-channel-mutations branch May 27, 2026 21:18
@wscurran wscurran added the refactor PR restructures code without intended behavior change label Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor PR restructures code without intended behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants