minerva 0.2.0

Causal ordering for distributed systems
use super::Kairos;
use thiserror::Error;

/// Version tag for the canonical `Kairos` wire encoding (PRD 0002). One leading
/// byte: the 128-bit payload is fully consumed by the field layout, so the version
/// cannot ride in-band.
const KAIROS_WIRE_V1: u8 = 0x01;

/// Length of a version-1 `Kairos` frame: one version byte plus the 16-byte payload.
///
/// Public so a caller composing the `metis` event envelope `(stamp, deps)` can split
/// the fixed-width stamp prefix from the variable-length `VersionVector` frame
/// (PRD 0007); the stamp is always exactly these leading bytes.
pub const KAIROS_WIRE_LEN: usize = 17;

impl Kairos {
    /// Encodes this stamp as its canonical version-1 wire frame: a `0x01` version
    /// byte followed by the 16-byte big-endian payload (PRD 0002). Infallible and
    /// fixed-size. The big-endian payload makes byte-wise comparison of two
    /// same-version frames equal the `Kairos` order, so encoded stamps sort
    /// causally in a byte-keyed store.
    ///
    /// # Byte identity is contract, not just byte order
    ///
    /// Both field consumers embed this frame in Ed25519 signature *preimages* and
    /// recompute it at verification (`pinax`'s event provenance, `ethos`'s seal;
    /// verified 2026-07-02). A future frame version is therefore a breaking change
    /// to record-carried authenticity ecosystem-wide, not merely a codec migration:
    /// every stored signature minted over a v1 preimage fails against a recomputed
    /// v2 frame. Any v2 ships only with advance notice to the named consumers, so
    /// stored records can pin v1 bytes in their preimages or re-sign (PRD 0002).
    #[must_use]
    pub const fn to_bytes(&self) -> [u8; KAIROS_WIRE_LEN] {
        let p = self.0.to_be_bytes();
        [
            KAIROS_WIRE_V1,
            p[0],
            p[1],
            p[2],
            p[3],
            p[4],
            p[5],
            p[6],
            p[7],
            p[8],
            p[9],
            p[10],
            p[11],
            p[12],
            p[13],
            p[14],
            p[15],
        ]
    }

    /// Decodes a canonical wire frame produced by [`Kairos::to_bytes`].
    ///
    /// Validates structure only: the exact frame length and a known version tag.
    /// It does not validate `station_id`; that is a `Clock` construction invariant,
    /// not a property of a decoded value, so decode round-trips whatever a valid
    /// encoder produced (PRD 0002).
    ///
    /// # Errors
    /// Returns [`DecodeError::UnexpectedLength`] if `bytes` is not exactly the
    /// 17-byte frame, or [`DecodeError::UnknownVersion`] if the leading version
    /// byte is not understood. Never panics on adversarial input.
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
        let frame: &[u8; KAIROS_WIRE_LEN] =
            bytes
                .try_into()
                .map_err(|_| DecodeError::UnexpectedLength {
                    expected: KAIROS_WIRE_LEN,
                    found: bytes.len(),
                })?;
        // Split by index rather than a `payload @ ..` sub-array binding: the
        // binding form is opaque to the Kani codegen (model-checking issue #707),
        // and this split is identical in behavior with the same panic-freedom
        // (const-bounded on a fixed-size frame; the fallible conversion keeps the
        // parser panic-free by construction, as in the `metis` vector decoder).
        let version = frame[0];
        let payload: [u8; KAIROS_WIRE_LEN - 1] =
            frame[1..]
                .try_into()
                .map_err(|_| DecodeError::UnexpectedLength {
                    expected: KAIROS_WIRE_LEN,
                    found: bytes.len(),
                })?;
        match version {
            KAIROS_WIRE_V1 => Ok(Self(u128::from_be_bytes(payload))),
            other => Err(DecodeError::UnknownVersion(other)),
        }
    }
}

/// Error returned by [`Kairos::from_bytes`] when decoding a wire frame.
///
/// Decode validates structure only (version tag and length), not `station_id`.
/// `#[non_exhaustive]` so a future version's validation failure can be added
/// without breaking callers.
#[non_exhaustive]
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecodeError {
    /// The leading version byte is not a version this build understands.
    #[error("unknown Kairos wire version: {0:#04x}")]
    UnknownVersion(u8),
    /// The input was not exactly the expected frame length.
    #[error("unexpected Kairos frame length: expected {expected}, found {found}")]
    UnexpectedLength {
        /// The frame length the decoder requires.
        expected: usize,
        /// The actual length of the supplied input.
        found: usize,
    },
}