cli

Command-line interface for Radroots
git clone https://radroots.dev/git/cli.git
Log | Files | Refs | README | LICENSE

commit 66faea106d0a7c9186df9a0a95df5325fdf4e290
parent ebbb908c84140402c8b7c647ab654c13ca682595
Author: triesap <tyson@radroots.org>
Date:   Sat,  9 May 2026 00:45:01 +0000

cli: mark order event watch deferred

- describe order event watch as a reserved unavailable command
- remove deferred watch from ndjson and relay-preflight metadata
- keep online watch failures on structured not_implemented output
- update registry output and target tests for deferred watch posture

Diffstat:
Msrc/operation_registry.rs | 8+++-----
Msrc/output_contract.rs | 8++++----
Mtests/target_cli.rs | 68+++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
3 files changed, 62 insertions(+), 22 deletions(-)

diff --git a/src/operation_registry.rs b/src/operation_registry.rs @@ -1094,12 +1094,12 @@ pub const OPERATION_REGISTRY: &[OperationSpec] = &[ "order_event_watch", "OrderEventWatchRequest", "OrderEventWatchResult", - "Stream order events.", + "Report deferred order event watch status.", Any, false, None, Low, - true, + false, false ), ]; @@ -1114,7 +1114,7 @@ pub fn network_requirement(operation_id: &str) -> NetworkRequirement { match operation_id { "sync.pull" | "sync.push" | "sync.watch" | "market.refresh" | "farm.publish" | "listing.publish" | "listing.archive" | "order.submit" | "order.status.get" - | "order.event.list" | "order.event.watch" => NetworkRequirement::External { + | "order.event.list" => NetworkRequirement::External { dry_run_requires_network: false, }, "order.accept" @@ -1425,7 +1425,6 @@ mod tests { "basket.list", "order.list", "order.event.list", - "order.event.watch", ] .into_iter() .collect::<BTreeSet<_>>(); @@ -1464,7 +1463,6 @@ mod tests { "order.receipt.record", "order.status.get", "order.event.list", - "order.event.watch", ] .into_iter() .collect::<BTreeSet<_>>(); diff --git a/src/output_contract.rs b/src/output_contract.rs @@ -410,21 +410,21 @@ mod tests { fn ndjson_frames_serialize_one_json_object_per_line() { let frames = [ NdjsonFrame::new( - "order.event.watch", + "sync.watch", "req_watch", 0, NdjsonFrameType::Started, json!({ "state": "started" }), ), NdjsonFrame::new( - "order.event.watch", + "sync.watch", "req_watch", 1, NdjsonFrameType::Event, json!({ "state": "submitted" }), ), NdjsonFrame::new( - "order.event.watch", + "sync.watch", "req_watch", 2, NdjsonFrameType::Completed, @@ -440,7 +440,7 @@ mod tests { for line in rendered.lines() { let value: Value = serde_json::from_str(line).expect("line is json"); assert_eq!(value["schema_version"], OUTPUT_SCHEMA_VERSION); - assert_eq!(value["operation_id"], "order.event.watch"); + assert_eq!(value["operation_id"], "sync.watch"); assert!(value["frame_type"].is_string()); } } diff --git a/tests/target_cli.rs b/tests/target_cli.rs @@ -2059,6 +2059,28 @@ fn unsupported_ndjson_returns_structured_invalid_input() { assert_eq!(frames[1]["frame_type"], "error"); assert_eq!(frames[1]["errors"][0]["code"], "invalid_input"); assert_eq!(frames[1]["errors"][0]["exit_code"], 2); + + let watch_output = radroots() + .args([ + "--format", + "ndjson", + "order", + "event", + "watch", + "ord_missing", + ]) + .output() + .expect("run order event watch ndjson"); + + assert_eq!(watch_output.status.code(), Some(2)); + let watch_frames = ndjson_from_stdout(&watch_output); + assert_eq!(watch_frames.len(), 2); + assert_eq!(watch_frames[0]["operation_id"], "order.event.watch"); + assert_eq!(watch_frames[0]["frame_type"], "started"); + assert_eq!(watch_frames[1]["operation_id"], "order.event.watch"); + assert_eq!(watch_frames[1]["frame_type"], "error"); + assert_eq!(watch_frames[1]["errors"][0]["code"], "invalid_input"); + assert_eq!(watch_frames[1]["errors"][0]["exit_code"], 2); } #[test] @@ -2432,19 +2454,6 @@ fn online_requires_relay_for_external_network_operations() { .as_slice(), ), ( - "order.event.watch", - [ - "--format", - "json", - "--online", - "order", - "event", - "watch", - "ord_missing", - ] - .as_slice(), - ), - ( "order.cancel", [ "--format", @@ -2565,6 +2574,39 @@ fn online_requires_relay_for_external_network_operations() { } #[test] +fn online_order_event_watch_returns_deferred_without_relay_preflight() { + let sandbox = RadrootsCliSandbox::new(); + let (output, value) = sandbox.json_output(&[ + "--format", + "json", + "--online", + "order", + "event", + "watch", + "ord_missing", + ]); + + assert!(!output.status.success()); + assert_eq!(output.status.code(), Some(3)); + assert_eq!(value["operation_id"], "order.event.watch"); + assert_eq!(value["result"], Value::Null); + assert_eq!(value["errors"][0]["code"], "not_implemented"); + assert_eq!(value["errors"][0]["detail"]["state"], "not_implemented"); + assert_eq!(value["errors"][0]["detail"]["order_id"], "ord_missing"); + assert_eq!( + value["next_actions"][0]["command"], + "radroots order status get ord_missing" + ); + assert!( + !value["errors"][0]["message"] + .as_str() + .expect("message") + .contains("configured relay") + ); + assert_no_daemon_runtime_reference(&value, &["order", "event", "watch"]); +} + +#[test] fn online_allows_local_diagnostics() { let value = RadrootsCliSandbox::new().json_success(&[ "--format",