cli

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

commit 9eb1dd012c3463b95030e4403ce7078002d91df9
parent 09c68259bab9a746c912f67b93724f139be800b5
Author: triesap <tyson@radroots.org>
Date:   Fri, 10 Jul 2026 19:30:52 +0000

transport: align CLI delivery-profile helpers

- replace the stale Nostr transport-profile registry helper with delivery-capable transport-profile semantics
- use the registry helper as the pre-runtime delivery gate in main
- rename farm and listing transport failure helpers away from generic relay wording
- add target CLI source-boundary coverage against stale helper names

Diffstat:
Msrc/main.rs | 17+++--------------
Msrc/ops/exec/farm.rs | 10++++++----
Msrc/ops/exec/listing.rs | 4++--
Msrc/registry/mod.rs | 28++++++----------------------
Msrc/runtime/farm.rs | 4++--
Mtests/target_cli.rs | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 88 insertions(+), 44 deletions(-)

diff --git a/src/main.rs b/src/main.rs @@ -30,7 +30,7 @@ use radroots_cli::out::terminal::renderer::{ }; use radroots_cli::registry::{ ApprovalPolicy, NetworkRequirement, OPERATION_REGISTRY, network_requirement, - requires_local_signer_mode, + requires_delivery_capable_transport_profile, requires_local_signer_mode, }; use radroots_cli::runtime::config::{ OutputFormat as RuntimeOutputFormat, RuntimeConfig, SignerBackend, TransportProfileKind, @@ -477,7 +477,7 @@ fn requires_local_signer_mode_for_transport_profile( } fn requires_pre_runtime_transport_target(operation_id: &str) -> bool { - !is_transport_profile_routed_operation(operation_id) + !requires_delivery_capable_transport_profile(operation_id) } fn allows_offline_local_mutation(operation_id: &str) -> bool { @@ -489,7 +489,7 @@ fn validate_transport_profile_contract( config: &RuntimeConfig, ) -> Result<(), OperationAdapterError> { let spec = request.spec(); - if !is_transport_profile_routed_operation(spec.operation_id) { + if !requires_delivery_capable_transport_profile(spec.operation_id) { return Ok(()); } if request.context().dry_run @@ -542,17 +542,6 @@ fn validate_transport_profile_contract( Ok(()) } -fn is_transport_profile_routed_operation(operation_id: &str) -> bool { - matches!( - operation_id, - "farm.publish" - | "listing.publish" - | "listing.update" - | "listing.archive" - | "transport.outbox.push" - ) -} - fn transport_profile_delivery_unavailable_reason(config: &RuntimeConfig) -> Option<String> { match config.transport.profile { TransportProfileKind::Nostr | TransportProfileKind::Hybrid => { diff --git a/src/ops/exec/farm.rs b/src/ops/exec/farm.rs @@ -236,7 +236,7 @@ impl OperationService<FarmPublishRequest> for FarmOperationService<'_> { self.config.transport.profile, TransportProfileKind::Nostr | TransportProfileKind::Hybrid ) { - require_relay_target(&request, self.config)?; + require_nostr_delivery_target(&request, self.config)?; } let view = crate::runtime::farm::publish(self.config, &args).map_err(|error| { @@ -300,7 +300,9 @@ fn farm_publish_result( ) -> Result<OperationResult<FarmPublishResult>, OperationAdapterError> { match view.disposition() { CommandDisposition::Success => serialized_operation_result::<FarmPublishResult, _>(view), - CommandDisposition::ExternalUnavailable if farm_publish_relay_unavailable(view) => { + CommandDisposition::ExternalUnavailable + if farm_publish_transport_delivery_unavailable(view) => + { Err(OperationAdapterError::network_unavailable_with_detail( operation_id, view.reason.clone().unwrap_or_else(|| { @@ -348,13 +350,13 @@ fn farm_private_location_set_result( } } -fn farm_publish_relay_unavailable(view: &FarmPublishView) -> bool { +fn farm_publish_transport_delivery_unavailable(view: &FarmPublishView) -> bool { view.state == "partial" || !view.profile.failed_transport_targets.is_empty() || !view.farm.failed_transport_targets.is_empty() } -fn require_relay_target<P>( +fn require_nostr_delivery_target<P>( request: &OperationRequest<P>, config: &RuntimeConfig, ) -> Result<(), OperationAdapterError> diff --git a/src/ops/exec/listing.rs b/src/ops/exec/listing.rs @@ -307,7 +307,7 @@ where { match view.disposition() { CommandDisposition::Success => serialized_operation_result::<R, _>(view), - CommandDisposition::ExternalUnavailable if listing_relay_unavailable(view) => { + CommandDisposition::ExternalUnavailable if listing_transport_delivery_unavailable(view) => { Err(OperationAdapterError::network_unavailable_with_detail( operation_id, view.reason.clone().unwrap_or_else(|| { @@ -332,7 +332,7 @@ where } } -fn listing_relay_unavailable(view: &ListingMutationView) -> bool { +fn listing_transport_delivery_unavailable(view: &ListingMutationView) -> bool { matches!( view.source.as_str(), "Nostr transport publish · local key" | "SDK listing publish · configured signer" diff --git a/src/registry/mod.rs b/src/registry/mod.rs @@ -235,22 +235,14 @@ pub fn requires_local_signer_mode(operation_id: &str) -> bool { ) } -pub fn requires_nostr_transport_profile(operation_id: &str) -> bool { +pub fn requires_delivery_capable_transport_profile(operation_id: &str) -> bool { matches!( operation_id, - "sync.push" - | "transport.outbox.push" + "transport.outbox.push" | "farm.publish" | "listing.publish" | "listing.update" | "listing.archive" - | "trade.submit" - | "trade.accept" - | "trade.decline" - | "trade.cancel" - | "trade.revision.propose" - | "trade.revision.accept" - | "trade.revision.decline" ) } @@ -269,8 +261,8 @@ mod tests { use super::{ ApprovalPolicy, NetworkRequirement, OPERATION_REGISTRY, OperationRole, RiskLevel, - get_operation, network_requirement, requires_local_signer_mode, - requires_nostr_transport_profile, + get_operation, network_requirement, requires_delivery_capable_transport_profile, + requires_local_signer_mode, }; const EXPECTED_OPERATION_IDS: &[&str] = &[ @@ -622,26 +614,18 @@ mod tests { } #[test] - fn registry_nostr_publish_requirements_are_explicit() { + fn registry_delivery_capable_transport_profile_requirements_are_explicit() { let publish = OPERATION_REGISTRY .iter() - .filter(|operation| requires_nostr_transport_profile(operation.operation_id)) + .filter(|operation| requires_delivery_capable_transport_profile(operation.operation_id)) .map(|operation| operation.operation_id) .collect::<BTreeSet<_>>(); let expected = [ - "sync.push", "transport.outbox.push", "farm.publish", "listing.publish", "listing.update", "listing.archive", - "trade.submit", - "trade.accept", - "trade.decline", - "trade.cancel", - "trade.revision.propose", - "trade.revision.accept", - "trade.revision.decline", ] .into_iter() .collect::<BTreeSet<_>>(); diff --git a/src/runtime/farm.rs b/src/runtime/farm.rs @@ -614,10 +614,10 @@ fn farm_publish_readiness( config: &RuntimeConfig, account: &AccountRecordView, ) -> FarmPublishReadiness { - relay_farm_publish_readiness(config, account) + transport_farm_publish_readiness(config, account) } -fn relay_farm_publish_readiness( +fn transport_farm_publish_readiness( config: &RuntimeConfig, account: &AccountRecordView, ) -> FarmPublishReadiness { diff --git a/tests/target_cli.rs b/tests/target_cli.rs @@ -1868,6 +1868,75 @@ fn transport_source_boundary_rejects_removed_relay_and_publish_proxy_surfaces() } } + let registry_source = + fs::read_to_string(manifest_dir.join("src/registry/mod.rs")).expect("read registry source"); + let main_source = fs::read_to_string(manifest_dir.join("src/main.rs")).expect("read main"); + let farm_exec_source = + fs::read_to_string(manifest_dir.join("src/ops/exec/farm.rs")).expect("read farm ops"); + let listing_exec_source = + fs::read_to_string(manifest_dir.join("src/ops/exec/listing.rs")).expect("read listing ops"); + let farm_runtime_source = + fs::read_to_string(manifest_dir.join("src/runtime/farm.rs")).expect("read farm runtime"); + for (relative_path, source) in [ + ("src/registry/mod.rs", registry_source.as_str()), + ("src/main.rs", main_source.as_str()), + ("src/ops/exec/farm.rs", farm_exec_source.as_str()), + ("src/ops/exec/listing.rs", listing_exec_source.as_str()), + ("src/runtime/farm.rs", farm_runtime_source.as_str()), + ] { + for forbidden in [ + "requires_nostr_transport_profile", + "registry_nostr_publish_requirements_are_explicit", + "is_transport_profile_routed_operation", + "farm_publish_relay_unavailable", + "require_relay_target", + "listing_relay_unavailable", + "relay_farm_publish_readiness", + ] { + assert!( + !source.contains(forbidden), + "{relative_path} must not retain stale transport-profile helper name `{forbidden}`" + ); + } + } + for (relative_path, source, required) in [ + ( + "src/registry/mod.rs", + registry_source.as_str(), + "requires_delivery_capable_transport_profile", + ), + ( + "src/main.rs", + main_source.as_str(), + "requires_delivery_capable_transport_profile", + ), + ( + "src/ops/exec/farm.rs", + farm_exec_source.as_str(), + "farm_publish_transport_delivery_unavailable", + ), + ( + "src/ops/exec/farm.rs", + farm_exec_source.as_str(), + "require_nostr_delivery_target", + ), + ( + "src/ops/exec/listing.rs", + listing_exec_source.as_str(), + "listing_transport_delivery_unavailable", + ), + ( + "src/runtime/farm.rs", + farm_runtime_source.as_str(), + "transport_farm_publish_readiness", + ), + ] { + assert!( + source.contains(required), + "{relative_path} must retain delivery-capable transport-profile witness `{required}`" + ); + } + let transport_source = fs::read_to_string(manifest_dir.join("src/runtime/transport.rs")).expect("read source"); let view_source =