arcature-cli 2026.2.0

Developer lifecycle CLI for Arcature applications.
Documentation
//! Parse the embedded Certified Stack Contract.
//!
//! The contract (`contract.toml`) is the single machine-readable source of
//! truth for the cross-ecosystem stack Arcature certifies. It is embedded in
//! the CLI at build time via `include_str!` so `arc doctor` can report against
//! it without network access. Certification is information and testing policy,
//! not ecosystem lock-in: Cargo overrides are not prevented; an override
//! outside this matrix is reported as `UNVERIFIED OVERRIDE`.

use std::collections::BTreeMap;

use serde::Deserialize;

/// The machine-readable Certified Stack Contract.
#[derive(Debug, Deserialize)]
pub(crate) struct Contract {
    pub(crate) snapshot: Snapshot,
    #[serde(default)]
    pub(crate) components: BTreeMap<String, Component>,
    #[serde(default)]
    pub(crate) services: BTreeMap<String, Service>,
    #[serde(default)]
    pub(crate) incompatible: Incompatible,
}

#[derive(Debug, Deserialize)]
pub(crate) struct Snapshot {
    pub(crate) date: String,
    pub(crate) arcature_version: String,
    /// The certified `arcature-build` crate version written into the
    /// generated `Cargo.toml`. `arcature-build` is its own release unit
    /// (ADR-0006 §6), distinct from `arcature`'s — the two may sit in
    /// different YBF generations in the same Platform set, so the fallback
    /// carries its own version rather than reusing `arcature`'s.
    pub(crate) arcature_build_version: String,
    /// The certified compatible version of the first-party `@arcature/client`
    /// npm package written into generated `frontend/package.json`. The real
    /// shipping dependency — never a monorepo `file:` path (ADR-0006 §6). This
    /// is a placeholder `0.0.0` while the package is unpublished; the
    /// separately-secured npm publication lifecycle replaces it with a real
    /// semver, and the snapshot is updated to match.
    pub(crate) arcature_client_version: String,
    #[allow(dead_code)]
    pub(crate) rust_toolchain: String,
}

#[derive(Debug, Deserialize)]
pub(crate) struct Component {
    pub(crate) version: String,
    #[allow(dead_code)]
    pub(crate) status: String,
    #[allow(dead_code)]
    pub(crate) role: String,
}

#[derive(Debug, Deserialize)]
pub(crate) struct Service {
    pub(crate) certified_versions: Vec<String>,
    #[allow(dead_code)]
    pub(crate) status: String,
    pub(crate) role: String,
}

#[derive(Debug, Default, Deserialize)]
pub(crate) struct Incompatible {
    #[serde(default)]
    pub(crate) entries: Vec<IncompatibleEntry>,
}

#[derive(Debug, Deserialize)]
pub(crate) struct IncompatibleEntry {
    pub(crate) component: String,
    pub(crate) version: String,
    pub(crate) reason: String,
}

/// Load the embedded contract. Panics only if the embedded TOML is malformed —
/// a compile-time asset, not runtime input.
pub(crate) fn load() -> Contract {
    toml::from_str(include_str!("contract.toml"))
        .expect("embedded certified-stack contract is valid TOML")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn contract_parses_and_lists_all_components() {
        let contract = load();
        assert_eq!(contract.snapshot.arcature_version, "2026.2.1");
        assert_eq!(
            contract.snapshot.arcature_build_version, "2026.1.0",
            "contract snapshot must declare an arcature-build version (its own release unit)"
        );
        assert!(
            !contract.snapshot.arcature_client_version.is_empty(),
            "contract snapshot must declare an @arcature/client version"
        );
        // Foundation stack from the H4 contract specification.
        for name in [
            "rust",
            "tokio",
            "axum",
            "tower",
            "tower_http",
            "hyper",
            "rustls",
            "aws_lc_rs",
            "serde",
            "serde_json",
            "sqlx",
            "sea_orm",
            "sea_orm_migration",
            "redis",
            "opendal",
            "reqwest",
            "lettre",
            "argon2",
            "tower_sessions",
            "tracing",
            "inertia",
            "node",
            "pnpm",
            "vite",
            "typescript",
            "react",
            "react_dom",
            "vue",
            "inertia_react",
            "inertia_vue",
        ] {
            assert!(
                contract.components.contains_key(name),
                "contract missing component `{name}`"
            );
        }
        // Services tested in CI.
        assert!(contract.services.contains_key("postgresql"));
        assert!(contract.services.contains_key("valkey"));
        assert_eq!(
            contract.services["postgresql"].certified_versions,
            vec!["15.13", "16.9", "17.5", "18.4"]
        );
    }

    #[test]
    fn no_component_claims_latest() {
        let contract = load();
        for (name, component) in &contract.components {
            assert!(
                !component.version.eq_ignore_ascii_case("latest"),
                "component `{name}` claims `latest` — certified versions must be exact"
            );
        }
    }
}