crtx 0.1.0

CLI for the Cortex supervisory memory substrate.
//! CLI exit-code table.
//!
//! Every command path returns an [`Exit`]. `main()` casts the variant to
//! `i32` and hands it to [`std::process::exit`]. The table is deliberately
//! frozen at lane 1.C and referenced from BUILD_SPEC §11 — operators and
//! shell wrappers depend on the numeric values not drifting.
//!
//! ## The table
//!
//! | Code | Variant              | Meaning                                                        |
//! |------|----------------------|----------------------------------------------------------------|
//! | 0    | `Ok`                 | Command completed successfully.                                |
//! | 2    | `Usage`              | Bad invocation (`clap` parse failure, unknown flag).           |
//! | 3    | `IntegrityFailure`   | Per-row audit failures (orphan, ordinal gap, …).               |
//! | 4    | `SchemaMismatch`     | On-disk schema version differs from this binary.               |
//! | 5    | `QuarantinedInput`   | Fixture integrity / quarantined input rejected.                |
//! | 6    | `ChainCorruption`    | Hash-chain decode / framing failure (file is structurally bad).|
//! | 7    | `PreconditionUnmet`  | Operator-visible precondition (perms, paths) not satisfied.    |
//! | 64   | `Internal`           | Unrecoverable internal error.                                  |
//!
//! `2` is reserved for `clap`-style usage failures so that `cortex --bogus`
//! lines up with conventional Unix tools (`/usr/bin/grep --bogus` exits 2).

/// CLI exit code. Numeric values are part of the public contract.
///
/// The enum is exhaustive even though some variants are not yet wired by
/// any subcommand — they are part of the documented exit-code table that
/// shell wrappers and operators rely on. `#[allow(dead_code)]` on the
/// individual unwired variants prevents `clippy -D warnings` from flagging
/// them while still letting clippy catch genuine dead-code in command
/// paths.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum Exit {
    /// Command completed successfully.
    Ok = 0,
    /// Invocation was malformed (unknown flag, missing required arg).
    Usage = 2,
    /// Per-row audit failure (orphan link, ordinal gap, payload mismatch).
    IntegrityFailure = 3,
    /// On-disk schema version is incompatible with this binary.
    ///
    /// Reserved for the schema-version check that lands with cortex-store
    /// migrations. Documented in the public exit table so wrappers can rely
    /// on the value.
    #[allow(dead_code)]
    SchemaMismatch = 4,
    /// Quarantined input — fixture / signed manifest integrity check failed.
    QuarantinedInput = 5,
    /// Hash-chain itself is structurally corrupt (decode / framing broken).
    ChainCorruption = 6,
    /// Operator precondition (file permission, expected path) not met.
    PreconditionUnmet = 7,
    /// Unrecoverable internal error.
    Internal = 64,
}

impl Exit {
    /// Numeric value handed to [`std::process::exit`].
    #[must_use]
    pub const fn code(self) -> i32 {
        self as i32
    }
}

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

    /// The numeric values are part of the public contract. If this snapshot
    /// changes, BUILD_SPEC §11 and any shell wrappers calling the CLI need to
    /// be updated in lockstep — and an ADR must justify the bump.
    #[test]
    fn exit_codes_are_frozen() {
        assert_eq!(Exit::Ok.code(), 0);
        assert_eq!(Exit::Usage.code(), 2);
        assert_eq!(Exit::IntegrityFailure.code(), 3);
        assert_eq!(Exit::SchemaMismatch.code(), 4);
        assert_eq!(Exit::QuarantinedInput.code(), 5);
        assert_eq!(Exit::ChainCorruption.code(), 6);
        assert_eq!(Exit::PreconditionUnmet.code(), 7);
        assert_eq!(Exit::Internal.code(), 64);
    }
}