cli

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

commit 69fe855a4a5aac65435916f3b20c8c859f964086
parent 8952e5b1705d992d5cf0c3e77c2b3ffa4be7664a
Author: triesap <tyson@radroots.org>
Date:   Mon, 27 Apr 2026 06:49:28 +0000

cli: fence legacy command surface

- rename adapter internals to target contract
- translate stale runtime command references
- assert target outputs omit removed commands
- preserve public parser rejection coverage

Diffstat:
Msrc/main.rs | 144+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/operation_adapter.rs | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/operation_registry.rs | 2+-
Msrc/target_cli.rs | 4++--
Mtests/target_cli.rs | 53++++++++++++++++++++++++++++++++++++++++++++++++++---
5 files changed, 287 insertions(+), 92 deletions(-)

diff --git a/src/main.rs b/src/main.rs @@ -22,9 +22,9 @@ use clap::Parser; use crate::cli::{CliArgs, Command, ConfigArgs, ConfigCommand, OutputFormatArg}; use crate::operation_adapter::{ - MvpOperationRequest, OperationAdapter, OperationAdapterError, OperationNetworkMode, - OperationOutputFormat, OperationRequest, OperationRequestPayload, OperationResultPayload, - OperationService, + OperationAdapter, OperationAdapterError, OperationNetworkMode, OperationOutputFormat, + OperationRequest, OperationRequestPayload, OperationResultPayload, OperationService, + TargetOperationRequest, }; use crate::operation_basket::BasketOperationService; use crate::operation_core::CoreOperationService; @@ -54,7 +54,8 @@ fn run() -> Result<ExitCode, runtime::RuntimeError> { let args = TargetCliArgs::parse(); let config = RuntimeConfig::from_system(&config_args_from_target(&args)?)?; let logging = initialize_logging(&config.logging)?; - let request = MvpOperationRequest::from_target_args(&args).map_err(operation_config_error)?; + let request = + TargetOperationRequest::from_target_args(&args).map_err(operation_config_error)?; let envelope = match validate_request_contract(&request, &config) { Ok(()) => execute_request(request, &config, &logging), Err(error) => failure_envelope(&request, error), @@ -100,195 +101,195 @@ fn config_args_from_target(args: &TargetCliArgs) -> Result<CliArgs, runtime::Run } fn execute_request( - request: MvpOperationRequest, + request: TargetOperationRequest, config: &RuntimeConfig, logging: &runtime::logging::LoggingState, ) -> OutputEnvelope { match request { - MvpOperationRequest::WorkspaceInit(request) => { + TargetOperationRequest::WorkspaceInit(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::WorkspaceGet(request) => { + TargetOperationRequest::WorkspaceGet(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::HealthStatusGet(request) => { + TargetOperationRequest::HealthStatusGet(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::HealthCheckRun(request) => { + TargetOperationRequest::HealthCheckRun(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::ConfigGet(request) => { + TargetOperationRequest::ConfigGet(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::AccountCreate(request) => { + TargetOperationRequest::AccountCreate(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::AccountImport(request) => { + TargetOperationRequest::AccountImport(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::AccountGet(request) => { + TargetOperationRequest::AccountGet(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::AccountList(request) => { + TargetOperationRequest::AccountList(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::AccountRemove(request) => { + TargetOperationRequest::AccountRemove(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::AccountSelectionGet(request) => { + TargetOperationRequest::AccountSelectionGet(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::AccountSelectionUpdate(request) => { + TargetOperationRequest::AccountSelectionUpdate(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::AccountSelectionClear(request) => { + TargetOperationRequest::AccountSelectionClear(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::StoreInit(request) => { + TargetOperationRequest::StoreInit(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::StoreStatusGet(request) => { + TargetOperationRequest::StoreStatusGet(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::StoreExport(request) => { + TargetOperationRequest::StoreExport(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::StoreBackupCreate(request) => { + TargetOperationRequest::StoreBackupCreate(request) => { execute_with(CoreOperationService::new(config, logging), request) } - MvpOperationRequest::SignerStatusGet(request) => { + TargetOperationRequest::SignerStatusGet(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::RelayList(request) => { + TargetOperationRequest::RelayList(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::SyncStatusGet(request) => { + TargetOperationRequest::SyncStatusGet(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::SyncPull(request) => { + TargetOperationRequest::SyncPull(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::SyncPush(request) => { + TargetOperationRequest::SyncPush(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::SyncWatch(request) => { + TargetOperationRequest::SyncWatch(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::RuntimeStatusGet(request) => { + TargetOperationRequest::RuntimeStatusGet(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::RuntimeStart(request) => { + TargetOperationRequest::RuntimeStart(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::RuntimeStop(request) => { + TargetOperationRequest::RuntimeStop(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::RuntimeRestart(request) => { + TargetOperationRequest::RuntimeRestart(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::RuntimeLogWatch(request) => { + TargetOperationRequest::RuntimeLogWatch(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::RuntimeConfigGet(request) => { + TargetOperationRequest::RuntimeConfigGet(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::JobGet(request) => { + TargetOperationRequest::JobGet(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::JobList(request) => { + TargetOperationRequest::JobList(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::JobWatch(request) => { + TargetOperationRequest::JobWatch(request) => { execute_with(RuntimeOperationService::new(config), request) } - MvpOperationRequest::FarmCreate(request) => { + TargetOperationRequest::FarmCreate(request) => { execute_with(FarmOperationService::new(config), request) } - MvpOperationRequest::FarmGet(request) => { + TargetOperationRequest::FarmGet(request) => { execute_with(FarmOperationService::new(config), request) } - MvpOperationRequest::FarmProfileUpdate(request) => { + TargetOperationRequest::FarmProfileUpdate(request) => { execute_with(FarmOperationService::new(config), request) } - MvpOperationRequest::FarmLocationUpdate(request) => { + TargetOperationRequest::FarmLocationUpdate(request) => { execute_with(FarmOperationService::new(config), request) } - MvpOperationRequest::FarmFulfillmentUpdate(request) => { + TargetOperationRequest::FarmFulfillmentUpdate(request) => { execute_with(FarmOperationService::new(config), request) } - MvpOperationRequest::FarmReadinessCheck(request) => { + TargetOperationRequest::FarmReadinessCheck(request) => { execute_with(FarmOperationService::new(config), request) } - MvpOperationRequest::FarmPublish(request) => { + TargetOperationRequest::FarmPublish(request) => { execute_with(FarmOperationService::new(config), request) } - MvpOperationRequest::ListingCreate(request) => { + TargetOperationRequest::ListingCreate(request) => { execute_with(ListingOperationService::new(config), request) } - MvpOperationRequest::ListingGet(request) => { + TargetOperationRequest::ListingGet(request) => { execute_with(ListingOperationService::new(config), request) } - MvpOperationRequest::ListingList(request) => { + TargetOperationRequest::ListingList(request) => { execute_with(ListingOperationService::new(config), request) } - MvpOperationRequest::ListingUpdate(request) => { + TargetOperationRequest::ListingUpdate(request) => { execute_with(ListingOperationService::new(config), request) } - MvpOperationRequest::ListingValidate(request) => { + TargetOperationRequest::ListingValidate(request) => { execute_with(ListingOperationService::new(config), request) } - MvpOperationRequest::ListingPublish(request) => { + TargetOperationRequest::ListingPublish(request) => { execute_with(ListingOperationService::new(config), request) } - MvpOperationRequest::ListingArchive(request) => { + TargetOperationRequest::ListingArchive(request) => { execute_with(ListingOperationService::new(config), request) } - MvpOperationRequest::MarketRefresh(request) => { + TargetOperationRequest::MarketRefresh(request) => { execute_with(MarketOperationService::new(config), request) } - MvpOperationRequest::MarketProductSearch(request) => { + TargetOperationRequest::MarketProductSearch(request) => { execute_with(MarketOperationService::new(config), request) } - MvpOperationRequest::MarketListingGet(request) => { + TargetOperationRequest::MarketListingGet(request) => { execute_with(MarketOperationService::new(config), request) } - MvpOperationRequest::BasketCreate(request) => { + TargetOperationRequest::BasketCreate(request) => { execute_with(BasketOperationService::new(config), request) } - MvpOperationRequest::BasketGet(request) => { + TargetOperationRequest::BasketGet(request) => { execute_with(BasketOperationService::new(config), request) } - MvpOperationRequest::BasketList(request) => { + TargetOperationRequest::BasketList(request) => { execute_with(BasketOperationService::new(config), request) } - MvpOperationRequest::BasketItemAdd(request) => { + TargetOperationRequest::BasketItemAdd(request) => { execute_with(BasketOperationService::new(config), request) } - MvpOperationRequest::BasketItemUpdate(request) => { + TargetOperationRequest::BasketItemUpdate(request) => { execute_with(BasketOperationService::new(config), request) } - MvpOperationRequest::BasketItemRemove(request) => { + TargetOperationRequest::BasketItemRemove(request) => { execute_with(BasketOperationService::new(config), request) } - MvpOperationRequest::BasketValidate(request) => { + TargetOperationRequest::BasketValidate(request) => { execute_with(BasketOperationService::new(config), request) } - MvpOperationRequest::BasketQuoteCreate(request) => { + TargetOperationRequest::BasketQuoteCreate(request) => { execute_with(BasketOperationService::new(config), request) } - MvpOperationRequest::OrderSubmit(request) => { + TargetOperationRequest::OrderSubmit(request) => { execute_with(OrderOperationService::new(config), request) } - MvpOperationRequest::OrderGet(request) => { + TargetOperationRequest::OrderGet(request) => { execute_with(OrderOperationService::new(config), request) } - MvpOperationRequest::OrderList(request) => { + TargetOperationRequest::OrderList(request) => { execute_with(OrderOperationService::new(config), request) } - MvpOperationRequest::OrderEventList(request) => { + TargetOperationRequest::OrderEventList(request) => { execute_with(OrderOperationService::new(config), request) } - MvpOperationRequest::OrderEventWatch(request) => { + TargetOperationRequest::OrderEventWatch(request) => { execute_with(OrderOperationService::new(config), request) } } @@ -316,7 +317,7 @@ where } fn validate_request_contract( - request: &MvpOperationRequest, + request: &TargetOperationRequest, config: &RuntimeConfig, ) -> Result<(), OperationAdapterError> { let spec = request.spec(); @@ -341,7 +342,7 @@ fn validate_request_contract( } fn validate_network_contract( - request: &MvpOperationRequest, + request: &TargetOperationRequest, config: &RuntimeConfig, ) -> Result<(), OperationAdapterError> { let spec = request.spec(); @@ -391,7 +392,10 @@ fn external_network_operation(operation_id: &str) -> bool { ) } -fn failure_envelope(request: &MvpOperationRequest, error: OperationAdapterError) -> OutputEnvelope { +fn failure_envelope( + request: &TargetOperationRequest, + error: OperationAdapterError, +) -> OutputEnvelope { OutputEnvelope::failure( request.operation_id(), error.to_output_error(), diff --git a/src/operation_adapter.rs b/src/operation_adapter.rs @@ -221,6 +221,9 @@ impl<P: OperationResultPayload> OperationResult<P> { &self, context: EnvelopeContext, ) -> Result<OutputEnvelope, OperationAdapterError> { + let mut result = serde_json::to_value(&self.payload) + .map_err(|error| OperationAdapterError::Serialization(error.to_string()))?; + translate_target_command_references(&mut result); Ok(OutputEnvelope { schema_version: OUTPUT_SCHEMA_VERSION, operation_id: self.operation_id().to_owned(), @@ -230,8 +233,7 @@ impl<P: OperationResultPayload> OperationResult<P> { idempotency_key: context.idempotency_key, dry_run: context.dry_run, actor: context.actor, - result: serde_json::to_value(&self.payload) - .map_err(|error| OperationAdapterError::Serialization(error.to_string()))?, + result, warnings: self.warnings.clone(), errors: Vec::new(), next_actions: self.next_actions.clone(), @@ -604,14 +606,156 @@ fn runtime_output_error( error } -macro_rules! mvp_operation_contracts { +fn translate_target_command_references(value: &mut Value) { + match value { + Value::String(value) => { + let translated = target_command_reference(value.as_str()); + if translated != *value { + *value = translated; + } + } + Value::Array(values) => { + for nested in values { + translate_target_command_references(nested); + } + } + Value::Object(object) => { + for nested in object.values_mut() { + translate_target_command_references(nested); + } + } + Value::Null | Value::Bool(_) | Value::Number(_) => {} + } +} + +fn target_command_reference(value: &str) -> String { + let action = target_command_action(value); + if action != value { + return action; + } + + let mut translated = value.to_owned(); + for (legacy, target) in [ + ("radroots config show", "radroots config get"), + ( + "radroots runtime config show", + "radroots runtime config get", + ), + ("radroots local init", "radroots store init"), + ("radroots local status", "radroots store status get"), + ("radroots local export", "radroots store export"), + ("radroots local backup", "radroots store backup create"), + ("radroots market update", "radroots market refresh"), + ("radroots market search", "radroots market product search"), + ("radroots market view", "radroots market listing get"), + ("radroots sync status", "radroots sync status get"), + ("radroots order ls", "radroots order list"), + ("radroots order history", "radroots order event list"), + ("radroots order watch", "radroots order event watch"), + ( + "radroots order create --listing", + "radroots basket item add", + ), + ("radroots order new", "radroots basket create"), + ("radroots order create", "radroots basket create"), + ("radroots rpc status", "radroots runtime status get"), + ("radroots rpc sessions", "radroots job list"), + ("radroots net status", "radroots relay list"), + ("radroots myc status", "radroots signer status get"), + ("radroots doctor", "radroots health check run"), + ("radroots status", "radroots health status get"), + ("radroots setup seller", "radroots farm create"), + ("radroots setup buyer", "radroots basket create"), + ("radroots setup both", "radroots workspace init"), + ("radroots sell add", "radroots listing create"), + ("radroots sell check", "radroots listing validate"), + ("radroots sell publish", "radroots listing publish"), + ("radroots sell update", "radroots listing update"), + ("radroots sell pause", "radroots listing archive"), + ("radroots sell show", "radroots listing get"), + ] { + translated = translated.replace(legacy, target); + } + translated +} + +fn target_command_action(action: &str) -> String { + match action { + "radroots config show" => "radroots config get".to_owned(), + "radroots runtime config show" => "radroots runtime config get".to_owned(), + "radroots local init" => "radroots store init".to_owned(), + "radroots local status" => "radroots store status get".to_owned(), + "radroots local export" => "radroots store export".to_owned(), + "radroots local backup" => "radroots store backup create".to_owned(), + "radroots market update" => "radroots market refresh".to_owned(), + "radroots sync status" => "radroots sync status get".to_owned(), + "radroots order ls" => "radroots order list".to_owned(), + "radroots order history" => "radroots order event list".to_owned(), + "radroots order new" | "radroots order create" => "radroots basket create".to_owned(), + "radroots rpc status" => "radroots runtime status get".to_owned(), + "radroots rpc sessions" => "radroots job list".to_owned(), + "radroots net status" => "radroots relay list".to_owned(), + "radroots myc status" => "radroots signer status get".to_owned(), + "radroots doctor" => "radroots health check run".to_owned(), + "radroots status" => "radroots health status get".to_owned(), + "radroots setup seller" => "radroots farm create".to_owned(), + "radroots setup buyer" => "radroots basket create".to_owned(), + "radroots setup both" => "radroots workspace init".to_owned(), + other if other.starts_with("radroots local export ") => { + other.replacen("radroots local export ", "radroots store export ", 1) + } + other if other.starts_with("radroots local backup ") => { + other.replacen("radroots local backup ", "radroots store backup create ", 1) + } + other if other.starts_with("radroots market search ") => other.replacen( + "radroots market search ", + "radroots market product search ", + 1, + ), + other if other.starts_with("radroots market view ") => { + other.replacen("radroots market view ", "radroots market listing get ", 1) + } + other if other.starts_with("radroots order watch ") => { + other.replacen("radroots order watch ", "radroots order event watch ", 1) + } + other if other.starts_with("radroots order create --listing ") => other.replacen( + "radroots order create --listing ", + "radroots basket item add ", + 1, + ), + other if other.starts_with("radroots find ") => { + other.replacen("radroots find ", "radroots market product search ", 1) + } + other if other.starts_with("radroots sell add ") => { + other.replacen("radroots sell add ", "radroots listing create ", 1) + } + other if other.starts_with("radroots sell check ") => { + other.replacen("radroots sell check ", "radroots listing validate ", 1) + } + other if other.starts_with("radroots sell publish ") => { + other.replacen("radroots sell publish ", "radroots listing publish ", 1) + } + other if other.starts_with("radroots sell update ") => { + other.replacen("radroots sell update ", "radroots listing update ", 1) + } + other if other.starts_with("radroots sell pause ") => { + other.replacen("radroots sell pause ", "radroots listing archive ", 1) + } + other if other.starts_with("radroots sell show ") => { + other.replacen("radroots sell show ", "radroots listing get ", 1) + } + other => other.to_owned(), + } +} + +macro_rules! target_operation_contracts { ($( $variant:ident => ($request:ident, $result:ident, $operation_id:literal) ),+ $(,)?) => { #[derive(Debug, Clone, PartialEq)] - pub enum MvpOperationRequest { + pub enum TargetOperationRequest { $( $variant(OperationRequest<$request>), )+ } - impl MvpOperationRequest { + impl TargetOperationRequest { pub fn from_target_args(args: &TargetCliArgs) -> Result<Self, OperationAdapterError> { Self::from_operation_id_with_input( args.command.operation_id(), @@ -671,11 +815,11 @@ macro_rules! mvp_operation_contracts { } #[derive(Debug, Clone, PartialEq)] - pub enum MvpOperationResult { + pub enum TargetOperationResult { $( $variant(OperationResult<$result>), )+ } - impl MvpOperationResult { + impl TargetOperationResult { pub fn operation_id(&self) -> &'static str { match self { $( Self::$variant(result) => result.operation_id(), )+ @@ -939,7 +1083,7 @@ fn insert_path(input: &mut OperationData, key: &str, value: &Option<std::path::P } } -mvp_operation_contracts! { +target_operation_contracts! { WorkspaceInit => (WorkspaceInitRequest, WorkspaceInitResult, "workspace.init"), WorkspaceGet => (WorkspaceGetRequest, WorkspaceGetResult, "workspace.get"), HealthStatusGet => (HealthStatusGetRequest, HealthStatusGetResult, "health.status.get"), @@ -1006,9 +1150,9 @@ mvp_operation_contracts! { pub fn adapter_registry_linkage_is_valid() -> bool { OPERATION_REGISTRY.iter().all(|operation| { - MvpOperationRequest::request_type_for_operation(operation.operation_id) + TargetOperationRequest::request_type_for_operation(operation.operation_id) == Some(operation.rust_request) - && MvpOperationResult::result_type_for_operation(operation.operation_id) + && TargetOperationResult::result_type_for_operation(operation.operation_id) == Some(operation.rust_result) }) } @@ -1019,9 +1163,9 @@ mod tests { use serde_json::json; use super::{ - MvpOperationRequest, OperationAdapter, OperationAdapterError, OperationContext, - OperationInputMode, OperationNetworkMode, OperationOutputFormat, OperationRequest, - OperationResult, OperationService, WorkspaceGetRequest, WorkspaceGetResult, + OperationAdapter, OperationAdapterError, OperationContext, OperationInputMode, + OperationNetworkMode, OperationOutputFormat, OperationRequest, OperationResult, + OperationService, TargetOperationRequest, WorkspaceGetRequest, WorkspaceGetResult, adapter_registry_linkage_is_valid, }; use crate::operation_registry::OPERATION_REGISTRY; @@ -1036,14 +1180,14 @@ mod tests { .unwrap_or_else(|error| { panic!("{} failed to parse: {error}", operation.cli_path); }); - let request = MvpOperationRequest::from_target_args(&parsed) + let request = TargetOperationRequest::from_target_args(&parsed) .expect("operation request from target args"); assert_eq!(request.operation_id(), operation.operation_id); assert_eq!(request.spec().mcp_tool, operation.mcp_tool); assert_eq!(request.request_type_name(), operation.rust_request); assert_eq!( - MvpOperationRequest::request_type_for_operation(operation.operation_id), + TargetOperationRequest::request_type_for_operation(operation.operation_id), Some(operation.rust_request) ); } @@ -1077,7 +1221,7 @@ mod tests { ]) .expect("target args parse"); - let request = MvpOperationRequest::from_target_args(&parsed) + let request = TargetOperationRequest::from_target_args(&parsed) .expect("operation request from target args"); let context = request.context(); diff --git a/src/operation_registry.rs b/src/operation_registry.rs @@ -1095,7 +1095,7 @@ mod tests { ]; #[test] - fn registry_contains_exact_mvp_operation_set() { + fn registry_contains_exact_target_operation_set() { let actual = operation_ids(); let expected = EXPECTED_OPERATION_IDS .iter() diff --git a/src/target_cli.rs b/src/target_cli.rs @@ -782,7 +782,7 @@ mod tests { use crate::operation_registry::OPERATION_REGISTRY; #[test] - fn target_parser_accepts_every_mvp_registry_path() { + fn target_parser_accepts_every_target_registry_path() { for operation in OPERATION_REGISTRY { let parsed = TargetCliArgs::try_parse_from(operation.cli_path.split_whitespace()) .unwrap_or_else(|error| { @@ -793,7 +793,7 @@ mod tests { } #[test] - fn target_parser_exposes_only_mvp_top_level_namespaces() { + fn target_parser_exposes_only_target_top_level_namespaces() { let actual = TargetCliArgs::command() .get_subcommands() .map(|command| command.get_name().to_owned()) diff --git a/tests/target_cli.rs b/tests/target_cli.rs @@ -12,7 +12,7 @@ fn json_success(sandbox: &RadrootsCliSandbox, args: &[&str]) -> Value { } #[test] -fn root_help_exposes_only_mvp_namespaces() { +fn root_help_exposes_only_target_namespaces() { let output = radroots().arg("--help").output().expect("run root help"); assert!(output.status.success()); @@ -97,6 +97,53 @@ fn removed_command_families_are_rejected_publicly() { } #[test] +fn target_outputs_do_not_suggest_removed_command_families() { + let sandbox = RadrootsCliSandbox::new(); + + for args in [ + ["--format", "json", "market", "product", "search", "eggs"].as_slice(), + ["--format", "json", "market", "listing", "get", "eggs"].as_slice(), + ["--format", "json", "listing", "get", "eggs"].as_slice(), + ["--format", "json", "sync", "status", "get"].as_slice(), + [ + "--format", + "json", + "order", + "get", + "ord_AAAAAAAAAAAAAAAAAAAAAA", + ] + .as_slice(), + ] { + let value = json_success(&sandbox, args); + assert_no_removed_command_reference(&value, args); + } +} + +fn assert_no_removed_command_reference(value: &Value, args: &[&str]) { + let raw = serde_json::to_string(value).expect("json value"); + for removed in [ + "radroots setup", + "radroots status", + "radroots doctor", + "radroots sell", + "radroots find", + "radroots local", + "radroots net", + "radroots myc", + "radroots rpc", + "radroots product", + "radroots message", + "radroots approval", + "radroots agent", + ] { + assert!( + !raw.contains(removed), + "`{args:?}` output should not contain removed command reference `{removed}`: {raw}" + ); + } +} + +#[test] fn account_id_global_populates_envelope_actor() { let output = radroots() .args([ @@ -233,7 +280,7 @@ fn required_approval_missing_token_returns_structured_error() { } #[test] -fn buyer_mvp_flow_acceptance_uses_target_operations() { +fn buyer_target_flow_acceptance_uses_target_operations() { let sandbox = RadrootsCliSandbox::new(); let search = json_success( @@ -316,7 +363,7 @@ fn buyer_mvp_flow_acceptance_uses_target_operations() { } #[test] -fn seller_mvp_flow_acceptance_uses_target_operations() { +fn seller_target_flow_acceptance_uses_target_operations() { let sandbox = RadrootsCliSandbox::new(); let listing_file = sandbox.root().join("listing.toml"); let listing_file = listing_file.to_string_lossy().into_owned();