Gunnar

mikronova/modgunn

src/about.rs

//! **About surface** — modgunn's identity card in the constellation-wide COMMON
//! FRAMEWORK (the JetBrains-style shared shell every suite app carries: an *About*,
//! a *Tools* menu, and a shared *lifecycle*). This is the always-compiled half:
//! pure identity data (name / version / role / tagline / engines), so even the lean
//! `scan` build — and holger / static-holger, which link modgunn's lib — can answer
//! `modgunn about` with **no** heavy dependency (the shared model is serde-only).
//!
//! **Shared-component note (L5 — DRY, RECONCILED).** modgunn used to carry its OWN
//! `struct Identity` here; that stale "no shared About component yet" note is gone.
//! The identity **model** now lives ONCE in the [`nornir_appshell::Identity`] edda
//! leaf (name / display_name / version / tagline / subtitle / logo / repository /
//! links / framework / engines + a `state_json` oracle), and modgunn CONSUMES it:
//! [`identity`] builds a `nornir_appshell::Identity`, so the shared **`facett-about`**
//! egui card — which renders exactly that model — paints modgunn's About unchanged
//! (korp / holger feed the SAME shape). modgunn keeps only its identity DATA here
//! and consumes the shared Tools (jera) + lifecycle (skidbladnir) surfaces too (see
//! [`crate::tools`] / [`crate::service`]).

use nornir_appshell::Identity;

/// modgunn's identity card as the shared [`nornir_appshell::Identity`] — the data an
/// About surface (CLI `modgunn about`, or the shared `facett-about` egui card)
/// renders. `version` tracks the crate version automatically.
///
/// `subtitle` carries modgunn's one-line **role** (the gatekeeper); `engines` lists
/// the two sharply-split engines it ships; `framework` names the shared components it
/// is built on (the reuse story). No logo bytes are embedded — the `facett-about`
/// renderer falls back to the text identity, never a panic.
pub fn identity() -> Identity {
    Identity {
        // modgunn's one-line role in the constellation (was the bespoke `role` field).
        subtitle: "the nordisk security + SCIP world-delta gatekeeper (Gjallarbrú)".to_string(),
        repository: "ssh://git@git.gunnar.rs:2222/mikronova/modgunn.git".to_string(),
        // The two sharply-split engines modgunn ships.
        engines: vec![
            "Engine A — artifact security VERDICT (scan: multi-ecosystem CVE / license / provenance)".to_string(),
            "Engine B — SCIP whole-world dep-tree BEHAVIOUR-DELTA (scip: tripwires / graph-algs / anomaly)".to_string(),
        ],
        // The shared framework modgunn is BUILT ON (LAW #5 reuse, shown on the card).
        framework: vec![
            "nornir-appshell — the shared About / Tools app-shell models".to_string(),
            "jera — the shared job ledger + no-shell container runtime".to_string(),
            "skidbladnir — the shared service lifecycle (one systemd renderer)".to_string(),
        ],
        ..Identity::new(
            "modgunn",
            "Móðguðr",
            // `<semver> (<git-hash>)` — the SAME string `modgunn --version` prints and
            // the ONE source of truth for which build this is (build-version-githash-law).
            crate::version_with_hash(),
            "Guards the bridge: scrutinizes what passes (CVE / license / provenance \
             verdict) and tracks every change in the dependency world.",
        )
    }
}

/// The About surface's headless oracle: the identity as JSON — what `modgunn about
/// --json` prints and a robot asserts (name · version · role · engines present).
/// A thin passthrough to [`Identity::state_json`] (the shared oracle every suite
/// About surface asserts); `open` is `true` for the CLI (the card is "showing").
pub fn state_json() -> serde_json::Value {
    identity().state_json(true)
}

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

    /// **RED-when-broken: the About surface carries modgunn's real identity, on the
    /// SHARED model.** The identity names modgunn / Móðguðr, tracks the crate version
    /// (never blank), states the gatekeeper role (as `subtitle`), and lists BOTH
    /// engines (A verdict + B world-delta). If the About lost its identity (blank
    /// name/version, or an engine dropped), this is red — the surface a `modgunn
    /// about` robot asserts, and the shape `facett-about` renders.
    #[test]
    fn identity_carries_name_version_and_both_engines() {
        let id = identity();
        assert_eq!(id.name, "modgunn");
        assert_eq!(id.display_name, "Móðguðr");
        assert!(
            !id.version.is_empty(),
            "version is stamped from CARGO_PKG_VERSION"
        );
        // The version carries BOTH the semver and the git-hash (build-version-githash-law),
        // in the suite shape `<semver> (<hash>)` — the same string `modgunn --version` prints.
        assert!(
            id.version.starts_with(env!("CARGO_PKG_VERSION")),
            "version starts with the crate semver: {}",
            id.version
        );
        assert!(
            id.version.contains(env!("MODGUNN_GIT_HASH")),
            "version carries the git-hash stamp: {}",
            id.version
        );
        assert!(
            id.subtitle.contains("gatekeeper"),
            "the role names the gatekeeper: {}",
            id.subtitle
        );
        assert_eq!(id.engines.len(), 2, "both Engine A + Engine B are listed");
        assert!(id.engines[0].contains("Engine A"));
        assert!(id.engines[1].contains("Engine B"));
        // The reuse story is shown: the shared framework modgunn is built on.
        assert!(
            !id.framework.is_empty(),
            "About names the shared framework it consumes"
        );

        crate::functional_status(
            "modgunn/about",
            "identity_name_version_engines",
            id.name == "modgunn" && !id.version.is_empty() && id.engines.len() == 2,
            "the About surface carries modgunn's identity on nornir_appshell::Identity: name + version + both engines",
        );
    }

    /// The `state_json` oracle mirrors the identity — the exact surface `modgunn about`
    /// dumps and an out-of-crate CLI robot asserts. It is the shared
    /// [`Identity::state_json`], so `wordmark` (korp/holger key) and `display_name`
    /// (modgunn key) both resolve to Móðguðr.
    #[test]
    fn state_json_mirrors_identity() {
        let js = state_json();
        assert_eq!(js["name"], "modgunn");
        let jver = js["version"].as_str().unwrap_or_default();
        assert!(
            jver.starts_with(env!("CARGO_PKG_VERSION")) && jver.contains(env!("MODGUNN_GIT_HASH")),
            "state_json version carries semver + git-hash: {jver}"
        );
        assert_eq!(js["engines"].as_array().unwrap().len(), 2);
        assert_eq!(js["display_name"], "Móðguðr");
        assert_eq!(
            js["wordmark"], "Móðguðr",
            "the shared oracle carries both keys"
        );
        assert_eq!(
            js["open"],
            serde_json::json!(true),
            "the CLI card is showing"
        );
    }

    /// The identity round-trips through serde as the shared model — the oracle a CLI
    /// dumps can be read back into an owned `nornir_appshell::Identity` out of process.
    #[test]
    fn identity_round_trips_as_shared_model() {
        let id = identity();
        let s = serde_json::to_string(&id).unwrap();
        let back: Identity = serde_json::from_str(&s).unwrap();
        assert_eq!(id, back, "modgunn's Identity survives a serde round-trip");
    }
}