commit ba7c536e00a0775e279eafcdca36d7597b6deb37
parent bc4bca0e24f29650525394730d78b36613233779
Author: triesap <tyson@radroots.org>
Date: Fri, 10 Apr 2026 19:32:55 +0000
runtime: add resolved provider views
Diffstat:
10 files changed, 634 insertions(+), 193 deletions(-)
diff --git a/src/commands/doctor.rs b/src/commands/doctor.rs
@@ -3,10 +3,9 @@ use crate::domain::runtime::{
};
use crate::runtime::RuntimeError;
use crate::runtime::config::{RuntimeConfig, SignerBackend};
-use crate::runtime::hyf::resolve_runtime_status as resolve_hyf_status;
use crate::runtime::logging::LoggingState;
+use crate::runtime::provider::{resolve_hyf_provider, resolve_workflow_provider};
use crate::runtime::signer::resolve_signer_status;
-use crate::runtime::workflow::resolve_workflow_provider;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum DoctorSeverity {
@@ -59,7 +58,7 @@ pub fn report(
}
}
- checks.push(hyf_check(&resolve_hyf_status(config)));
+ checks.push(hyf_check(&resolve_hyf_provider(config)));
checks.push(workflow_check(&resolve_workflow_provider(config)));
checks.push(logging_check(config, logging));
checks.push(binding_check(config));
@@ -269,7 +268,7 @@ fn myc_check(myc: &crate::domain::runtime::MycStatusView) -> EvaluatedCheck {
}
}
-fn hyf_check(hyf: &crate::runtime::hyf::HyfStatusView) -> EvaluatedCheck {
+fn hyf_check(hyf: &crate::runtime::provider::HyfProviderView) -> EvaluatedCheck {
let (severity, detail) = match hyf.state.as_str() {
"disabled" => (
DoctorSeverity::Ok,
@@ -302,9 +301,7 @@ fn hyf_check(hyf: &crate::runtime::hyf::HyfStatusView) -> EvaluatedCheck {
}
}
-fn workflow_check(
- workflow: &crate::runtime::workflow::WorkflowProviderStatusView,
-) -> EvaluatedCheck {
+fn workflow_check(workflow: &crate::runtime::provider::WorkflowProviderView) -> EvaluatedCheck {
EvaluatedCheck {
severity: DoctorSeverity::Ok,
view: DoctorCheckView {
diff --git a/src/commands/runtime.rs b/src/commands/runtime.rs
@@ -1,20 +1,28 @@
use crate::domain::runtime::{
AccountRuntimeView, AccountSecretRuntimeView, CapabilityBindingRuntimeView,
- ConfigFilesRuntimeView, ConfigShowView, HyfRuntimeView, LegacyPathRuntimeView,
- LocalRuntimeView, LoggingRuntimeView, MigrationRuntimeView, MycRuntimeView, OutputRuntimeView,
- PathsRuntimeView, RelayRuntimeView, RpcRuntimeView, SignerRuntimeView, WorkflowRuntimeView,
+ ConfigFilesRuntimeView, ConfigShowView, HyfProviderRuntimeView, HyfRuntimeView,
+ LegacyPathRuntimeView, LocalRuntimeView, LoggingRuntimeView, MigrationRuntimeView,
+ MycRuntimeView, OutputRuntimeView, PathsRuntimeView, RelayRuntimeView,
+ ResolvedProviderRuntimeView, RpcRuntimeView, SignerRuntimeView, WorkflowRuntimeView,
+ WritePlaneRuntimeView,
};
use crate::runtime::RuntimeError;
use crate::runtime::config::RuntimeConfig;
use crate::runtime::logging::LoggingState;
-use crate::runtime::workflow::resolve_workflow_provider;
+use crate::runtime::provider::{
+ resolve_capability_providers, resolve_hyf_provider, resolve_workflow_provider,
+ resolve_write_plane_provider,
+};
pub fn show(
config: &RuntimeConfig,
logging: &LoggingState,
) -> Result<ConfigShowView, RuntimeError> {
let secret_backend = crate::runtime::accounts::secret_backend_status(config);
+ let write_plane = resolve_write_plane_provider(config);
let workflow = resolve_workflow_provider(config);
+ let hyf_provider = resolve_hyf_provider(config);
+ let resolved_providers = resolve_capability_providers(config);
Ok(ConfigShowView {
source: "local runtime state".to_owned(),
output: OutputRuntimeView {
@@ -106,16 +114,41 @@ pub fn show(
myc: MycRuntimeView {
executable: config.myc.executable.display().to_string(),
},
+ write_plane: WritePlaneRuntimeView {
+ provider_runtime_id: write_plane.provider_runtime_id,
+ binding_model: write_plane.binding_model,
+ state: write_plane.state,
+ provenance: write_plane.provenance,
+ source: write_plane.source,
+ target_kind: write_plane.target_kind,
+ target: write_plane.target,
+ detail: write_plane.detail,
+ bridge_auth_configured: write_plane.bridge_auth_configured,
+ },
workflow: WorkflowRuntimeView {
provider_runtime_id: workflow.provider_runtime_id,
binding_model: workflow.binding_model,
state: workflow.state,
+ provenance: workflow.provenance,
source: workflow.source,
target_kind: workflow.target_kind,
target: workflow.target,
hyf_helper_state: workflow.hyf_helper_state,
hyf_helper_detail: workflow.hyf_helper_detail,
},
+ hyf_provider: HyfProviderRuntimeView {
+ provider_runtime_id: hyf_provider.provider_runtime_id,
+ binding_model: hyf_provider.binding_model,
+ state: hyf_provider.state,
+ provenance: hyf_provider.provenance,
+ source: hyf_provider.source,
+ target_kind: hyf_provider.target_kind,
+ target: hyf_provider.target,
+ executable: hyf_provider.executable,
+ reason: hyf_provider.reason,
+ protocol_version: hyf_provider.protocol_version,
+ deterministic_available: hyf_provider.deterministic_available,
+ },
hyf: HyfRuntimeView {
enabled: config.hyf.enabled,
executable: config.hyf.executable.display().to_string(),
@@ -124,6 +157,19 @@ pub fn show(
url: config.rpc.url.clone(),
bridge_auth_configured: config.rpc.bridge_bearer_token.is_some(),
},
+ resolved_providers: resolved_providers
+ .into_iter()
+ .map(|provider| ResolvedProviderRuntimeView {
+ capability_id: provider.capability_id,
+ provider_runtime_id: provider.provider_runtime_id,
+ binding_model: provider.binding_model,
+ state: provider.state,
+ provenance: provider.provenance,
+ source: provider.source,
+ target_kind: provider.target_kind,
+ target: provider.target,
+ })
+ .collect(),
capability_bindings: config
.inspect_capability_bindings()
.into_iter()
diff --git a/src/domain/runtime.rs b/src/domain/runtime.rs
@@ -118,10 +118,13 @@ pub struct ConfigShowView {
pub relay: RelayRuntimeView,
pub local: LocalRuntimeView,
pub myc: MycRuntimeView,
+ pub write_plane: WritePlaneRuntimeView,
pub workflow: WorkflowRuntimeView,
+ pub hyf_provider: HyfProviderRuntimeView,
pub hyf: HyfRuntimeView,
pub rpc: RpcRuntimeView,
pub capability_bindings: Vec<CapabilityBindingRuntimeView>,
+ pub resolved_providers: Vec<ResolvedProviderRuntimeView>,
}
#[derive(Debug, Clone, Serialize)]
@@ -251,6 +254,7 @@ pub struct WorkflowRuntimeView {
pub provider_runtime_id: String,
pub binding_model: String,
pub state: String,
+ pub provenance: String,
pub source: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_kind: Option<String>,
@@ -267,12 +271,61 @@ pub struct HyfRuntimeView {
}
#[derive(Debug, Clone, Serialize)]
+pub struct HyfProviderRuntimeView {
+ pub provider_runtime_id: String,
+ pub binding_model: String,
+ pub state: String,
+ pub provenance: String,
+ pub source: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub target_kind: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub target: Option<String>,
+ pub executable: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub reason: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub protocol_version: Option<u64>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub deterministic_available: Option<bool>,
+}
+
+#[derive(Debug, Clone, Serialize)]
+pub struct WritePlaneRuntimeView {
+ pub provider_runtime_id: String,
+ pub binding_model: String,
+ pub state: String,
+ pub provenance: String,
+ pub source: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub target_kind: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub target: Option<String>,
+ pub detail: String,
+ pub bridge_auth_configured: bool,
+}
+
+#[derive(Debug, Clone, Serialize)]
pub struct RpcRuntimeView {
pub url: String,
pub bridge_auth_configured: bool,
}
#[derive(Debug, Clone, Serialize)]
+pub struct ResolvedProviderRuntimeView {
+ pub capability_id: String,
+ pub provider_runtime_id: String,
+ pub binding_model: String,
+ pub state: String,
+ pub provenance: String,
+ pub source: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub target_kind: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub target: Option<String>,
+}
+
+#[derive(Debug, Clone, Serialize)]
pub struct CapabilityBindingRuntimeView {
pub capability_id: String,
pub provider_runtime_id: String,
diff --git a/src/render/mod.rs b/src/render/mod.rs
@@ -617,6 +617,27 @@ fn render_config_show(
"myc",
&[("executable", view.myc.executable.as_str())],
)?;
+ let write_plane_target = format_runtime_target(
+ view.write_plane.target_kind.as_deref(),
+ view.write_plane.target.as_deref(),
+ );
+ render_pairs(
+ stdout,
+ "write plane",
+ &[
+ ("provider", view.write_plane.provider_runtime_id.as_str()),
+ ("binding model", view.write_plane.binding_model.as_str()),
+ ("state", view.write_plane.state.as_str()),
+ ("provenance", view.write_plane.provenance.as_str()),
+ ("source", view.write_plane.source.as_str()),
+ ("target", write_plane_target.as_str()),
+ ("detail", view.write_plane.detail.as_str()),
+ (
+ "bridge auth configured",
+ yes_no(view.write_plane.bridge_auth_configured),
+ ),
+ ],
+ )?;
let workflow_target = format_runtime_target(
view.workflow.target_kind.as_deref(),
view.workflow.target.as_deref(),
@@ -628,6 +649,7 @@ fn render_config_show(
("provider", view.workflow.provider_runtime_id.as_str()),
("binding model", view.workflow.binding_model.as_str()),
("state", view.workflow.state.as_str()),
+ ("provenance", view.workflow.provenance.as_str()),
("source", view.workflow.source.as_str()),
("target", workflow_target.as_str()),
("hyf helper", view.workflow.hyf_helper_state.as_str()),
@@ -645,6 +667,23 @@ fn render_config_show(
("executable", view.hyf.executable.as_str()),
],
)?;
+ let hyf_provider_target = format_runtime_target(
+ view.hyf_provider.target_kind.as_deref(),
+ view.hyf_provider.target.as_deref(),
+ );
+ let mut hyf_provider_rows = vec![
+ ("provider", view.hyf_provider.provider_runtime_id.as_str()),
+ ("binding model", view.hyf_provider.binding_model.as_str()),
+ ("state", view.hyf_provider.state.as_str()),
+ ("provenance", view.hyf_provider.provenance.as_str()),
+ ("source", view.hyf_provider.source.as_str()),
+ ("target", hyf_provider_target.as_str()),
+ ("executable", view.hyf_provider.executable.as_str()),
+ ];
+ if let Some(reason) = &view.hyf_provider.reason {
+ hyf_provider_rows.push(("reason", reason.as_str()));
+ }
+ render_pairs(stdout, "hyf provider", hyf_provider_rows.as_slice())?;
render_pairs(
stdout,
"rpc",
@@ -674,6 +713,28 @@ fn render_config_show(
.collect(),
};
render_table(stdout, &table)?;
+ writeln!(stdout)?;
+ writeln!(stdout, "resolved providers")?;
+ let resolved_table = Table {
+ headers: &["capability", "provider", "state", "provenance", "target"],
+ rows: view
+ .resolved_providers
+ .iter()
+ .map(|provider| {
+ vec![
+ provider.capability_id.clone(),
+ provider.provider_runtime_id.clone(),
+ provider.state.clone(),
+ provider.provenance.clone(),
+ format_runtime_target(
+ provider.target_kind.as_deref(),
+ provider.target.as_deref(),
+ ),
+ ]
+ })
+ .collect(),
+ };
+ render_table(stdout, &resolved_table)?;
writeln!(stdout, "source: {}", view.source)?;
Ok(())
}
diff --git a/src/runtime/config.rs b/src/runtime/config.rs
@@ -380,7 +380,7 @@ struct CapabilityBindingSpec {
}
pub(crate) const SIGNER_REMOTE_NIP46_CAPABILITY: &str = "signer.remote_nip46";
-const WRITE_PLANE_TRADE_JSONRPC_CAPABILITY: &str = "write_plane.trade_jsonrpc";
+pub(crate) const WRITE_PLANE_TRADE_JSONRPC_CAPABILITY: &str = "write_plane.trade_jsonrpc";
pub(crate) const WORKFLOW_TRADE_CAPABILITY: &str = "workflow.trade";
pub(crate) const INFERENCE_HYF_STDIO_CAPABILITY: &str = "inference.hyf_stdio";
diff --git a/src/runtime/hyf.rs b/src/runtime/hyf.rs
@@ -378,7 +378,7 @@ mod tests {
enabled: true,
executable,
});
- assert_eq!(view.state, "ready");
+ assert_eq!(view.state, "ready", "reason: {:?}", view.reason);
assert_eq!(view.protocol_version, Some(HYF_PROTOCOL_VERSION));
assert_eq!(view.deterministic_available, Some(true));
}
@@ -396,7 +396,7 @@ mod tests {
enabled: true,
executable,
});
- assert_eq!(view.state, "unavailable");
+ assert_eq!(view.state, "unavailable", "reason: {:?}", view.reason);
assert!(
view.reason
.as_deref()
diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs
@@ -11,9 +11,9 @@ pub mod myc;
pub mod network;
pub mod order;
pub mod paths;
+pub mod provider;
pub mod signer;
pub mod sync;
-pub mod workflow;
use std::process::ExitCode;
diff --git a/src/runtime/provider.rs b/src/runtime/provider.rs
@@ -0,0 +1,428 @@
+use crate::runtime::config::{
+ CapabilityBindingInspection, CapabilityBindingInspectionState, RuntimeConfig,
+ INFERENCE_HYF_STDIO_CAPABILITY, WORKFLOW_TRADE_CAPABILITY,
+ WRITE_PLANE_TRADE_JSONRPC_CAPABILITY,
+};
+use crate::runtime::hyf;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ProviderProvenance {
+ ExplicitBinding,
+ ManagedDefault,
+ DirectConfig,
+ Disabled,
+ Unavailable,
+}
+
+impl ProviderProvenance {
+ pub fn as_str(self) -> &'static str {
+ match self {
+ Self::ExplicitBinding => "explicit_binding",
+ Self::ManagedDefault => "managed_default",
+ Self::DirectConfig => "direct_config",
+ Self::Disabled => "disabled",
+ Self::Unavailable => "unavailable",
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ResolvedProviderView {
+ pub capability_id: String,
+ pub provider_runtime_id: String,
+ pub binding_model: String,
+ pub state: String,
+ pub provenance: String,
+ pub source: String,
+ pub target_kind: Option<String>,
+ pub target: Option<String>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct WritePlaneProviderView {
+ pub provider_runtime_id: String,
+ pub binding_model: String,
+ pub state: String,
+ pub provenance: String,
+ pub source: String,
+ pub target_kind: Option<String>,
+ pub target: Option<String>,
+ pub detail: String,
+ pub bridge_auth_configured: bool,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct WorkflowProviderView {
+ pub provider_runtime_id: String,
+ pub binding_model: String,
+ pub state: String,
+ pub provenance: String,
+ pub source: String,
+ pub target_kind: Option<String>,
+ pub target: Option<String>,
+ pub hyf_helper_state: String,
+ pub hyf_helper_detail: String,
+}
+
+impl WorkflowProviderView {
+ pub fn detail(&self) -> String {
+ match (self.target_kind.as_deref(), self.target.as_deref()) {
+ (Some(target_kind), Some(target)) if self.state == "configured" => {
+ format!(
+ "{} workflow provider configured via {} {}",
+ self.provider_runtime_id, target_kind, target
+ )
+ }
+ _ if self.state == "configured" => {
+ format!("{} workflow provider configured", self.provider_runtime_id)
+ }
+ _ => self.source.clone(),
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct HyfProviderView {
+ pub provider_runtime_id: String,
+ pub binding_model: String,
+ pub state: String,
+ pub provenance: String,
+ pub source: String,
+ pub target_kind: Option<String>,
+ pub target: Option<String>,
+ pub executable: String,
+ pub reason: Option<String>,
+ pub protocol_version: Option<u64>,
+ pub deterministic_available: Option<bool>,
+}
+
+pub fn resolve_write_plane_provider(config: &RuntimeConfig) -> WritePlaneProviderView {
+ let _binding = inspect_binding(config, WRITE_PLANE_TRADE_JSONRPC_CAPABILITY);
+ WritePlaneProviderView {
+ provider_runtime_id: "radrootsd".to_owned(),
+ binding_model: "daemon_backed_jsonrpc".to_owned(),
+ state: "configured".to_owned(),
+ provenance: ProviderProvenance::DirectConfig.as_str().to_owned(),
+ source: "raw rpc config resolves the current write plane".to_owned(),
+ target_kind: None,
+ target: Some(config.rpc.url.clone()),
+ detail: "actor-authored durable writes still resolve through rpc.url until authoritative write-plane binding resolution lands".to_owned(),
+ bridge_auth_configured: config.rpc.bridge_bearer_token.is_some(),
+ }
+}
+
+pub fn resolve_workflow_provider(config: &RuntimeConfig) -> WorkflowProviderView {
+ let binding = inspect_binding(config, WORKFLOW_TRADE_CAPABILITY);
+ let provenance = binding_provenance(&binding).as_str().to_owned();
+
+ WorkflowProviderView {
+ provider_runtime_id: binding.provider_runtime_id,
+ binding_model: binding.binding_model,
+ state: binding.state.as_str().to_owned(),
+ provenance,
+ source: binding.source,
+ target_kind: binding.target_kind,
+ target: binding.target,
+ hyf_helper_state: "not_implied".to_owned(),
+ hyf_helper_detail:
+ "cli bindings do not imply an rhi -> hyf helper path; any worker helper remains explicit and optional"
+ .to_owned(),
+ }
+}
+
+pub fn resolve_hyf_provider(config: &RuntimeConfig) -> HyfProviderView {
+ let binding = inspect_binding(config, INFERENCE_HYF_STDIO_CAPABILITY);
+ let status = hyf::resolve_runtime_status(config);
+ let binding_configured = binding.state == CapabilityBindingInspectionState::Configured;
+ let provenance = if binding_configured {
+ binding_provenance(&binding)
+ } else if status.state == "disabled" {
+ ProviderProvenance::Disabled
+ } else {
+ ProviderProvenance::DirectConfig
+ }
+ .as_str()
+ .to_owned();
+ let target_kind = hyf_target_kind(config, &binding);
+ let target = hyf_target(config, &binding);
+ let executable = hyf_executable(config, &binding, &status);
+ let source = if binding_configured {
+ binding.source.clone()
+ } else {
+ status.source.clone()
+ };
+
+ HyfProviderView {
+ provider_runtime_id: binding.provider_runtime_id,
+ binding_model: binding.binding_model,
+ state: status.state,
+ provenance,
+ source,
+ target_kind,
+ target,
+ executable,
+ reason: status.reason,
+ protocol_version: status.protocol_version,
+ deterministic_available: status.deterministic_available,
+ }
+}
+
+pub fn resolve_capability_providers(config: &RuntimeConfig) -> Vec<ResolvedProviderView> {
+ let write = resolve_write_plane_provider(config);
+ let workflow = resolve_workflow_provider(config);
+ let hyf = resolve_hyf_provider(config);
+
+ vec![
+ ResolvedProviderView {
+ capability_id: WRITE_PLANE_TRADE_JSONRPC_CAPABILITY.to_owned(),
+ provider_runtime_id: write.provider_runtime_id,
+ binding_model: write.binding_model,
+ state: write.state,
+ provenance: write.provenance,
+ source: write.source,
+ target_kind: write.target_kind,
+ target: write.target,
+ },
+ ResolvedProviderView {
+ capability_id: WORKFLOW_TRADE_CAPABILITY.to_owned(),
+ provider_runtime_id: workflow.provider_runtime_id,
+ binding_model: workflow.binding_model,
+ state: workflow.state,
+ provenance: workflow.provenance,
+ source: workflow.source,
+ target_kind: workflow.target_kind,
+ target: workflow.target,
+ },
+ ResolvedProviderView {
+ capability_id: INFERENCE_HYF_STDIO_CAPABILITY.to_owned(),
+ provider_runtime_id: hyf.provider_runtime_id,
+ binding_model: hyf.binding_model,
+ state: hyf.state,
+ provenance: hyf.provenance,
+ source: hyf.source,
+ target_kind: hyf.target_kind,
+ target: hyf.target,
+ },
+ ]
+}
+
+fn inspect_binding(config: &RuntimeConfig, capability_id: &str) -> CapabilityBindingInspection {
+ config
+ .inspect_capability_bindings()
+ .into_iter()
+ .find(|binding| binding.capability_id == capability_id)
+ .expect("provider capability binding inspection must exist")
+}
+
+fn binding_provenance(binding: &CapabilityBindingInspection) -> ProviderProvenance {
+ match binding.state {
+ CapabilityBindingInspectionState::Configured => match binding.target_kind.as_deref() {
+ Some("managed_instance") => ProviderProvenance::ManagedDefault,
+ _ => ProviderProvenance::ExplicitBinding,
+ },
+ CapabilityBindingInspectionState::Disabled => ProviderProvenance::Disabled,
+ CapabilityBindingInspectionState::NotConfigured => ProviderProvenance::Unavailable,
+ }
+}
+
+fn hyf_target_kind(
+ config: &RuntimeConfig,
+ binding: &CapabilityBindingInspection,
+) -> Option<String> {
+ if binding.state == CapabilityBindingInspectionState::Configured {
+ return binding.target_kind.clone();
+ }
+ if config.hyf.enabled {
+ return Some("direct_config".to_owned());
+ }
+ None
+}
+
+fn hyf_target(config: &RuntimeConfig, binding: &CapabilityBindingInspection) -> Option<String> {
+ if binding.state == CapabilityBindingInspectionState::Configured {
+ return binding.target.clone();
+ }
+ if config.hyf.enabled {
+ return Some(config.hyf.executable.display().to_string());
+ }
+ None
+}
+
+fn hyf_executable(
+ config: &RuntimeConfig,
+ binding: &CapabilityBindingInspection,
+ status: &hyf::HyfStatusView,
+) -> String {
+ if binding.state == CapabilityBindingInspectionState::Configured
+ && binding.target_kind.as_deref() == Some("explicit_endpoint")
+ {
+ return binding.target.clone().unwrap_or_else(|| status.executable.clone());
+ }
+ if !config.hyf.enabled {
+ return status.executable.clone();
+ }
+ status.executable.clone()
+}
+
+#[cfg(test)]
+mod tests {
+ use std::path::PathBuf;
+
+ use radroots_runtime_paths::RadrootsMigrationReport;
+ use radroots_secret_vault::RadrootsSecretBackend;
+
+ use super::{
+ ProviderProvenance, resolve_capability_providers, resolve_hyf_provider,
+ resolve_workflow_provider, resolve_write_plane_provider,
+ };
+ use crate::runtime::config::{
+ AccountConfig, AccountSecretContractConfig, CapabilityBindingConfig,
+ CapabilityBindingSource, CapabilityBindingTargetKind, HyfConfig, IdentityConfig,
+ LocalConfig, LoggingConfig, MigrationConfig, MycConfig, OutputConfig, OutputFormat,
+ PathsConfig, RelayConfig, RelayConfigSource, RelayPublishPolicy, RpcConfig, RuntimeConfig,
+ SignerBackend, SignerConfig, Verbosity,
+ };
+
+ fn sample_config(bindings: Vec<CapabilityBindingConfig>, hyf_enabled: bool) -> RuntimeConfig {
+ RuntimeConfig {
+ output: OutputConfig {
+ format: OutputFormat::Human,
+ verbosity: Verbosity::Normal,
+ color: true,
+ dry_run: false,
+ },
+ paths: PathsConfig {
+ profile: "interactive_user".into(),
+ profile_source: "default".into(),
+ allowed_profiles: vec!["interactive_user".into()],
+ root_source: "host_defaults".into(),
+ repo_local_root: None,
+ repo_local_root_source: None,
+ subordinate_path_override_source: "runtime_config".into(),
+ app_namespace: "apps/cli".into(),
+ shared_accounts_namespace: "shared/accounts".into(),
+ shared_identities_namespace: "shared/identities".into(),
+ app_config_path: PathBuf::from("/tmp/config.toml"),
+ workspace_config_path: PathBuf::from("/tmp/workspace-config.toml"),
+ app_data_root: PathBuf::from("/tmp/data"),
+ app_logs_root: PathBuf::from("/tmp/logs"),
+ shared_accounts_data_root: PathBuf::from("/tmp/shared/accounts"),
+ shared_accounts_secrets_root: PathBuf::from("/tmp/shared/accounts-secrets"),
+ default_identity_path: PathBuf::from("/tmp/default-identity.json"),
+ },
+ migration: MigrationConfig {
+ report: RadrootsMigrationReport::empty(),
+ },
+ logging: LoggingConfig {
+ filter: "info".into(),
+ directory: None,
+ stdout: true,
+ },
+ account: AccountConfig {
+ selector: None,
+ store_path: PathBuf::from("/tmp/store.json"),
+ secrets_dir: PathBuf::from("/tmp/secrets"),
+ secret_backend: RadrootsSecretBackend::EncryptedFile,
+ secret_fallback: None,
+ },
+ account_secret_contract: AccountSecretContractConfig {
+ default_backend: "host_vault".into(),
+ default_fallback: Some("encrypted_file".into()),
+ allowed_backends: vec!["host_vault".into(), "encrypted_file".into()],
+ host_vault_policy: Some("desktop".into()),
+ uses_protected_store: true,
+ },
+ identity: IdentityConfig {
+ path: PathBuf::from("/tmp/default-identity.json"),
+ },
+ signer: SignerConfig {
+ backend: SignerBackend::Local,
+ },
+ relay: RelayConfig {
+ urls: Vec::new(),
+ publish_policy: RelayPublishPolicy::Any,
+ source: RelayConfigSource::Defaults,
+ },
+ local: LocalConfig {
+ root: PathBuf::from("/tmp/local"),
+ replica_db_path: PathBuf::from("/tmp/local/replica.sqlite"),
+ backups_dir: PathBuf::from("/tmp/local/backups"),
+ exports_dir: PathBuf::from("/tmp/local/exports"),
+ },
+ myc: MycConfig {
+ executable: PathBuf::from("myc"),
+ },
+ hyf: HyfConfig {
+ enabled: hyf_enabled,
+ executable: PathBuf::from("hyfd"),
+ },
+ rpc: RpcConfig {
+ url: "http://127.0.0.1:7070".into(),
+ bridge_bearer_token: None,
+ },
+ capability_bindings: bindings,
+ }
+ }
+
+ #[test]
+ fn write_plane_uses_direct_config_provenance() {
+ let view = resolve_write_plane_provider(&sample_config(Vec::new(), false));
+ assert_eq!(view.provenance, ProviderProvenance::DirectConfig.as_str());
+ assert_eq!(view.target.as_deref(), Some("http://127.0.0.1:7070"));
+ }
+
+ #[test]
+ fn workflow_uses_explicit_binding_provenance_when_configured() {
+ let binding = CapabilityBindingConfig {
+ capability_id: "workflow.trade".into(),
+ provider_runtime_id: "rhi".into(),
+ binding_model: "out_of_process_worker".into(),
+ source: CapabilityBindingSource::WorkspaceConfig,
+ target_kind: CapabilityBindingTargetKind::ExplicitEndpoint,
+ target: "/tmp/rhi".into(),
+ managed_account_ref: None,
+ signer_session_ref: None,
+ };
+ let view = resolve_workflow_provider(&sample_config(vec![binding], false));
+ assert_eq!(view.provenance, ProviderProvenance::ExplicitBinding.as_str());
+ assert_eq!(view.target_kind.as_deref(), Some("explicit_endpoint"));
+ }
+
+ #[test]
+ fn hyf_uses_direct_config_when_enabled_without_binding() {
+ let view = resolve_hyf_provider(&sample_config(Vec::new(), true));
+ assert_eq!(view.provenance, ProviderProvenance::DirectConfig.as_str());
+ assert_eq!(view.target_kind.as_deref(), Some("direct_config"));
+ assert_eq!(view.target.as_deref(), Some("hyfd"));
+ }
+
+ #[test]
+ fn hyf_binding_remains_visible_when_runtime_is_disabled() {
+ let binding = CapabilityBindingConfig {
+ capability_id: "inference.hyf_stdio".into(),
+ provider_runtime_id: "hyf".into(),
+ binding_model: "stdio_service".into(),
+ source: CapabilityBindingSource::UserConfig,
+ target_kind: CapabilityBindingTargetKind::ExplicitEndpoint,
+ target: "bin/hyfd-user".into(),
+ managed_account_ref: None,
+ signer_session_ref: None,
+ };
+ let view = resolve_hyf_provider(&sample_config(vec![binding], false));
+ assert_eq!(view.state, "disabled");
+ assert_eq!(view.provenance, ProviderProvenance::ExplicitBinding.as_str());
+ assert_eq!(view.source, "user config [[capability_binding]]");
+ assert_eq!(view.target_kind.as_deref(), Some("explicit_endpoint"));
+ assert_eq!(view.target.as_deref(), Some("bin/hyfd-user"));
+ assert_eq!(view.executable, "bin/hyfd-user");
+ }
+
+ #[test]
+ fn capability_provider_list_covers_write_workflow_and_hyf() {
+ let providers = resolve_capability_providers(&sample_config(Vec::new(), false));
+ assert_eq!(providers.len(), 3);
+ assert_eq!(providers[0].capability_id, "write_plane.trade_jsonrpc");
+ assert_eq!(providers[1].capability_id, "workflow.trade");
+ assert_eq!(providers[2].capability_id, "inference.hyf_stdio");
+ }
+}
diff --git a/src/runtime/workflow.rs b/src/runtime/workflow.rs
@@ -1,178 +0,0 @@
-use crate::runtime::config::{RuntimeConfig, WORKFLOW_TRADE_CAPABILITY};
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct WorkflowProviderStatusView {
- pub provider_runtime_id: String,
- pub binding_model: String,
- pub state: String,
- pub source: String,
- pub target_kind: Option<String>,
- pub target: Option<String>,
- pub hyf_helper_state: String,
- pub hyf_helper_detail: String,
-}
-
-impl WorkflowProviderStatusView {
- pub fn detail(&self) -> String {
- match (self.target_kind.as_deref(), self.target.as_deref()) {
- (Some(target_kind), Some(target)) if self.state == "configured" => {
- format!(
- "{} workflow provider configured via {} {}",
- self.provider_runtime_id, target_kind, target
- )
- }
- _ if self.state == "configured" => {
- format!("{} workflow provider configured", self.provider_runtime_id)
- }
- _ => self.source.clone(),
- }
- }
-}
-
-pub fn resolve_workflow_provider(config: &RuntimeConfig) -> WorkflowProviderStatusView {
- let binding = config
- .inspect_capability_bindings()
- .into_iter()
- .find(|binding| binding.capability_id == WORKFLOW_TRADE_CAPABILITY)
- .expect("workflow.trade binding inspection must exist");
-
- WorkflowProviderStatusView {
- provider_runtime_id: binding.provider_runtime_id,
- binding_model: binding.binding_model,
- state: binding.state.as_str().to_owned(),
- source: binding.source,
- target_kind: binding.target_kind,
- target: binding.target,
- hyf_helper_state: "not_implied".to_owned(),
- hyf_helper_detail:
- "cli bindings do not imply an rhi -> hyf helper path; any worker helper remains explicit and optional"
- .to_owned(),
- }
-}
-
-#[cfg(test)]
-mod tests {
- use std::path::PathBuf;
-
- use radroots_runtime_paths::RadrootsMigrationReport;
- use radroots_secret_vault::RadrootsSecretBackend;
-
- use super::resolve_workflow_provider;
- use crate::runtime::config::{
- AccountConfig, AccountSecretContractConfig, CapabilityBindingConfig,
- CapabilityBindingSource, CapabilityBindingTargetKind, HyfConfig, IdentityConfig,
- LocalConfig, LoggingConfig, MigrationConfig, MycConfig, OutputConfig, OutputFormat,
- PathsConfig, RelayConfig, RelayConfigSource, RelayPublishPolicy, RpcConfig, RuntimeConfig,
- SignerBackend, SignerConfig, Verbosity,
- };
-
- fn sample_config(workflow_binding: Option<CapabilityBindingConfig>) -> RuntimeConfig {
- RuntimeConfig {
- output: OutputConfig {
- format: OutputFormat::Human,
- verbosity: Verbosity::Normal,
- color: true,
- dry_run: false,
- },
- paths: PathsConfig {
- profile: "interactive_user".into(),
- profile_source: "default".into(),
- allowed_profiles: vec!["interactive_user".into()],
- root_source: "host_defaults".into(),
- repo_local_root: None,
- repo_local_root_source: None,
- subordinate_path_override_source: "runtime_config".into(),
- app_namespace: "apps/cli".into(),
- shared_accounts_namespace: "shared/accounts".into(),
- shared_identities_namespace: "shared/identities".into(),
- app_config_path: PathBuf::from("/tmp/config.toml"),
- workspace_config_path: PathBuf::from("/tmp/workspace-config.toml"),
- app_data_root: PathBuf::from("/tmp/data"),
- app_logs_root: PathBuf::from("/tmp/logs"),
- shared_accounts_data_root: PathBuf::from("/tmp/shared/accounts"),
- shared_accounts_secrets_root: PathBuf::from("/tmp/shared/accounts-secrets"),
- default_identity_path: PathBuf::from("/tmp/default-identity.json"),
- },
- migration: MigrationConfig {
- report: RadrootsMigrationReport::empty(),
- },
- logging: LoggingConfig {
- filter: "info".into(),
- directory: None,
- stdout: true,
- },
- account: AccountConfig {
- selector: None,
- store_path: PathBuf::from("/tmp/store.json"),
- secrets_dir: PathBuf::from("/tmp/secrets"),
- secret_backend: RadrootsSecretBackend::EncryptedFile,
- secret_fallback: None,
- },
- account_secret_contract: AccountSecretContractConfig {
- default_backend: "host_vault".into(),
- default_fallback: Some("encrypted_file".into()),
- allowed_backends: vec!["host_vault".into(), "encrypted_file".into()],
- host_vault_policy: Some("desktop".into()),
- uses_protected_store: true,
- },
- identity: IdentityConfig {
- path: PathBuf::from("/tmp/default-identity.json"),
- },
- signer: SignerConfig {
- backend: SignerBackend::Local,
- },
- relay: RelayConfig {
- urls: Vec::new(),
- publish_policy: RelayPublishPolicy::Any,
- source: RelayConfigSource::Defaults,
- },
- local: LocalConfig {
- root: PathBuf::from("/tmp/local"),
- replica_db_path: PathBuf::from("/tmp/local/replica.sqlite"),
- backups_dir: PathBuf::from("/tmp/local/backups"),
- exports_dir: PathBuf::from("/tmp/local/exports"),
- },
- myc: MycConfig {
- executable: PathBuf::from("myc"),
- },
- hyf: HyfConfig {
- enabled: false,
- executable: PathBuf::from("hyfd"),
- },
- rpc: RpcConfig {
- url: "http://127.0.0.1:7070".into(),
- bridge_bearer_token: None,
- },
- capability_bindings: workflow_binding.into_iter().collect(),
- }
- }
-
- #[test]
- fn workflow_provider_reports_not_configured_without_binding() {
- let view = resolve_workflow_provider(&sample_config(None));
- assert_eq!(view.provider_runtime_id, "rhi");
- assert_eq!(view.binding_model, "out_of_process_worker");
- assert_eq!(view.state, "not_configured");
- assert_eq!(view.source, "no explicit capability binding");
- assert_eq!(view.hyf_helper_state, "not_implied");
- }
-
- #[test]
- fn workflow_provider_reports_explicit_binding_details() {
- let binding = CapabilityBindingConfig {
- capability_id: "workflow.trade".into(),
- provider_runtime_id: "rhi".into(),
- binding_model: "out_of_process_worker".into(),
- source: CapabilityBindingSource::WorkspaceConfig,
- target_kind: CapabilityBindingTargetKind::ExplicitEndpoint,
- target: "/tmp/rhi-binary".into(),
- managed_account_ref: None,
- signer_session_ref: None,
- };
- let view = resolve_workflow_provider(&sample_config(Some(binding)));
- assert_eq!(view.state, "configured");
- assert_eq!(view.target_kind.as_deref(), Some("explicit_endpoint"));
- assert_eq!(view.target.as_deref(), Some("/tmp/rhi-binary"));
- assert!(view.detail().contains("explicit_endpoint /tmp/rhi-binary"));
- }
-}
diff --git a/tests/runtime_show.rs b/tests/runtime_show.rs
@@ -279,14 +279,40 @@ fn config_show_json_reports_default_bootstrap_state() {
.to_string()
);
assert_eq!(json["myc"]["executable"], "myc");
+ assert_eq!(json["write_plane"]["provider_runtime_id"], "radrootsd");
+ assert_eq!(json["write_plane"]["binding_model"], "daemon_backed_jsonrpc");
+ assert_eq!(json["write_plane"]["state"], "configured");
+ assert_eq!(json["write_plane"]["provenance"], "direct_config");
+ assert_eq!(
+ json["write_plane"]["source"],
+ "raw rpc config resolves the current write plane"
+ );
+ assert_eq!(json["write_plane"]["target"], "http://127.0.0.1:7070");
+ assert_eq!(json["write_plane"]["bridge_auth_configured"], false);
assert_eq!(json["workflow"]["provider_runtime_id"], "rhi");
assert_eq!(json["workflow"]["binding_model"], "out_of_process_worker");
assert_eq!(json["workflow"]["state"], "not_configured");
+ assert_eq!(json["workflow"]["provenance"], "unavailable");
assert_eq!(json["workflow"]["source"], "no explicit capability binding");
assert_eq!(json["workflow"]["hyf_helper_state"], "not_implied");
+ assert_eq!(json["hyf_provider"]["provider_runtime_id"], "hyf");
+ assert_eq!(json["hyf_provider"]["binding_model"], "stdio_service");
+ assert_eq!(json["hyf_provider"]["state"], "disabled");
+ assert_eq!(json["hyf_provider"]["provenance"], "disabled");
+ assert_eq!(
+ json["hyf_provider"]["source"],
+ "hyf status control request ยท local first"
+ );
assert_eq!(json["rpc"]["url"], "http://127.0.0.1:7070");
assert_eq!(json["rpc"]["bridge_auth_configured"], false);
assert_eq!(
+ json["resolved_providers"]
+ .as_array()
+ .expect("resolved providers")
+ .len(),
+ 3
+ );
+ assert_eq!(
json["capability_bindings"]
.as_array()
.expect("capability bindings")
@@ -651,6 +677,7 @@ target = "bin/hyfd-user"
assert_eq!(workflow["target"], "workflow-default");
assert_eq!(json["workflow"]["provider_runtime_id"], "rhi");
assert_eq!(json["workflow"]["state"], "configured");
+ assert_eq!(json["workflow"]["provenance"], "managed_default");
assert_eq!(
json["workflow"]["source"],
"user config [[capability_binding]]"
@@ -663,6 +690,13 @@ target = "bin/hyfd-user"
.as_str()
.is_some_and(|detail| detail.contains("do not imply"))
);
+ assert_eq!(json["write_plane"]["provenance"], "direct_config");
+ assert_eq!(json["write_plane"]["target"], "http://127.0.0.1:7070");
+ assert_eq!(json["hyf_provider"]["provider_runtime_id"], "hyf");
+ assert_eq!(json["hyf_provider"]["provenance"], "explicit_binding");
+ assert_eq!(json["hyf_provider"]["target_kind"], "explicit_endpoint");
+ assert_eq!(json["hyf_provider"]["target"], "bin/hyfd-user");
+ assert_eq!(json["hyf_provider"]["executable"], "bin/hyfd-user");
let inference = binding_by_capability(&json, "inference.hyf_stdio");
assert_eq!(inference["state"], "configured");