lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

managed.rs (51787B)


      1 use std::path::PathBuf;
      2 
      3 use radroots_runtime_paths::{
      4     RadrootsPathOverrides, RadrootsPathProfile, RadrootsPathResolver, RadrootsRuntimePathSelection,
      5 };
      6 
      7 use crate::{
      8     BootstrapRuntimeContract, ManagedRuntimeHealthState, ManagedRuntimeInstallState,
      9     ManagedRuntimeInstancePaths, ManagedRuntimeInstanceRecord, ManagedRuntimeInstanceRegistry,
     10     ManagementModeContract, RadrootsRuntimeManagementContract, RadrootsRuntimeManagerError,
     11     load_registry, resolve_instance_paths, resolve_shared_paths,
     12 };
     13 
     14 #[derive(Debug, Clone)]
     15 pub struct ManagedRuntimeContext {
     16     pub contract: RadrootsRuntimeManagementContract,
     17     pub shared_paths: crate::ManagedRuntimeSharedPaths,
     18     pub registry: ManagedRuntimeInstanceRegistry,
     19 }
     20 
     21 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
     22 pub enum ManagedRuntimeGroup {
     23     ActiveManagedTarget,
     24     DefinedManagedTarget,
     25     BootstrapOnly,
     26     Unknown,
     27 }
     28 
     29 impl ManagedRuntimeGroup {
     30     pub fn as_str(self) -> &'static str {
     31         match self {
     32             Self::ActiveManagedTarget => "active_managed_target",
     33             Self::DefinedManagedTarget => "defined_managed_target",
     34             Self::BootstrapOnly => "bootstrap_only",
     35             Self::Unknown => "unknown",
     36         }
     37     }
     38 
     39     pub fn posture(self) -> &'static str {
     40         match self {
     41             Self::ActiveManagedTarget => "active_managed_target",
     42             Self::DefinedManagedTarget => "defined_future_target",
     43             Self::BootstrapOnly => "bootstrap_only_direct_binding",
     44             Self::Unknown => "unknown_runtime",
     45         }
     46     }
     47 }
     48 
     49 #[derive(Debug, Clone)]
     50 pub struct ManagedRuntimeTarget {
     51     pub runtime_id: String,
     52     pub instance_id: String,
     53     pub instance_source: String,
     54     pub runtime_group: ManagedRuntimeGroup,
     55     pub management_mode: Option<String>,
     56     pub mode_contract: Option<ManagementModeContract>,
     57     pub bootstrap: Option<BootstrapRuntimeContract>,
     58     pub instance_record: Option<ManagedRuntimeInstanceRecord>,
     59     pub predicted_paths: Option<ManagedRuntimeInstancePaths>,
     60     pub registry_path: PathBuf,
     61 }
     62 
     63 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
     64 pub enum ManagedRuntimeInspectionAvailability {
     65     Success,
     66     Unconfigured,
     67     Unsupported,
     68 }
     69 
     70 #[derive(Debug, Clone, PartialEq, Eq)]
     71 pub struct ManagedRuntimeInspection<T> {
     72     pub availability: ManagedRuntimeInspectionAvailability,
     73     pub view: T,
     74 }
     75 
     76 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
     77 pub enum ManagedRuntimeLifecycleAction {
     78     Install,
     79     Uninstall,
     80     Start,
     81     Stop,
     82     Restart,
     83     ConfigSet,
     84 }
     85 
     86 impl ManagedRuntimeLifecycleAction {
     87     pub fn as_str(self) -> &'static str {
     88         match self {
     89             Self::Install => "install",
     90             Self::Uninstall => "uninstall",
     91             Self::Start => "start",
     92             Self::Stop => "stop",
     93             Self::Restart => "restart",
     94             Self::ConfigSet => "config_set",
     95         }
     96     }
     97 }
     98 
     99 #[derive(Debug, Clone, PartialEq, Eq)]
    100 pub struct ManagedRuntimeStatusInspection {
    101     pub runtime_id: String,
    102     pub instance_id: String,
    103     pub instance_source: String,
    104     pub runtime_group: String,
    105     pub management_posture: String,
    106     pub state: String,
    107     pub source: String,
    108     pub detail: String,
    109     pub management_mode: Option<String>,
    110     pub service_manager_integration: Option<bool>,
    111     pub uses_absolute_binary_paths: Option<bool>,
    112     pub preferred_cli_binding: Option<bool>,
    113     pub install_state: String,
    114     pub health_state: String,
    115     pub health_source: String,
    116     pub registry_path: PathBuf,
    117     pub lifecycle_actions: Vec<String>,
    118     pub instance_paths: Option<ManagedRuntimeInstancePaths>,
    119     pub instance_record: Option<ManagedRuntimeInstanceRecord>,
    120 }
    121 
    122 #[derive(Debug, Clone, PartialEq, Eq)]
    123 pub struct ManagedRuntimeLogsInspection {
    124     pub runtime_id: String,
    125     pub instance_id: String,
    126     pub instance_source: String,
    127     pub runtime_group: String,
    128     pub state: String,
    129     pub source: String,
    130     pub detail: String,
    131     pub stdout_log_path: Option<PathBuf>,
    132     pub stderr_log_path: Option<PathBuf>,
    133     pub stdout_log_present: bool,
    134     pub stderr_log_present: bool,
    135 }
    136 
    137 #[derive(Debug, Clone, PartialEq, Eq)]
    138 pub struct ManagedRuntimeConfigInspection {
    139     pub runtime_id: String,
    140     pub instance_id: String,
    141     pub instance_source: String,
    142     pub runtime_group: String,
    143     pub state: String,
    144     pub source: String,
    145     pub detail: String,
    146     pub config_format: Option<String>,
    147     pub config_path: Option<PathBuf>,
    148     pub config_present: bool,
    149     pub requires_bootstrap_secret: Option<bool>,
    150     pub requires_config_bootstrap: Option<bool>,
    151     pub requires_signer_provider: Option<bool>,
    152 }
    153 
    154 #[derive(Debug, Clone, PartialEq, Eq)]
    155 pub struct ManagedRuntimeActionInspection {
    156     pub action: String,
    157     pub runtime_id: String,
    158     pub instance_id: String,
    159     pub instance_source: String,
    160     pub runtime_group: String,
    161     pub state: String,
    162     pub source: String,
    163     pub detail: String,
    164     pub mutates_bindings: bool,
    165     pub next_step: Option<String>,
    166 }
    167 
    168 pub fn load_management_context(
    169     contract: RadrootsRuntimeManagementContract,
    170     resolver: &RadrootsPathResolver,
    171     profile: RadrootsPathProfile,
    172     overrides: &RadrootsPathOverrides,
    173 ) -> Result<ManagedRuntimeContext, RadrootsRuntimeManagerError> {
    174     let mode_id = active_management_mode_for_profile(&contract, profile)?;
    175     let shared_paths = resolve_shared_paths(&contract, resolver, profile, overrides, mode_id)?;
    176     let registry = load_registry(&shared_paths.instance_registry_path)?;
    177     Ok(ManagedRuntimeContext {
    178         contract,
    179         shared_paths,
    180         registry,
    181     })
    182 }
    183 
    184 pub fn load_management_context_with_selection(
    185     contract: RadrootsRuntimeManagementContract,
    186     resolver: &RadrootsPathResolver,
    187     selection: &RadrootsRuntimePathSelection,
    188 ) -> Result<ManagedRuntimeContext, RadrootsRuntimeManagerError> {
    189     let overrides = selection.caller_overrides()?;
    190     load_management_context(contract, resolver, selection.profile, &overrides)
    191 }
    192 
    193 pub fn active_management_mode_for_profile(
    194     contract: &RadrootsRuntimeManagementContract,
    195     profile: RadrootsPathProfile,
    196 ) -> Result<&str, RadrootsRuntimeManagerError> {
    197     let profile_id = profile.to_string();
    198     contract
    199         .mode
    200         .iter()
    201         .find(|(_, mode)| {
    202             mode.contract_state == "active"
    203                 && mode
    204                     .supported_profiles
    205                     .iter()
    206                     .any(|entry| entry == &profile_id)
    207         })
    208         .map(|(mode_id, _)| mode_id.as_str())
    209         .ok_or_else(|| RadrootsRuntimeManagerError::UnsupportedProfile {
    210             mode_id: "active".to_owned(),
    211             profile: profile_id,
    212         })
    213 }
    214 
    215 pub fn resolve_runtime_target(
    216     context: &ManagedRuntimeContext,
    217     runtime_id: &str,
    218     requested_instance_id: Option<&str>,
    219 ) -> ManagedRuntimeTarget {
    220     let runtime_group = runtime_group(&context.contract, runtime_id);
    221     let bootstrap = context.contract.bootstrap.get(runtime_id).cloned();
    222     let instance_id = requested_instance_id
    223         .map(ToOwned::to_owned)
    224         .or_else(|| {
    225             bootstrap
    226                 .as_ref()
    227                 .map(|entry| entry.default_instance_id.clone())
    228         })
    229         .unwrap_or_else(|| "default".to_owned());
    230     let instance_source = if requested_instance_id.is_some() {
    231         "command_arg".to_owned()
    232     } else if bootstrap.is_some() {
    233         "bootstrap_default".to_owned()
    234     } else {
    235         "implicit_default".to_owned()
    236     };
    237     let management_mode = bootstrap
    238         .as_ref()
    239         .map(|entry| entry.management_mode.clone());
    240     let mode_contract = management_mode
    241         .as_ref()
    242         .and_then(|mode_id| context.contract.mode.get(mode_id).cloned());
    243     let instance_record = context
    244         .registry
    245         .instances
    246         .iter()
    247         .find(|record| record.runtime_id == runtime_id && record.instance_id == instance_id)
    248         .cloned();
    249     let predicted_paths = if runtime_group == ManagedRuntimeGroup::ActiveManagedTarget {
    250         Some(resolve_instance_paths(
    251             &context.shared_paths,
    252             runtime_id,
    253             instance_id.as_str(),
    254         ))
    255     } else {
    256         None
    257     };
    258 
    259     ManagedRuntimeTarget {
    260         runtime_id: runtime_id.to_owned(),
    261         instance_id,
    262         instance_source,
    263         runtime_group,
    264         management_mode,
    265         mode_contract,
    266         bootstrap,
    267         instance_record,
    268         predicted_paths,
    269         registry_path: context.shared_paths.instance_registry_path.clone(),
    270     }
    271 }
    272 
    273 pub fn inspect_runtime_status(
    274     target: &ManagedRuntimeTarget,
    275     lifecycle_actions: &[String],
    276 ) -> ManagedRuntimeInspection<ManagedRuntimeStatusInspection> {
    277     let availability = if target.runtime_group == ManagedRuntimeGroup::Unknown {
    278         ManagedRuntimeInspectionAvailability::Unconfigured
    279     } else {
    280         ManagedRuntimeInspectionAvailability::Success
    281     };
    282 
    283     ManagedRuntimeInspection {
    284         availability,
    285         view: ManagedRuntimeStatusInspection {
    286             runtime_id: target.runtime_id.clone(),
    287             instance_id: target.instance_id.clone(),
    288             instance_source: target.instance_source.clone(),
    289             runtime_group: target.runtime_group.as_str().to_owned(),
    290             management_posture: target.runtime_group.posture().to_owned(),
    291             state: status_state(target).to_owned(),
    292             source: "runtime management contract + shared instance registry".to_owned(),
    293             detail: status_detail(target),
    294             management_mode: target.management_mode.clone(),
    295             service_manager_integration: target
    296                 .mode_contract
    297                 .as_ref()
    298                 .map(|mode| mode.service_manager_integration),
    299             uses_absolute_binary_paths: target
    300                 .mode_contract
    301                 .as_ref()
    302                 .map(|mode| mode.uses_absolute_binary_paths),
    303             preferred_cli_binding: target
    304                 .bootstrap
    305                 .as_ref()
    306                 .map(|entry| entry.preferred_cli_binding),
    307             install_state: target
    308                 .instance_record
    309                 .as_ref()
    310                 .map(|record| install_state_label(record.install_state))
    311                 .unwrap_or_else(|| install_state_label(ManagedRuntimeInstallState::NotInstalled))
    312                 .to_owned(),
    313             health_state: infer_health_state(target).0.to_owned(),
    314             health_source: infer_health_state(target).1.to_owned(),
    315             registry_path: target.registry_path.clone(),
    316             lifecycle_actions: if target.runtime_group == ManagedRuntimeGroup::ActiveManagedTarget {
    317                 lifecycle_actions.to_vec()
    318             } else {
    319                 Vec::new()
    320             },
    321             instance_paths: target.predicted_paths.clone(),
    322             instance_record: target.instance_record.clone(),
    323         },
    324     }
    325 }
    326 
    327 pub fn inspect_runtime_logs(
    328     target: &ManagedRuntimeTarget,
    329 ) -> ManagedRuntimeInspection<ManagedRuntimeLogsInspection> {
    330     let stdout_log_path = target
    331         .predicted_paths
    332         .as_ref()
    333         .map(|paths| paths.stdout_log_path.clone());
    334     let stderr_log_path = target
    335         .predicted_paths
    336         .as_ref()
    337         .map(|paths| paths.stderr_log_path.clone());
    338     let availability = match target.runtime_group {
    339         ManagedRuntimeGroup::Unknown => ManagedRuntimeInspectionAvailability::Unconfigured,
    340         ManagedRuntimeGroup::ActiveManagedTarget => ManagedRuntimeInspectionAvailability::Success,
    341         ManagedRuntimeGroup::DefinedManagedTarget | ManagedRuntimeGroup::BootstrapOnly => {
    342             if target.instance_record.is_some() {
    343                 ManagedRuntimeInspectionAvailability::Success
    344             } else {
    345                 ManagedRuntimeInspectionAvailability::Unsupported
    346             }
    347         }
    348     };
    349     let detail = match target.runtime_group {
    350         ManagedRuntimeGroup::ActiveManagedTarget => {
    351             "runtime logs report the managed stdout/stderr locations for the active managed instance"
    352                 .to_owned()
    353         }
    354         ManagedRuntimeGroup::DefinedManagedTarget => format!(
    355             "runtime `{}` is only a defined future managed target; no active generic logs surface exists without a registered instance",
    356             target.runtime_id
    357         ),
    358         ManagedRuntimeGroup::BootstrapOnly => format!(
    359             "runtime `{}` remains bootstrap_only and direct-bindable in this wave; generic managed logs are not admitted",
    360             target.runtime_id
    361         ),
    362         ManagedRuntimeGroup::Unknown => unknown_runtime_detail(target),
    363     };
    364 
    365     ManagedRuntimeInspection {
    366         availability,
    367         view: ManagedRuntimeLogsInspection {
    368             runtime_id: target.runtime_id.clone(),
    369             instance_id: target.instance_id.clone(),
    370             instance_source: target.instance_source.clone(),
    371             runtime_group: target.runtime_group.as_str().to_owned(),
    372             state: match availability {
    373                 ManagedRuntimeInspectionAvailability::Success => "ready".to_owned(),
    374                 ManagedRuntimeInspectionAvailability::Unconfigured => "unknown_runtime".to_owned(),
    375                 ManagedRuntimeInspectionAvailability::Unsupported => "unsupported".to_owned(),
    376             },
    377             source: "runtime management contract + shared instance registry".to_owned(),
    378             detail,
    379             stdout_log_path: stdout_log_path.clone(),
    380             stderr_log_path: stderr_log_path.clone(),
    381             stdout_log_present: path_present(stdout_log_path.as_ref()).unwrap_or_else(|| {
    382                 target
    383                     .instance_record
    384                     .as_ref()
    385                     .is_some_and(|record| record.logs_path.join("stdout.log").exists())
    386             }),
    387             stderr_log_present: path_present(stderr_log_path.as_ref()).unwrap_or_else(|| {
    388                 target
    389                     .instance_record
    390                     .as_ref()
    391                     .is_some_and(|record| record.logs_path.join("stderr.log").exists())
    392             }),
    393         },
    394     }
    395 }
    396 
    397 pub fn inspect_runtime_config(
    398     target: &ManagedRuntimeTarget,
    399 ) -> ManagedRuntimeInspection<ManagedRuntimeConfigInspection> {
    400     let availability = match target.runtime_group {
    401         ManagedRuntimeGroup::Unknown => ManagedRuntimeInspectionAvailability::Unconfigured,
    402         ManagedRuntimeGroup::ActiveManagedTarget => ManagedRuntimeInspectionAvailability::Success,
    403         ManagedRuntimeGroup::DefinedManagedTarget | ManagedRuntimeGroup::BootstrapOnly => {
    404             if target.instance_record.is_some() {
    405                 ManagedRuntimeInspectionAvailability::Success
    406             } else {
    407                 ManagedRuntimeInspectionAvailability::Unsupported
    408             }
    409         }
    410     };
    411     let config_path = target
    412         .instance_record
    413         .as_ref()
    414         .map(|record| record.config_path.clone());
    415     let detail = match target.runtime_group {
    416         ManagedRuntimeGroup::ActiveManagedTarget => {
    417             if config_path.is_some() {
    418                 "runtime config show reports the managed config location without mutating bindings"
    419                     .to_owned()
    420             } else {
    421                 format!(
    422                     "managed runtime `{}` has no registered instance config yet",
    423                     target.runtime_id
    424                 )
    425             }
    426         }
    427         ManagedRuntimeGroup::DefinedManagedTarget => format!(
    428             "runtime `{}` is only a defined future managed target; generic config surfaces are not admitted without a registered instance",
    429             target.runtime_id
    430         ),
    431         ManagedRuntimeGroup::BootstrapOnly => format!(
    432             "runtime `{}` remains bootstrap_only and direct-bindable in this wave; generic managed config is not admitted",
    433             target.runtime_id
    434         ),
    435         ManagedRuntimeGroup::Unknown => unknown_runtime_detail(target),
    436     };
    437 
    438     ManagedRuntimeInspection {
    439         availability,
    440         view: ManagedRuntimeConfigInspection {
    441             runtime_id: target.runtime_id.clone(),
    442             instance_id: target.instance_id.clone(),
    443             instance_source: target.instance_source.clone(),
    444             runtime_group: target.runtime_group.as_str().to_owned(),
    445             state: match availability {
    446                 ManagedRuntimeInspectionAvailability::Success => {
    447                     if config_path.is_some() {
    448                         "ready".to_owned()
    449                     } else {
    450                         "not_installed".to_owned()
    451                     }
    452                 }
    453                 ManagedRuntimeInspectionAvailability::Unconfigured => "unknown_runtime".to_owned(),
    454                 ManagedRuntimeInspectionAvailability::Unsupported => "unsupported".to_owned(),
    455             },
    456             source: "runtime management contract + shared instance registry".to_owned(),
    457             detail,
    458             config_format: target
    459                 .bootstrap
    460                 .as_ref()
    461                 .map(|entry| entry.config_format.clone()),
    462             config_path: config_path.clone(),
    463             config_present: config_path.as_ref().is_some_and(|path| path.exists()),
    464             requires_bootstrap_secret: target
    465                 .bootstrap
    466                 .as_ref()
    467                 .map(|entry| entry.requires_bootstrap_secret),
    468             requires_config_bootstrap: target
    469                 .bootstrap
    470                 .as_ref()
    471                 .map(|entry| entry.requires_config_bootstrap),
    472             requires_signer_provider: target
    473                 .bootstrap
    474                 .as_ref()
    475                 .map(|entry| entry.requires_signer_provider),
    476         },
    477     }
    478 }
    479 
    480 pub fn inspect_runtime_action(
    481     target: &ManagedRuntimeTarget,
    482     action: ManagedRuntimeLifecycleAction,
    483     detail_override: Option<String>,
    484 ) -> ManagedRuntimeInspection<ManagedRuntimeActionInspection> {
    485     let (availability, state, detail, next_step) = match target.runtime_group {
    486         ManagedRuntimeGroup::ActiveManagedTarget => (
    487             ManagedRuntimeInspectionAvailability::Unsupported,
    488             "deferred",
    489             detail_override.unwrap_or_else(|| {
    490                 format!(
    491                     "runtime {} `{}` is not supported for this managed target",
    492                     action.as_str().replace('_', " "),
    493                     target.runtime_id
    494                 )
    495             }),
    496             None,
    497         ),
    498         ManagedRuntimeGroup::DefinedManagedTarget => (
    499             ManagedRuntimeInspectionAvailability::Unsupported,
    500             "unsupported",
    501             detail_override.unwrap_or_else(|| {
    502                 format!(
    503                     "runtime `{}` is only a defined future managed target; `{}` is not admitted in the current wave",
    504                     target.runtime_id,
    505                     action.as_str().replace('_', " ")
    506                 )
    507             }),
    508             None,
    509         ),
    510         ManagedRuntimeGroup::BootstrapOnly => (
    511             ManagedRuntimeInspectionAvailability::Unsupported,
    512             "unsupported",
    513             detail_override.unwrap_or_else(|| {
    514                 format!(
    515                     "runtime `{}` remains bootstrap_only and direct-bindable in this wave; generic managed `{}` is not admitted",
    516                     target.runtime_id,
    517                     action.as_str().replace('_', " ")
    518                 )
    519             }),
    520             None,
    521         ),
    522         ManagedRuntimeGroup::Unknown => (
    523             ManagedRuntimeInspectionAvailability::Unconfigured,
    524             "unknown_runtime",
    525             detail_override.unwrap_or_else(|| unknown_runtime_detail(target)),
    526             None,
    527         ),
    528     };
    529 
    530     ManagedRuntimeInspection {
    531         availability,
    532         view: ManagedRuntimeActionInspection {
    533             action: action.as_str().to_owned(),
    534             runtime_id: target.runtime_id.clone(),
    535             instance_id: target.instance_id.clone(),
    536             instance_source: target.instance_source.clone(),
    537             runtime_group: target.runtime_group.as_str().to_owned(),
    538             state: state.to_owned(),
    539             source: "generic runtime-management command family".to_owned(),
    540             detail,
    541             mutates_bindings: false,
    542             next_step,
    543         },
    544     }
    545 }
    546 
    547 fn status_state(target: &ManagedRuntimeTarget) -> &'static str {
    548     match target.runtime_group {
    549         ManagedRuntimeGroup::ActiveManagedTarget => match target.instance_record.as_ref() {
    550             Some(record) => install_state_label(record.install_state),
    551             None => "not_installed",
    552         },
    553         ManagedRuntimeGroup::DefinedManagedTarget => "defined_not_active",
    554         ManagedRuntimeGroup::BootstrapOnly => "bootstrap_only",
    555         ManagedRuntimeGroup::Unknown => "unknown_runtime",
    556     }
    557 }
    558 
    559 fn status_detail(target: &ManagedRuntimeTarget) -> String {
    560     match target.runtime_group {
    561         ManagedRuntimeGroup::ActiveManagedTarget => match &target.instance_record {
    562             Some(record) => format!(
    563                 "managed runtime `{}` instance `{}` is registered with config at {}",
    564                 target.runtime_id,
    565                 target.instance_id,
    566                 record.config_path.display()
    567             ),
    568             None => format!(
    569                 "managed runtime `{}` has no registered instance `{}` in {}",
    570                 target.runtime_id,
    571                 target.instance_id,
    572                 target.registry_path.display()
    573             ),
    574         },
    575         ManagedRuntimeGroup::DefinedManagedTarget => format!(
    576             "runtime `{}` is defined in the management contract but not yet admitted as an active managed target",
    577             target.runtime_id
    578         ),
    579         ManagedRuntimeGroup::BootstrapOnly => format!(
    580             "runtime `{}` is bootstrap_only in the management contract and remains direct-bindable outside managed lifecycle in this wave",
    581             target.runtime_id
    582         ),
    583         ManagedRuntimeGroup::Unknown => unknown_runtime_detail(target),
    584     }
    585 }
    586 
    587 fn unknown_runtime_detail(target: &ManagedRuntimeTarget) -> String {
    588     format!(
    589         "runtime `{}` is not present in the current runtime-management contract",
    590         target.runtime_id
    591     )
    592 }
    593 
    594 fn infer_health_state(target: &ManagedRuntimeTarget) -> (&'static str, &'static str) {
    595     let Some(record) = &target.instance_record else {
    596         return (
    597             health_state_label(ManagedRuntimeHealthState::NotInstalled),
    598             "registry_absent",
    599         );
    600     };
    601     if record.install_state == ManagedRuntimeInstallState::Failed {
    602         return (
    603             health_state_label(ManagedRuntimeHealthState::Failed),
    604             "registry_install_state",
    605         );
    606     }
    607 
    608     if let Some(paths) = target.predicted_paths.as_ref() {
    609         if crate::process_running(paths).unwrap_or(false) {
    610             return (
    611                 health_state_label(ManagedRuntimeHealthState::Running),
    612                 "process_probe",
    613             );
    614         }
    615     } else if record.run_path.join("runtime.pid").exists() {
    616         return (
    617             health_state_label(ManagedRuntimeHealthState::Running),
    618             "pid_file_presence",
    619         );
    620     }
    621 
    622     if record.install_state == ManagedRuntimeInstallState::NotInstalled {
    623         (
    624             health_state_label(ManagedRuntimeHealthState::NotInstalled),
    625             "registry_install_state",
    626         )
    627     } else {
    628         (
    629             health_state_label(ManagedRuntimeHealthState::Stopped),
    630             "pid_file_absent",
    631         )
    632     }
    633 }
    634 
    635 fn install_state_label(state: ManagedRuntimeInstallState) -> &'static str {
    636     match state {
    637         ManagedRuntimeInstallState::NotInstalled => "not_installed",
    638         ManagedRuntimeInstallState::Installed => "installed",
    639         ManagedRuntimeInstallState::Configured => "configured",
    640         ManagedRuntimeInstallState::Failed => "failed",
    641     }
    642 }
    643 
    644 fn health_state_label(state: ManagedRuntimeHealthState) -> &'static str {
    645     match state {
    646         ManagedRuntimeHealthState::NotInstalled => "not_installed",
    647         ManagedRuntimeHealthState::Stopped => "stopped",
    648         ManagedRuntimeHealthState::Starting => "starting",
    649         ManagedRuntimeHealthState::Running => "running",
    650         ManagedRuntimeHealthState::Degraded => "degraded",
    651         ManagedRuntimeHealthState::Failed => "failed",
    652     }
    653 }
    654 
    655 fn path_present(path: Option<&PathBuf>) -> Option<bool> {
    656     path.map(|value| value.exists())
    657 }
    658 
    659 pub fn runtime_group(
    660     contract: &RadrootsRuntimeManagementContract,
    661     runtime_id: &str,
    662 ) -> ManagedRuntimeGroup {
    663     if contract
    664         .managed_runtime_targets
    665         .active
    666         .iter()
    667         .any(|entry| entry == runtime_id)
    668     {
    669         ManagedRuntimeGroup::ActiveManagedTarget
    670     } else if contract
    671         .managed_runtime_targets
    672         .defined
    673         .iter()
    674         .any(|entry| entry == runtime_id)
    675     {
    676         ManagedRuntimeGroup::DefinedManagedTarget
    677     } else if contract
    678         .managed_runtime_targets
    679         .bootstrap_only
    680         .iter()
    681         .any(|entry| entry == runtime_id)
    682     {
    683         ManagedRuntimeGroup::BootstrapOnly
    684     } else {
    685         ManagedRuntimeGroup::Unknown
    686     }
    687 }
    688 
    689 #[cfg(test)]
    690 mod tests {
    691     use std::{
    692         fs,
    693         path::{Path, PathBuf},
    694     };
    695 
    696     use radroots_runtime_paths::{
    697         RadrootsHostEnvironment, RadrootsPathOverrides, RadrootsPathProfile, RadrootsPathResolver,
    698         RadrootsPlatform, RadrootsRuntimePathSelection,
    699     };
    700     use tempfile::tempdir;
    701 
    702     use super::{
    703         ManagedRuntimeContext, ManagedRuntimeGroup, ManagedRuntimeInspectionAvailability,
    704         ManagedRuntimeLifecycleAction, active_management_mode_for_profile, health_state_label,
    705         inspect_runtime_action, inspect_runtime_config, inspect_runtime_logs,
    706         inspect_runtime_status, load_management_context, load_management_context_with_selection,
    707         resolve_runtime_target, runtime_group,
    708     };
    709     use crate::{
    710         ManagedRuntimeHealthState, ManagedRuntimeInstallState, ManagedRuntimeInstanceRecord,
    711         parse_contract_str,
    712     };
    713 
    714     const CONTRACT: &str = r#"
    715 schema = "radroots-runtime-management"
    716 schema_version = 1
    717 owner_doc = "docs/execution/rcl/radroots-modular-runtime-management-bootstrap-rcl.md"
    718 runtime_registry = "registry.toml"
    719 distribution_contract = "distribution.toml"
    720 capabilities_contract = "capabilities.toml"
    721 
    722 [defaults]
    723 instance_cardinality = "single_default_instance"
    724 managed_runtime_lookup = "shared_instance_registry"
    725 explicit_runtime_endpoint_overrides_precede_managed_instance_binding = true
    726 global_path_mutation_forbidden = true
    727 
    728 [management_clients]
    729 active = ["cli"]
    730 defined = []
    731 
    732 [managed_runtime_targets]
    733 active = ["radrootsd"]
    734 defined = ["myc"]
    735 bootstrap_only = ["hyf"]
    736 
    737 [lifecycle]
    738 actions = ["install", "start"]
    739 destructive_actions = []
    740 health_states = ["not_installed", "running"]
    741 
    742 [mode.interactive_user_managed]
    743 contract_state = "active"
    744 platforms = ["linux"]
    745 supported_profiles = ["interactive_user", "repo_local"]
    746 service_manager_integration = false
    747 	uses_absolute_binary_paths = true
    748 	default_instance_cardinality = "single_default_instance"
    749 
    750 	[mode.service_host_managed]
    751 	contract_state = "defined"
    752 	platforms = ["linux"]
    753 	supported_profiles = ["service_host"]
    754 	service_manager_integration = true
    755 	uses_absolute_binary_paths = true
    756 	default_instance_cardinality = "single_default_instance"
    757 
    758 	[paths.interactive_user_managed]
    759 	shared_namespace = "shared/runtime-manager"
    760 	instance_registry_root_class = "config"
    761 	instance_registry_rel = "shared/runtime-manager/instances.toml"
    762 	artifact_cache_root_class = "cache"
    763 artifact_cache_rel = "shared/runtime-manager/artifacts"
    764 install_root_class = "data"
    765 install_root_rel = "shared/runtime-manager/installs"
    766 state_root_class = "data"
    767 state_root_rel = "shared/runtime-manager/state"
    768 logs_root_class = "logs"
    769 logs_root_rel = "shared/runtime-manager"
    770 run_root_class = "run"
    771 run_root_rel = "shared/runtime-manager"
    772 secrets_root_class = "secrets"
    773 secrets_namespace_rel = "shared/runtime-manager"
    774 
    775 [instance_metadata]
    776 required_fields = ["runtime_id"]
    777 optional_fields = ["notes"]
    778 
    779 	[bootstrap.radrootsd]
    780 	runtime_id = "radrootsd"
    781 	management_mode = "interactive_user_managed"
    782 	default_instance_id = "local"
    783 install_strategy = "archive_unpack"
    784 config_format = "toml"
    785 requires_bootstrap_secret = true
    786 requires_config_bootstrap = true
    787 requires_signer_provider = false
    788 health_surface = "jsonrpc_status"
    789 	preferred_cli_binding = true
    790 	"#;
    791 
    792     fn resolver_for_home(home_dir: PathBuf) -> RadrootsPathResolver {
    793         RadrootsPathResolver::new(
    794             RadrootsPlatform::Linux,
    795             RadrootsHostEnvironment {
    796                 home_dir: Some(home_dir),
    797                 ..RadrootsHostEnvironment::default()
    798             },
    799         )
    800     }
    801 
    802     fn repo_local_context(root: &Path) -> ManagedRuntimeContext {
    803         let contract = parse_contract_str(CONTRACT).expect("contract");
    804         let resolver =
    805             RadrootsPathResolver::new(RadrootsPlatform::Linux, RadrootsHostEnvironment::default());
    806         load_management_context(
    807             contract,
    808             &resolver,
    809             RadrootsPathProfile::RepoLocal,
    810             &RadrootsPathOverrides::repo_local(root),
    811         )
    812         .expect("context")
    813     }
    814 
    815     fn sample_record(
    816         runtime_id: &str,
    817         instance_id: &str,
    818         install_state: ManagedRuntimeInstallState,
    819         root: &Path,
    820     ) -> ManagedRuntimeInstanceRecord {
    821         let instance_root = root.join(runtime_id).join(instance_id);
    822         ManagedRuntimeInstanceRecord {
    823             runtime_id: runtime_id.to_owned(),
    824             instance_id: instance_id.to_owned(),
    825             management_mode: "interactive_user_managed".to_owned(),
    826             install_state,
    827             binary_path: instance_root.join("bin/runtime"),
    828             config_path: instance_root.join("config/runtime.toml"),
    829             logs_path: instance_root.join("logs"),
    830             run_path: instance_root.join("run"),
    831             installed_version: "0.1.0-alpha.2".to_owned(),
    832             health_endpoint: Some("jsonrpc_status".to_owned()),
    833             secret_material_ref: None,
    834             last_started_at: None,
    835             last_stopped_at: None,
    836             notes: Some("managed test record".to_owned()),
    837         }
    838     }
    839 
    840     #[test]
    841     fn active_management_mode_matches_supported_profile() {
    842         let contract = parse_contract_str(CONTRACT).expect("contract");
    843         let mode_id =
    844             active_management_mode_for_profile(&contract, RadrootsPathProfile::InteractiveUser)
    845                 .expect("mode");
    846         assert_eq!(mode_id, "interactive_user_managed");
    847     }
    848 
    849     #[test]
    850     fn active_management_mode_rejects_profiles_without_active_mode() {
    851         let contract = parse_contract_str(CONTRACT).expect("contract");
    852         let err = active_management_mode_for_profile(&contract, RadrootsPathProfile::ServiceHost)
    853             .expect_err("service host mode is defined but inactive");
    854 
    855         assert!(err.to_string().contains("service_host"));
    856     }
    857 
    858     #[test]
    859     fn management_context_reports_selection_and_context_errors() {
    860         let dir = tempdir().expect("tempdir");
    861         let contract = parse_contract_str(CONTRACT).expect("contract");
    862         let resolver =
    863             RadrootsPathResolver::new(RadrootsPlatform::Linux, RadrootsHostEnvironment::default());
    864         let selection = RadrootsRuntimePathSelection::caller(RadrootsPathProfile::RepoLocal, None);
    865         let err = load_management_context_with_selection(contract.clone(), &resolver, &selection)
    866             .expect_err("repo local selection without root should fail");
    867         assert!(err.to_string().contains("repo_local"));
    868 
    869         let err = load_management_context(
    870             contract.clone(),
    871             &resolver,
    872             RadrootsPathProfile::ServiceHost,
    873             &RadrootsPathOverrides::default(),
    874         )
    875         .expect_err("service host mode is inactive");
    876         assert!(err.to_string().contains("service_host"));
    877 
    878         let root = dir.path().join("runtime-root");
    879         let overrides = RadrootsPathOverrides::repo_local(&root);
    880         fs::create_dir_all(root.join("config/shared/runtime-manager")).expect("registry parent");
    881         fs::create_dir(root.join("config/shared/runtime-manager/instances.toml"))
    882             .expect("registry directory");
    883         let err = load_management_context(
    884             contract,
    885             &resolver,
    886             RadrootsPathProfile::RepoLocal,
    887             &overrides,
    888         )
    889         .expect_err("directory registry path should fail");
    890         assert!(err.to_string().contains("read runtime instance registry"));
    891     }
    892 
    893     #[test]
    894     fn resolve_runtime_target_uses_bootstrap_default_instance_id() {
    895         let contract = parse_contract_str(CONTRACT).expect("contract");
    896         let resolver = resolver_for_home(PathBuf::from("/home/treesap"));
    897         let mut context = load_management_context(
    898             contract,
    899             &resolver,
    900             RadrootsPathProfile::InteractiveUser,
    901             &RadrootsPathOverrides::default(),
    902         )
    903         .expect("context");
    904         context
    905             .registry
    906             .instances
    907             .push(ManagedRuntimeInstanceRecord {
    908                 runtime_id: "radrootsd".to_owned(),
    909                 instance_id: "local".to_owned(),
    910                 management_mode: "interactive_user_managed".to_owned(),
    911                 install_state: ManagedRuntimeInstallState::Configured,
    912                 binary_path: PathBuf::from("/tmp/bin/radrootsd"),
    913                 config_path: PathBuf::from("/tmp/config.toml"),
    914                 logs_path: PathBuf::from("/tmp/logs"),
    915                 run_path: PathBuf::from("/tmp/run"),
    916                 installed_version: "0.1.0-alpha.2".to_owned(),
    917                 health_endpoint: None,
    918                 secret_material_ref: None,
    919                 last_started_at: None,
    920                 last_stopped_at: None,
    921                 notes: None,
    922             });
    923 
    924         let target = resolve_runtime_target(&context, "radrootsd", None);
    925         assert_eq!(target.instance_id, "local");
    926         assert_eq!(target.instance_source, "bootstrap_default");
    927         assert_eq!(
    928             target.runtime_group,
    929             ManagedRuntimeGroup::ActiveManagedTarget
    930         );
    931         assert!(target.predicted_paths.is_some());
    932     }
    933 
    934     #[test]
    935     fn load_context_with_selection_uses_caller_path_selection() {
    936         let dir = tempdir().expect("tempdir");
    937         let root = dir.path().join("runtime-root");
    938         let contract = parse_contract_str(CONTRACT).expect("contract");
    939         let resolver =
    940             RadrootsPathResolver::new(RadrootsPlatform::Linux, RadrootsHostEnvironment::default());
    941         let selection = RadrootsRuntimePathSelection::caller(
    942             RadrootsPathProfile::RepoLocal,
    943             Some(root.clone()),
    944         );
    945 
    946         let context = load_management_context_with_selection(contract, &resolver, &selection)
    947             .expect("selection context");
    948 
    949         assert_eq!(
    950             context.shared_paths.instance_registry_path,
    951             root.join("config/shared/runtime-manager/instances.toml")
    952         );
    953         assert!(context.registry.instances.is_empty());
    954     }
    955 
    956     #[test]
    957     fn runtime_groups_and_action_labels_cover_declared_surfaces() {
    958         let contract = parse_contract_str(CONTRACT).expect("contract");
    959         assert_eq!(
    960             runtime_group(&contract, "radrootsd"),
    961             ManagedRuntimeGroup::ActiveManagedTarget
    962         );
    963         assert_eq!(
    964             runtime_group(&contract, "myc"),
    965             ManagedRuntimeGroup::DefinedManagedTarget
    966         );
    967         assert_eq!(
    968             runtime_group(&contract, "hyf"),
    969             ManagedRuntimeGroup::BootstrapOnly
    970         );
    971         assert_eq!(
    972             runtime_group(&contract, "unknown"),
    973             ManagedRuntimeGroup::Unknown
    974         );
    975 
    976         assert_eq!(
    977             ManagedRuntimeGroup::ActiveManagedTarget.as_str(),
    978             "active_managed_target"
    979         );
    980         assert_eq!(
    981             ManagedRuntimeGroup::DefinedManagedTarget.posture(),
    982             "defined_future_target"
    983         );
    984         assert_eq!(
    985             ManagedRuntimeGroup::BootstrapOnly.posture(),
    986             "bootstrap_only_direct_binding"
    987         );
    988         assert_eq!(ManagedRuntimeGroup::Unknown.as_str(), "unknown");
    989 
    990         let actions = [
    991             (ManagedRuntimeLifecycleAction::Install, "install"),
    992             (ManagedRuntimeLifecycleAction::Uninstall, "uninstall"),
    993             (ManagedRuntimeLifecycleAction::Start, "start"),
    994             (ManagedRuntimeLifecycleAction::Stop, "stop"),
    995             (ManagedRuntimeLifecycleAction::Restart, "restart"),
    996             (ManagedRuntimeLifecycleAction::ConfigSet, "config_set"),
    997         ];
    998         for (action, expected) in actions {
    999             assert_eq!(action.as_str(), expected);
   1000         }
   1001     }
   1002 
   1003     #[test]
   1004     fn resolve_runtime_target_covers_requested_and_non_active_sources() {
   1005         let dir = tempdir().expect("tempdir");
   1006         let mut context = repo_local_context(dir.path());
   1007         context.registry.instances.push(sample_record(
   1008             "myc",
   1009             "default",
   1010             ManagedRuntimeInstallState::Configured,
   1011             dir.path(),
   1012         ));
   1013 
   1014         let requested = resolve_runtime_target(&context, "radrootsd", Some("manual"));
   1015         assert_eq!(requested.instance_id, "manual");
   1016         assert_eq!(requested.instance_source, "command_arg");
   1017         assert_eq!(
   1018             requested.runtime_group,
   1019             ManagedRuntimeGroup::ActiveManagedTarget
   1020         );
   1021         assert!(requested.predicted_paths.is_some());
   1022 
   1023         let defined = resolve_runtime_target(&context, "myc", None);
   1024         assert_eq!(defined.instance_id, "default");
   1025         assert_eq!(defined.instance_source, "implicit_default");
   1026         assert_eq!(
   1027             defined.runtime_group,
   1028             ManagedRuntimeGroup::DefinedManagedTarget
   1029         );
   1030         assert!(defined.predicted_paths.is_none());
   1031         assert!(defined.instance_record.is_some());
   1032 
   1033         let bootstrap = resolve_runtime_target(&context, "hyf", None);
   1034         assert_eq!(bootstrap.instance_source, "implicit_default");
   1035         assert_eq!(bootstrap.runtime_group, ManagedRuntimeGroup::BootstrapOnly);
   1036         assert!(bootstrap.management_mode.is_none());
   1037 
   1038         let unknown = resolve_runtime_target(&context, "unknown", Some("manual"));
   1039         assert_eq!(unknown.instance_id, "manual");
   1040         assert_eq!(unknown.instance_source, "command_arg");
   1041         assert_eq!(unknown.runtime_group, ManagedRuntimeGroup::Unknown);
   1042         assert!(unknown.predicted_paths.is_none());
   1043     }
   1044 
   1045     #[test]
   1046     fn status_inspection_covers_install_and_health_states() {
   1047         let dir = tempdir().expect("tempdir");
   1048         let context = repo_local_context(dir.path());
   1049         let active_missing = resolve_runtime_target(&context, "radrootsd", None);
   1050         let status =
   1051             inspect_runtime_status(&active_missing, &["install".to_owned(), "start".to_owned()]);
   1052         assert_eq!(
   1053             status.availability,
   1054             ManagedRuntimeInspectionAvailability::Success
   1055         );
   1056         assert_eq!(status.view.state, "not_installed");
   1057         assert_eq!(status.view.health_state, "not_installed");
   1058         assert_eq!(status.view.health_source, "registry_absent");
   1059         assert_eq!(status.view.lifecycle_actions, ["install", "start"]);
   1060 
   1061         let mut context = repo_local_context(dir.path());
   1062         context.registry.instances.push(sample_record(
   1063             "radrootsd",
   1064             "local",
   1065             ManagedRuntimeInstallState::Configured,
   1066             dir.path(),
   1067         ));
   1068         let active_configured = resolve_runtime_target(&context, "radrootsd", None);
   1069         let configured_status = inspect_runtime_status(&active_configured, &[]);
   1070         assert_eq!(configured_status.view.state, "configured");
   1071         assert_eq!(configured_status.view.health_state, "stopped");
   1072         assert_eq!(configured_status.view.health_source, "pid_file_absent");
   1073         assert_eq!(configured_status.view.install_state, "configured");
   1074 
   1075         let predicted = active_configured
   1076             .predicted_paths
   1077             .as_ref()
   1078             .expect("predicted active paths");
   1079         fs::create_dir_all(&predicted.run_dir).expect("run dir");
   1080         fs::write(&predicted.pid_file_path, std::process::id().to_string()).expect("pid");
   1081         let running_status = inspect_runtime_status(&active_configured, &[]);
   1082         assert_eq!(running_status.view.health_state, "running");
   1083         assert_eq!(running_status.view.health_source, "process_probe");
   1084         fs::remove_file(&predicted.pid_file_path).expect("remove pid");
   1085 
   1086         let mut context = repo_local_context(dir.path());
   1087         context.registry.instances.push(sample_record(
   1088             "radrootsd",
   1089             "local",
   1090             ManagedRuntimeInstallState::Failed,
   1091             dir.path(),
   1092         ));
   1093         let active_failed = resolve_runtime_target(&context, "radrootsd", None);
   1094         let failed_status = inspect_runtime_status(&active_failed, &[]);
   1095         assert_eq!(failed_status.view.state, "failed");
   1096         assert_eq!(failed_status.view.health_state, "failed");
   1097         assert_eq!(failed_status.view.health_source, "registry_install_state");
   1098 
   1099         let mut context = repo_local_context(dir.path());
   1100         context.registry.instances.push(sample_record(
   1101             "radrootsd",
   1102             "local",
   1103             ManagedRuntimeInstallState::NotInstalled,
   1104             dir.path(),
   1105         ));
   1106         let active_not_installed = resolve_runtime_target(&context, "radrootsd", None);
   1107         let not_installed_status = inspect_runtime_status(&active_not_installed, &[]);
   1108         assert_eq!(not_installed_status.view.health_state, "not_installed");
   1109         assert_eq!(
   1110             not_installed_status.view.health_source,
   1111             "registry_install_state"
   1112         );
   1113 
   1114         let mut context = repo_local_context(dir.path());
   1115         let defined_record = sample_record(
   1116             "myc",
   1117             "default",
   1118             ManagedRuntimeInstallState::Installed,
   1119             dir.path(),
   1120         );
   1121         fs::create_dir_all(&defined_record.run_path).expect("run dir");
   1122         fs::write(defined_record.run_path.join("runtime.pid"), "42").expect("pid");
   1123         context.registry.instances.push(defined_record);
   1124         let defined = resolve_runtime_target(&context, "myc", None);
   1125         let defined_status = inspect_runtime_status(&defined, &["install".to_owned()]);
   1126         assert_eq!(defined_status.view.state, "defined_not_active");
   1127         assert_eq!(defined_status.view.health_state, "running");
   1128         assert_eq!(defined_status.view.health_source, "pid_file_presence");
   1129         assert!(defined_status.view.lifecycle_actions.is_empty());
   1130 
   1131         let no_pid_dir = tempdir().expect("no-pid tempdir");
   1132         let mut context = repo_local_context(no_pid_dir.path());
   1133         context.registry.instances.push(sample_record(
   1134             "myc",
   1135             "default",
   1136             ManagedRuntimeInstallState::Installed,
   1137             no_pid_dir.path(),
   1138         ));
   1139         let defined_without_pid = resolve_runtime_target(&context, "myc", None);
   1140         let defined_without_pid_status = inspect_runtime_status(&defined_without_pid, &[]);
   1141         assert_eq!(defined_without_pid_status.view.health_state, "stopped");
   1142         assert_eq!(
   1143             defined_without_pid_status.view.health_source,
   1144             "pid_file_absent"
   1145         );
   1146 
   1147         let bootstrap = resolve_runtime_target(&context, "hyf", None);
   1148         let bootstrap_status = inspect_runtime_status(&bootstrap, &[]);
   1149         assert_eq!(bootstrap_status.view.state, "bootstrap_only");
   1150         assert_eq!(
   1151             bootstrap_status.view.management_posture,
   1152             "bootstrap_only_direct_binding"
   1153         );
   1154         assert_eq!(
   1155             health_state_label(ManagedRuntimeHealthState::Starting),
   1156             "starting"
   1157         );
   1158         assert_eq!(
   1159             health_state_label(ManagedRuntimeHealthState::Degraded),
   1160             "degraded"
   1161         );
   1162 
   1163         let unknown = resolve_runtime_target(&context, "unknown", None);
   1164         let unknown_status = inspect_runtime_status(&unknown, &[]);
   1165         assert_eq!(
   1166             unknown_status.availability,
   1167             ManagedRuntimeInspectionAvailability::Unconfigured
   1168         );
   1169         assert_eq!(unknown_status.view.state, "unknown_runtime");
   1170     }
   1171 
   1172     #[test]
   1173     fn logs_and_config_inspections_cover_availability_paths() {
   1174         let dir = tempdir().expect("tempdir");
   1175         let mut context = repo_local_context(dir.path());
   1176         context.registry.instances.push(sample_record(
   1177             "radrootsd",
   1178             "local",
   1179             ManagedRuntimeInstallState::Configured,
   1180             dir.path(),
   1181         ));
   1182         let active = resolve_runtime_target(&context, "radrootsd", None);
   1183         let predicted = active.predicted_paths.as_ref().expect("predicted paths");
   1184         fs::create_dir_all(&predicted.logs_dir).expect("predicted logs dir");
   1185         fs::write(&predicted.stdout_log_path, "stdout").expect("stdout");
   1186         fs::write(&predicted.stderr_log_path, "stderr").expect("stderr");
   1187         let config_path = active
   1188             .instance_record
   1189             .as_ref()
   1190             .expect("record")
   1191             .config_path
   1192             .clone();
   1193         fs::create_dir_all(config_path.parent().expect("config parent")).expect("config parent");
   1194         fs::write(&config_path, "listen = true").expect("config");
   1195 
   1196         let active_logs = inspect_runtime_logs(&active);
   1197         assert_eq!(
   1198             active_logs.availability,
   1199             ManagedRuntimeInspectionAvailability::Success
   1200         );
   1201         assert_eq!(active_logs.view.state, "ready");
   1202         assert!(active_logs.view.stdout_log_present);
   1203         assert!(active_logs.view.stderr_log_present);
   1204         assert!(active_logs.view.stdout_log_path.is_some());
   1205 
   1206         let active_config = inspect_runtime_config(&active);
   1207         assert_eq!(active_config.view.state, "ready");
   1208         assert!(active_config.view.config_present);
   1209         assert_eq!(active_config.view.config_format.as_deref(), Some("toml"));
   1210         assert_eq!(active_config.view.requires_bootstrap_secret, Some(true));
   1211         assert_eq!(active_config.view.requires_config_bootstrap, Some(true));
   1212         assert_eq!(active_config.view.requires_signer_provider, Some(false));
   1213 
   1214         let empty_dir = tempdir().expect("empty tempdir");
   1215         let empty_context = repo_local_context(empty_dir.path());
   1216         let active_missing = resolve_runtime_target(&empty_context, "radrootsd", None);
   1217         let missing_logs = inspect_runtime_logs(&active_missing);
   1218         assert_eq!(missing_logs.view.state, "ready");
   1219         assert!(!missing_logs.view.stdout_log_present);
   1220         assert!(!missing_logs.view.stderr_log_present);
   1221         let missing_config = inspect_runtime_config(&active_missing);
   1222         assert_eq!(missing_config.view.state, "not_installed");
   1223         assert!(!missing_config.view.config_present);
   1224 
   1225         let mut context = repo_local_context(dir.path());
   1226         let defined_record = sample_record(
   1227             "myc",
   1228             "default",
   1229             ManagedRuntimeInstallState::Configured,
   1230             dir.path(),
   1231         );
   1232         fs::create_dir_all(&defined_record.logs_path).expect("defined logs dir");
   1233         fs::write(defined_record.logs_path.join("stdout.log"), "stdout").expect("defined stdout");
   1234         fs::create_dir_all(defined_record.config_path.parent().expect("config parent"))
   1235             .expect("defined config parent");
   1236         fs::write(&defined_record.config_path, "enabled = true").expect("defined config");
   1237         context.registry.instances.push(defined_record);
   1238         let defined = resolve_runtime_target(&context, "myc", None);
   1239         let defined_logs = inspect_runtime_logs(&defined);
   1240         assert_eq!(
   1241             defined_logs.availability,
   1242             ManagedRuntimeInspectionAvailability::Success
   1243         );
   1244         assert_eq!(defined_logs.view.state, "ready");
   1245         assert!(defined_logs.view.stdout_log_present);
   1246         assert!(!defined_logs.view.stderr_log_present);
   1247         assert!(defined_logs.view.stdout_log_path.is_none());
   1248         let defined_config = inspect_runtime_config(&defined);
   1249         assert_eq!(
   1250             defined_config.availability,
   1251             ManagedRuntimeInspectionAvailability::Success
   1252         );
   1253         assert_eq!(defined_config.view.state, "ready");
   1254         assert!(defined_config.view.config_present);
   1255 
   1256         let defined_without_record = resolve_runtime_target(&empty_context, "myc", None);
   1257         assert_eq!(
   1258             inspect_runtime_logs(&defined_without_record).availability,
   1259             ManagedRuntimeInspectionAvailability::Unsupported
   1260         );
   1261         assert_eq!(
   1262             inspect_runtime_config(&defined_without_record).availability,
   1263             ManagedRuntimeInspectionAvailability::Unsupported
   1264         );
   1265 
   1266         let bootstrap = resolve_runtime_target(&empty_context, "hyf", None);
   1267         assert_eq!(
   1268             inspect_runtime_logs(&bootstrap).availability,
   1269             ManagedRuntimeInspectionAvailability::Unsupported
   1270         );
   1271         assert_eq!(
   1272             inspect_runtime_config(&bootstrap).availability,
   1273             ManagedRuntimeInspectionAvailability::Unsupported
   1274         );
   1275 
   1276         let unknown = resolve_runtime_target(&empty_context, "unknown", None);
   1277         assert_eq!(
   1278             inspect_runtime_logs(&unknown).availability,
   1279             ManagedRuntimeInspectionAvailability::Unconfigured
   1280         );
   1281         assert_eq!(
   1282             inspect_runtime_config(&unknown).availability,
   1283             ManagedRuntimeInspectionAvailability::Unconfigured
   1284         );
   1285     }
   1286 
   1287     #[test]
   1288     fn action_inspection_covers_all_group_postures() {
   1289         let dir = tempdir().expect("tempdir");
   1290         let context = repo_local_context(dir.path());
   1291         let active = resolve_runtime_target(&context, "radrootsd", None);
   1292         let defined = resolve_runtime_target(&context, "myc", None);
   1293         let bootstrap = resolve_runtime_target(&context, "hyf", None);
   1294         let unknown = resolve_runtime_target(&context, "unknown", None);
   1295 
   1296         let active_install =
   1297             inspect_runtime_action(&active, ManagedRuntimeLifecycleAction::Install, None);
   1298         assert_eq!(
   1299             active_install.availability,
   1300             ManagedRuntimeInspectionAvailability::Unsupported
   1301         );
   1302         assert_eq!(active_install.view.state, "deferred");
   1303         assert!(active_install.view.detail.contains("runtime install"));
   1304         assert!(!active_install.view.mutates_bindings);
   1305         assert!(active_install.view.next_step.is_none());
   1306 
   1307         let overridden = inspect_runtime_action(
   1308             &active,
   1309             ManagedRuntimeLifecycleAction::ConfigSet,
   1310             Some("custom detail".to_owned()),
   1311         );
   1312         assert_eq!(overridden.view.action, "config_set");
   1313         assert_eq!(overridden.view.detail, "custom detail");
   1314 
   1315         let defined_start =
   1316             inspect_runtime_action(&defined, ManagedRuntimeLifecycleAction::Start, None);
   1317         assert_eq!(defined_start.view.state, "unsupported");
   1318         assert!(
   1319             defined_start
   1320                 .view
   1321                 .detail
   1322                 .contains("defined future managed target")
   1323         );
   1324 
   1325         let bootstrap_stop =
   1326             inspect_runtime_action(&bootstrap, ManagedRuntimeLifecycleAction::Stop, None);
   1327         assert_eq!(bootstrap_stop.view.state, "unsupported");
   1328         assert!(bootstrap_stop.view.detail.contains("bootstrap_only"));
   1329 
   1330         let unknown_restart =
   1331             inspect_runtime_action(&unknown, ManagedRuntimeLifecycleAction::Restart, None);
   1332         assert_eq!(
   1333             unknown_restart.availability,
   1334             ManagedRuntimeInspectionAvailability::Unconfigured
   1335         );
   1336         assert_eq!(unknown_restart.view.state, "unknown_runtime");
   1337     }
   1338 }