Skip to content

feat: add monitors CLI command (API parity)#81

Merged
callumreid merged 6 commits into
mainfrom
feat/cli-api-parity-monitors
Jul 2, 2026
Merged

feat: add monitors CLI command (API parity)#81
callumreid merged 6 commits into
mainfrom
feat/cli-api-parity-monitors

Conversation

@callumreid

@callumreid callumreid commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the coval monitors command to the CLI, bringing it to parity with the /v1/monitors API endpoints.

What's New

  • coval monitors list [--scope <scope>] — list all monitors
  • coval monitors get <id> — get a monitor by ID
  • coval monitors create — create a monitor with --name, --conditions (JSON), --scope, etc.
  • coval monitors update <id> — PATCH-update a monitor
  • coval monitors delete <id> — delete (soft-delete) a monitor
  • coval monitors test-evaluate <id> --run-id <id> — test-evaluate a monitor against a run
  • coval monitors events <id> [list] — list monitor trigger events

Files Changed

  • src/client/models/monitor.rs — Monitor, MonitorEvent, MonitorCondition, and request/response structs with Tabular impls
  • src/commands/monitors.rs — MonitorCommands enum with all CRUD + test-evaluate + events subcommands
  • src/client/mod.rs — MonitorsClient with list, get, create, update, delete, test_evaluate, list_events methods
  • src/client/models/mod.rs — registered new monitor module
  • src/commands/mod.rs — registered monitors module
  • src/cli.rs — wired Monitors command into Commands enum with context + execute handlers

Verification

Tested all operations against the Coval API (cal-demo org):

# Create
$ coval monitors create --name test-monitor --conditions '[{aggregation:JOB_SUCCESS,operator:EQ,threshold_float:1.0}]'
# → Returns Monitor JSON with ulid

# List
$ coval monitors list
# → Table with 2 monitors shown

# Get
$ coval monitors get 01KW2VQQJS71SC31HN95AKNZ9F
# → Full Monitor JSON

# Update
$ coval monitors update 01KW2VQQJS71SC31HN95AKNZ9F --description "updated via CLI"
# → Returns updated Monitor

# Events
$ coval monitors events 01KW2VQQJS71SC31HN95AKNZ9F
# → No results (no events yet)

# Cleanup
$ coval monitors delete 01KW2VQQJS71SC31HN95AKNZ9F
# → "Monitor deleted."
  • cargo build — passes
  • cargo clippy — no warnings
  • cargo test — 90/90 tests pass

Greptile Summary

Adds the coval monitors command group with full CRUD parity against the /v1/monitors API: list, get, create, update, delete, test-evaluate, and events. This is a large but mechanical addition that follows the same structure as the existing reports, tags, and other resource modules.

  • All three previously-reported issues are resolved: get now correctly unwraps through GetMonitorResponse, evaluation_type is Option<String> with a contains_key guard so --input-json values are never silently overwritten, and --name/--conditions are validated before finish with actionable bail! messages. Two new integration tests cover the evaluation_type preservation logic.
  • Auth tokens in channels and dispatched_channels are redacted at deserialization time via a recursive redact_auth_tokens helper, with unit tests proving the behavior.

Confidence Score: 5/5

Safe to merge — all previously identified runtime defects have been corrected and are now covered by tests.

The three bugs flagged in the prior review round (wrong deserialization path for get, silent default overwrite of evaluation_type, and missing pre-flight validation for required fields) are all fixed and verified with new integration tests. The new monitors module is structurally consistent with the rest of the codebase, and cargo test reports 90/90 passing.

No files require special attention.

Reviews (5): Last reviewed commit: "merge origin/main into monitors CLI PR" | Re-trigger Greptile

Adds the monitors resource to the CLI with full CRUD support:
- monitors list
- monitors get <id>
- monitors create (with --conditions, --name, --scope, etc.)
- monitors update <id> (PATCH semantics)
- monitors delete <id>
- monitors test-evaluate <id> --run-id <id>
- monitors events <id> [list]

Verified working against API with create, list, get, update, delete,
events, and test-evaluate operations.
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR adds a "monitors" resource across the CLI. It introduces Monitor/MonitorEvent data models with an auth-token redaction deserializer, a MonitorsClient with list/get/create/update/delete/test-evaluate/list-events API methods, a new commands::monitors module implementing MonitorCommands and EventCommands with a dispatcher, CLI wiring (Commands::Monitors variant, resource/operation mappings, run() routing), agent-discovery entries for "monitors" and "tags" resources, and corresponding test coverage.

Changes

  • New Monitor/MonitorEvent models with Tabular rendering and auth_token redaction in serialization
  • MonitorsClient added to CovalClient exposing monitor CRUD, test-evaluate, and event listing
  • New commands::monitors module with subcommands and execute() dispatcher handling JSON input merging and validation
  • CLI enum, resource/operation mapping, and run() dispatch updated for the Monitors command
  • agent_discovery RESOURCE_SPECS extended with "monitors" and "tags" entries
  • Test suite updated with new resource coverage and monitors-create evaluation_type tests

Suggested reviewers: borgesius

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly names the new monitors CLI command and the API parity goal.
Description check ✅ Passed The description accurately summarizes the new monitors subcommands, touched files, and verification steps.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment thread src/client/mod.rs
Comment thread src/commands/monitors.rs
Comment thread src/client/models/monitor.rs Outdated
Comment thread .worktrees/cli-parity Outdated
Comment thread src/commands/monitors.rs Outdated
@callumreid

Copy link
Copy Markdown
Contributor Author

🟢

@borgesius borgesius left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Holding approval on one correctness issue: CreateArgs.evaluation_type has a clap default and is always inserted into the request, so --input-json with an evaluation_type gets silently overwritten with ON_RUN_COMPLETE unless the user also passes --evaluation-type. Please make it Option and only insert when the flag is provided, or otherwise avoid overwriting an input-json value.

@callumreid callumreid requested a review from borgesius July 2, 2026 20:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
tests/cli_tests.rs (1)

2175-2256: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Correct tests, but no coverage for the default evaluation_type fallback or other monitors subcommands.

The two tests correctly validate evaluation_type preservation vs. --evaluation-type override, matching the MonitorCommands::Create logic in src/commands/monitors.rs. However, there's no test for the default-value path (when evaluation_type is absent from both --input-json and the flag, it should fall back to DEFAULT_EVALUATION_TYPE), nor any coverage for list, get, update, delete, test-evaluate, or events subcommands.

Consider adding at least one test for the default fallback and a smoke test per remaining subcommand to match the coverage depth given to other resources in this file.

🤖 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 `@tests/cli_tests.rs` around lines 2175 - 2256, The current monitors tests
cover create override behavior, but they miss the default evaluation_type
fallback and the rest of the monitors CLI surface. Add a test around
MonitorCommands::Create that verifies when neither --input-json nor
--evaluation-type supplies a value, the request uses DEFAULT_EVALUATION_TYPE
from src/commands/monitors.rs. Also add lightweight smoke coverage for the
remaining monitors subcommands in tests/cli_tests.rs: list, get, update, delete,
test-evaluate, and events, using their command names and expected request
shapes.
src/commands/monitors.rs (2)

141-146: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Hardcoded default page_size duplicates ListEventArgs's default_value.

The None branch of the Events match constructs ListEventArgs { outcome: None, page_size: 50 } with a literal 50, duplicating the default_value = "50" declared on ListEventArgs::page_size. If the default changes in one place, it's easy to miss the other.

♻️ Proposed fix using Default
+impl Default for ListEventArgs {
+    fn default() -> Self {
+        Self { outcome: None, page_size: 50 }
+    }
+}
+
 ...
-                None => ListEventArgs {
-                    outcome: None,
-                    page_size: 50,
-                },
+                None => ListEventArgs::default(),

Also applies to: 316-343

🤖 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/commands/monitors.rs` around lines 141 - 146, The `Events` match is
hardcoding `page_size: 50` when constructing `ListEventArgs`, duplicating the
default already declared on `ListEventArgs::page_size`. Update the `None` branch
to rely on a shared default source instead of a literal, ideally by implementing
or using `Default` for `ListEventArgs` and reusing it where `ListEventArgs` is
constructed. Apply the same cleanup anywhere else `ListEventArgs` is manually
built so the default stays in one place.

70-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reduce duplicated CSV-splitting/field-building logic between Create and Update.

agent_ids, required_tags, and scheduled_run_ids are parsed identically via manual split(',') + trim() in both Create and Update (6 near-identical blocks total). Other resources in this codebase (e.g. run_templates.rs) avoid this by letting clap parse comma-separated values directly into Vec<String> via value_delimiter, removing the need for manual splitting and its duplication entirely.

♻️ Proposed refactor using clap's value_delimiter
     #[arg(long)]
-    agent_ids: Option<String>,
+    #[arg(long, value_delimiter = ',')]
+    agent_ids: Option<Vec<String>>,
     #[arg(long)]
-    required_tags: Option<String>,
+    #[arg(long, value_delimiter = ',')]
+    required_tags: Option<Vec<String>>,
     #[arg(long)]
-    scheduled_run_ids: Option<String>,
+    #[arg(long, value_delimiter = ',')]
+    scheduled_run_ids: Option<Vec<String>>,

Apply the same field changes to UpdateArgs, then in both execute branches replace:

-            if let Some(ref ids) = args.agent_ids {
-                let v: Vec<String> = ids.split(',').map(|s| s.trim().to_string()).collect();
-                input_json::insert(&mut input, "agent_ids", Some(v))?;
-            }
-            if let Some(ref ids) = args.required_tags {
-                let v: Vec<String> = ids.split(',').map(|s| s.trim().to_string()).collect();
-                input_json::insert(&mut input, "required_tags", Some(v))?;
-            }
-            if let Some(ref ids) = args.scheduled_run_ids {
-                let v: Vec<String> = ids.split(',').map(|s| s.trim().to_string()).collect();
-                input_json::insert(&mut input, "scheduled_run_ids", Some(v))?;
-            }
+            input_json::insert(&mut input, "agent_ids", args.agent_ids)?;
+            input_json::insert(&mut input, "required_tags", args.required_tags)?;
+            input_json::insert(&mut input, "scheduled_run_ids", args.scheduled_run_ids)?;

Also applies to: 100-126, 200-211, 263-274

🤖 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/commands/monitors.rs` around lines 70 - 97, The CreateArgs and UpdateArgs
paths in monitors.rs duplicate manual CSV parsing for agent_ids, required_tags,
and scheduled_run_ids; switch these clap fields to parse directly into
Vec<String> using value_delimiter, matching the approach used in
run_templates.rs. Update both CreateArgs and UpdateArgs, then simplify the
execute logic to use the parsed vectors directly instead of repeating split(',')
and trim() field-building blocks.
🤖 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/cli.rs`:
- Around line 117-120: The Monitors CLI variant is using the wrong branch name
for the events subcommand path, which causes next_actions::list_result to emit
an unmapped monitor-events argv. Update the `Monitors` entry in `src/cli.rs` to
keep the events branch under `monitors`, and make sure the corresponding
`commands::monitors::MonitorCommands` handling in `src/commands/monitors.rs`
continues to resolve the existing `context|get` flow without introducing a new
`monitor-events` resource unless you also add a matching top-level command.

In `@src/commands/monitors.rs`:
- Around line 100-126: Expose the missing monitor update flag by adding
`evaluation_type` to `UpdateArgs` alongside the other update fields in
`monitors.rs`. Wire it through the same clap parsing pattern as the existing
options so `UpdateMonitorRequest` can receive it from `update_monitor` without
requiring `--input-json`. Use the existing `UpdateArgs` and request-building
code to locate where the new option should be forwarded.

---

Nitpick comments:
In `@src/commands/monitors.rs`:
- Around line 141-146: The `Events` match is hardcoding `page_size: 50` when
constructing `ListEventArgs`, duplicating the default already declared on
`ListEventArgs::page_size`. Update the `None` branch to rely on a shared default
source instead of a literal, ideally by implementing or using `Default` for
`ListEventArgs` and reusing it where `ListEventArgs` is constructed. Apply the
same cleanup anywhere else `ListEventArgs` is manually built so the default
stays in one place.
- Around line 70-97: The CreateArgs and UpdateArgs paths in monitors.rs
duplicate manual CSV parsing for agent_ids, required_tags, and
scheduled_run_ids; switch these clap fields to parse directly into Vec<String>
using value_delimiter, matching the approach used in run_templates.rs. Update
both CreateArgs and UpdateArgs, then simplify the execute logic to use the
parsed vectors directly instead of repeating split(',') and trim()
field-building blocks.

In `@tests/cli_tests.rs`:
- Around line 2175-2256: The current monitors tests cover create override
behavior, but they miss the default evaluation_type fallback and the rest of the
monitors CLI surface. Add a test around MonitorCommands::Create that verifies
when neither --input-json nor --evaluation-type supplies a value, the request
uses DEFAULT_EVALUATION_TYPE from src/commands/monitors.rs. Also add lightweight
smoke coverage for the remaining monitors subcommands in tests/cli_tests.rs:
list, get, update, delete, test-evaluate, and events, using their command names
and expected request shapes.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0a8b50d5-5078-4cce-9064-bcc21a67e111

📥 Commits

Reviewing files that changed from the base of the PR and between 40f3bb3 and faf89e7.

📒 Files selected for processing (8)
  • src/agent_discovery.rs
  • src/cli.rs
  • src/client/mod.rs
  • src/client/models/mod.rs
  • src/client/models/monitor.rs
  • src/commands/mod.rs
  • src/commands/monitors.rs
  • tests/cli_tests.rs

Comment thread src/cli.rs
Comment thread src/commands/monitors.rs
@callumreid callumreid merged commit 60942b2 into main Jul 2, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants