klieo-ops 0.2.0

Operational layer above klieo-core: supervisor, governor, gates, escalation, worklog, handoff.
Documentation
//! `OpsRuntime` builder.

use crate::clock::{SharedClock, SystemClock};
use crate::error::BuildError;
use crate::redactor::{DefaultRedactor, SharedRedactor};
use crate::runtime::supervised::SupervisedAgent;
use crate::tenant::TenantResolver;
use klieo_core::Agent;
use std::sync::Arc;

/// Entry point for wiring the ops layer around a user-built agent.
pub struct OpsRuntime;

impl OpsRuntime {
    /// Start a builder chain.
    #[must_use]
    pub fn builder() -> OpsRuntimeBuilder {
        OpsRuntimeBuilder::default()
    }
}

/// Builder accumulator.
pub struct OpsRuntimeBuilder {
    tenant_resolver: Option<Arc<dyn TenantResolver>>,
    redactor: SharedRedactor,
    clock: SharedClock,
}

// Hand-written because DefaultRedactor and SystemClock construct non-trivial
// Arc values that cannot be expressed with #[derive(Default)].
impl Default for OpsRuntimeBuilder {
    fn default() -> Self {
        Self {
            tenant_resolver: None,
            redactor: Arc::new(DefaultRedactor::new()),
            clock: Arc::new(SystemClock::default()),
        }
    }
}

impl OpsRuntimeBuilder {
    /// Set the tenant resolver. Required before calling [`spawn`](Self::spawn).
    #[must_use]
    pub fn tenant_resolver(mut self, r: Arc<dyn TenantResolver>) -> Self {
        self.tenant_resolver = Some(r);
        self
    }

    /// Override the default redactor.
    #[must_use]
    pub fn redactor(mut self, r: SharedRedactor) -> Self {
        self.redactor = r;
        self
    }

    /// Override the default clock.
    #[must_use]
    pub fn clock(mut self, c: SharedClock) -> Self {
        self.clock = c;
        self
    }

    /// Validate configuration and wrap the agent.
    ///
    /// Returns [`BuildError::MissingDependency`] when `tenant_resolver` was
    /// not supplied. The `redactor` and `clock` fields default safely and are
    /// captured for use once supervisor / governor / gates land in Phase B.
    pub fn spawn<A>(self, agent: A) -> Result<SupervisedAgent<A>, BuildError>
    where
        A: Agent,
    {
        let tenant_resolver =
            self.tenant_resolver
                .ok_or_else(|| BuildError::MissingDependency {
                    needed_by: "OpsRuntime".into(),
                    missing: "tenant_resolver".into(),
                })?;
        // redactor and clock are captured but currently unused — load-bearing
        // once supervisor / governor / gates land in Phase B.
        let _ = (&self.redactor, &self.clock);
        Ok(SupervisedAgent {
            inner: agent,
            tenant_resolver,
        })
    }
}