ASELSANMicrokernel
S566 · SOURCE-BOUND GATE EVIDENCE

S566 · R1 güncelleme: laboratuvar güncelleme gösterimi runbook sözleşmesi

tam S566 implementation modülü → Operations --test hedefi ile bağlı tam focused test → ayrı Operations kaydı Bu sayfa yalnız S566 kapısına aittir; komşu kapıların kaynakları bu kabulün içine katılmaz.

S566Focused kod testiOperations id exactsource SHA exacttest target exact

operation: g8l-s566-r1-lab-update-demonstration-runbook-contract

uygulama/model · focused test · Operations · 3 exact excerpt

sequence-bound=true · implementation-bound=true
01 · Yürütme / doğrulama kodu

Kapının gerçek repository sözleşmesi

tam dosyaL1–L868
kernel/src/g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s566_r1_lab_update_demonstration_runbook_contract.rs::S566 r1 lab update demonstration runbook contract implementation
//! S566 models the laboratory update demonstration runbook contract for the
//! R1 application/recovery/update stage.  It is a host-side source model only:
//! the runbook is described, ordered and type-checked here, it is never
//! executed.
//!
//! The contract fixes six ordered steps (freeze package, verify manifest,
//! stage to the inactive slot, reboot, confirm boot markers, commit or
//! rollback).  Every step carries a required evidence kind (hash, UART marker
//! or operator report) and an observation class (host-observed or
//! operator-reported).  The frozen S545 package identities are reused as the
//! fixture "current image" so that later demonstrations bind to an exact,
//! already frozen artifact instead of an ad-hoc build.  A demo-boundary
//! statement is part of the contract: the subject is a laboratory
//! demonstrator and no field or product claim is derivable from it.
//!
//! Fail-closed conditions: step reordering, duplicated or missing steps, a
//! missing or mismatched evidence kind, a mismatched observation class, a
//! current image that differs from the S545 fixture, malformed digests, any
//! field/product claim, any claim flag set without recorded evidence, any
//! claim that a step was executed or evidence was recorded, and divergence
//! after publication.
//!
//! The module performs no device operation, opens no UART, writes no SD card,
//! reports no power transition, and is not wired into any boot, IRQ,
//! scheduler or driver path.  It cannot promote the immutable S540 or S543
//! physical RED verdicts.  `RUNBOOK_EXECUTED_IN_S566` is `false`.
//!
//! Predecessor: S565 (staged update apply/rollback model).  Next gate: S567
//! (application/recovery/update acceptance matrix).

pub const S566_SEQUENCE: usize = 566;
pub const S566_EXPECTED_PREDECESSOR: usize = 565;
pub const S566_NEXT_SEQUENCE: usize = 567;
pub const S566_R1_STAGE: u8 = 4;
pub const S566_R1_RANGE_FIRST: usize = 536;
pub const S566_R1_RANGE_LAST: usize = 568;
pub const S566_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS: usize = 0;
pub const S566_PHYSICAL_OBSERVATIONS: usize = 0;
pub const S566_PHYSICAL_OR_DEVICE_OPERATIONS: usize = 0;
pub const S566_SD_WRITES: usize = 0;
pub const S566_UART_OPENS: usize = 0;
pub const S566_POWER_TRANSITIONS: usize = 0;
pub const S566_NEW_IMMUTABLE_RAW_CAPTURES: usize = 0;
pub const S566_S540_PHYSICAL_VERDICT_RETAINED_RED: bool = true;
pub const S566_S543_PHYSICAL_VERDICT_RETAINED_RED: bool = true;
pub const S566_AUTOMATIC_PROMOTION: bool = false;
pub const S566_BOOT_TO_UI_PHYSICALLY_OBSERVED: bool = false;
pub const S566_HARDWARE_PRESENT: bool = false;
pub const S566_R1_ACCEPTANCE_COMPLETE: bool = false;
pub const RUNBOOK_EXECUTED_IN_S566: bool = false;

// Runbook shape.
pub const S566_RUNBOOK_STEP_COUNT: usize = 6;
pub const S566_HOST_OBSERVED_STEPS: usize = 4;
pub const S566_OPERATOR_REPORTED_STEPS: usize = 2;
pub const S566_HASH_EVIDENCE_STEPS: usize = 3;
pub const S566_UART_MARKER_EVIDENCE_STEPS: usize = 1;
pub const S566_OPERATOR_REPORT_EVIDENCE_STEPS: usize = 2;
pub const S566_EXECUTED_STEPS: usize = 0;
pub const S566_EVIDENCE_RECORDS: usize = 0;
pub const S566_DEMO_BOUNDARY_STATEMENT: &str =
    "LAB_DEMONSTRATOR_ONLY; NO_FIELD_CLAIM; NO_PRODUCT_CLAIM";
pub const S566_ACTIVE_SLOT: G8lS566Slot = G8lS566Slot::A;
pub const S566_INACTIVE_SLOT: G8lS566Slot = G8lS566Slot::B;

// Fixture "current image": the frozen S545 package identities, byte-exact.
pub const S566_CURRENT_PACKAGE_ENTRIES: usize = 4;
pub const S566_CURRENT_IMAGE_NAME: &str = "aselsanos-rpi5.img";
pub const S566_CURRENT_IMAGE_BYTES: u64 = 945_760;
pub const S566_CURRENT_IMAGE_SHA256: &str =
    "ed1901a991e2f9e9ae3c16f254147a2b0180686a8d70ca5d7353374fee08d467";
pub const S566_CURRENT_DTB_NAME: &str = "bcm2712-rpi-5-b.dtb";
pub const S566_CURRENT_DTB_BYTES: u64 = 78_703;
pub const S566_CURRENT_DTB_SHA256: &str =
    "40a2fbe9c29e8b9a4912cf726a943068defb779fc052ec38e457a79c58abca00";
pub const S566_CURRENT_CONFIG_NAME: &str = "config.txt";
pub const S566_CURRENT_CONFIG_BYTES: u64 = 420;
pub const S566_CURRENT_CONFIG_SHA256: &str =
    "aef848bf6e0c324148eade5054a15c71a1e8c04814a3ed2e680056f87c1f9bba";
pub const S566_CURRENT_MANIFEST_NAME: &str = "SHA256SUMS";
pub const S566_CURRENT_MANIFEST_BYTES: u64 = 248;
pub const S566_CURRENT_MANIFEST_SHA256: &str =
    "4accd201849f68107db4d21e4657b1510f043d7f900f713c383bd8909d33e586";
pub const S566_CURRENT_MANIFEST_LINES: usize = 3;

// Boot markers the demonstration must confirm after the staged reboot.
pub const S566_BOOT_MARKER: &str = "ASELSAN/BOOT8H";
pub const S566_BOOT_TO_UI_MARKER: &str = "[R1:S536] BOOT_TO_UI_READY";
pub const S566_BOOT_MARKER_MINIMUM: usize = 1;
pub const S566_BOOT_TO_UI_MARKER_EXACT: usize = 1;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS566Slot {
    A,
    B,
}

impl G8lS566Slot {
    pub const fn other(self) -> Self {
        match self {
            Self::A => Self::B,
            Self::B => Self::A,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS566RunbookStep {
    FreezePackage,
    VerifyManifest,
    StageToInactiveSlot,
    Reboot,
    ConfirmBootMarkers,
    CommitOrRollback,
}

impl G8lS566RunbookStep {
    pub const fn ordinal(self) -> usize {
        match self {
            Self::FreezePackage => 0,
            Self::VerifyManifest => 1,
            Self::StageToInactiveSlot => 2,
            Self::Reboot => 3,
            Self::ConfirmBootMarkers => 4,
            Self::CommitOrRollback => 5,
        }
    }

    pub const fn from_ordinal(ordinal: usize) -> Option<Self> {
        match ordinal {
            0 => Some(Self::FreezePackage),
            1 => Some(Self::VerifyManifest),
            2 => Some(Self::StageToInactiveSlot),
            3 => Some(Self::Reboot),
            4 => Some(Self::ConfirmBootMarkers),
            5 => Some(Self::CommitOrRollback),
            _ => None,
        }
    }

    pub const fn next(self) -> Option<Self> {
        Self::from_ordinal(self.ordinal() + 1)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS566EvidenceKind {
    Hash,
    UartMarker,
    OperatorReport,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS566ObservationClass {
    HostObserved,
    OperatorReported,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS566StepSpec {
    pub step: G8lS566RunbookStep,
    pub evidence_kind: G8lS566EvidenceKind,
    pub observation_class: G8lS566ObservationClass,
}

pub const S566_RUNBOOK_TABLE: [G8lS566StepSpec; S566_RUNBOOK_STEP_COUNT] = [
    G8lS566StepSpec {
        step: G8lS566RunbookStep::FreezePackage,
        evidence_kind: G8lS566EvidenceKind::Hash,
        observation_class: G8lS566ObservationClass::HostObserved,
    },
    G8lS566StepSpec {
        step: G8lS566RunbookStep::VerifyManifest,
        evidence_kind: G8lS566EvidenceKind::Hash,
        observation_class: G8lS566ObservationClass::HostObserved,
    },
    G8lS566StepSpec {
        step: G8lS566RunbookStep::StageToInactiveSlot,
        evidence_kind: G8lS566EvidenceKind::Hash,
        observation_class: G8lS566ObservationClass::HostObserved,
    },
    G8lS566StepSpec {
        step: G8lS566RunbookStep::Reboot,
        evidence_kind: G8lS566EvidenceKind::OperatorReport,
        observation_class: G8lS566ObservationClass::OperatorReported,
    },
    G8lS566StepSpec {
        step: G8lS566RunbookStep::ConfirmBootMarkers,
        evidence_kind: G8lS566EvidenceKind::UartMarker,
        observation_class: G8lS566ObservationClass::HostObserved,
    },
    G8lS566StepSpec {
        step: G8lS566RunbookStep::CommitOrRollback,
        evidence_kind: G8lS566EvidenceKind::OperatorReport,
        observation_class: G8lS566ObservationClass::OperatorReported,
    },
];

pub const fn s566_runbook_spec(step: G8lS566RunbookStep) -> G8lS566StepSpec {
    S566_RUNBOOK_TABLE[step.ordinal()]
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS566ArtifactIdentity<'a> {
    pub name: &'a str,
    pub bytes: u64,
    pub sha256: &'a str,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS566PackageIdentity<'a> {
    pub image: G8lS566ArtifactIdentity<'a>,
    pub dtb: G8lS566ArtifactIdentity<'a>,
    pub config: G8lS566ArtifactIdentity<'a>,
    pub manifest: G8lS566ArtifactIdentity<'a>,
    pub manifest_lines: usize,
}

pub const fn s566_current_image_fixture() -> G8lS566PackageIdentity<'static> {
    G8lS566PackageIdentity {
        image: G8lS566ArtifactIdentity {
            name: S566_CURRENT_IMAGE_NAME,
            bytes: S566_CURRENT_IMAGE_BYTES,
            sha256: S566_CURRENT_IMAGE_SHA256,
        },
        dtb: G8lS566ArtifactIdentity {
            name: S566_CURRENT_DTB_NAME,
            bytes: S566_CURRENT_DTB_BYTES,
            sha256: S566_CURRENT_DTB_SHA256,
        },
        config: G8lS566ArtifactIdentity {
            name: S566_CURRENT_CONFIG_NAME,
            bytes: S566_CURRENT_CONFIG_BYTES,
            sha256: S566_CURRENT_CONFIG_SHA256,
        },
        manifest: G8lS566ArtifactIdentity {
            name: S566_CURRENT_MANIFEST_NAME,
            bytes: S566_CURRENT_MANIFEST_BYTES,
            sha256: S566_CURRENT_MANIFEST_SHA256,
        },
        manifest_lines: S566_CURRENT_MANIFEST_LINES,
    }
}

/// A demo-boundary statement.  Only `{lab_demonstrator: true, field_claim:
/// false, product_claim: false}` is accepted.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS566DemoBoundary {
    pub lab_demonstrator: bool,
    pub field_claim: bool,
    pub product_claim: bool,
}

pub const S566_DEMO_BOUNDARY: G8lS566DemoBoundary = G8lS566DemoBoundary {
    lab_demonstrator: true,
    field_claim: false,
    product_claim: false,
};

/// Claim flags.  A flag may only be set when the ledger derives it from
/// recorded evidence; the contract itself has no evidence, so every flag must
/// be `false` at publication.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub struct G8lS566ClaimFlags {
    pub update_demonstrated: bool,
    pub rollback_demonstrated: bool,
    pub boot_markers_confirmed: bool,
    pub package_identity_verified: bool,
}

impl G8lS566ClaimFlags {
    pub const NONE: Self = Self {
        update_demonstrated: false,
        rollback_demonstrated: false,
        boot_markers_confirmed: false,
        package_identity_verified: false,
    };

    pub const fn any(self) -> bool {
        self.update_demonstrated
            || self.rollback_demonstrated
            || self.boot_markers_confirmed
            || self.package_identity_verified
    }

    /// `self` is a subset of `evidence`-derived flags.
    pub const fn is_covered_by(self, derived: Self) -> bool {
        (!self.update_demonstrated || derived.update_demonstrated)
            && (!self.rollback_demonstrated || derived.rollback_demonstrated)
            && (!self.boot_markers_confirmed || derived.boot_markers_confirmed)
            && (!self.package_identity_verified || derived.package_identity_verified)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS566StepDeclaration {
    pub ordinal: usize,
    pub step: G8lS566RunbookStep,
    pub evidence_kind: Option<G8lS566EvidenceKind>,
    pub observation_class: G8lS566ObservationClass,
}

pub fn s566_canonical_declarations() -> [G8lS566StepDeclaration; S566_RUNBOOK_STEP_COUNT] {
    let mut declarations = [G8lS566StepDeclaration {
        ordinal: 0,
        step: G8lS566RunbookStep::FreezePackage,
        evidence_kind: None,
        observation_class: G8lS566ObservationClass::HostObserved,
    }; S566_RUNBOOK_STEP_COUNT];
    let mut index = 0;
    while index < S566_RUNBOOK_STEP_COUNT {
        let spec = S566_RUNBOOK_TABLE[index];
        declarations[index] = G8lS566StepDeclaration {
            ordinal: index,
            step: spec.step,
            evidence_kind: Some(spec.evidence_kind),
            observation_class: spec.observation_class,
        };
        index += 1;
    }
    declarations
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS566RunbookContractRequest<'a> {
    pub contract_anchor_id: u64,
    pub predecessor_sequence: usize,
    pub current_image: G8lS566PackageIdentity<'a>,
    pub active_slot: G8lS566Slot,
    pub steps: &'a [G8lS566StepDeclaration],
    pub boundary: G8lS566DemoBoundary,
    pub claims: G8lS566ClaimFlags,
    pub executed_steps: usize,
    pub evidence_records: usize,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS566RunbookContractReceipt {
    pub sequence: usize,
    pub predecessor_sequence: usize,
    pub next_sequence: usize,
    pub r1_stage: u8,
    pub contract_anchor_id: u64,
    pub step_count: usize,
    pub host_observed_steps: usize,
    pub operator_reported_steps: usize,
    pub hash_evidence_steps: usize,
    pub uart_marker_evidence_steps: usize,
    pub operator_report_evidence_steps: usize,
    pub current_image_bytes: u64,
    pub current_image_digest_prefix: u64,
    pub current_package_entries: usize,
    pub active_slot: G8lS566Slot,
    pub staging_slot: G8lS566Slot,
    pub boundary: G8lS566DemoBoundary,
    pub claims: G8lS566ClaimFlags,
    pub contract_published: bool,
    pub executed_steps: usize,
    pub evidence_records: usize,
    pub s540_physical_verdict_retained_red: bool,
    pub s543_physical_verdict_retained_red: bool,
    pub automatic_promotion: bool,
    pub hardware_present: bool,
    pub supported_profile_runtime_observations: usize,
    pub physical_observations: usize,
    pub boot_to_ui_physically_observed: bool,
    pub r1_acceptance_complete: bool,
    pub runbook_executed: bool,
}

#[derive(Debug)]
pub struct G8lS566RunbookContractState {
    receipt: Option<G8lS566RunbookContractReceipt>,
}

impl G8lS566RunbookContractState {
    pub const fn new() -> Self {
        Self { receipt: None }
    }

    pub const fn receipt(&self) -> Option<G8lS566RunbookContractReceipt> {
        self.receipt
    }
}

impl Default for G8lS566RunbookContractState {
    fn default() -> Self {
        Self::new()
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS566RunbookContractOutcome {
    ContractPublished(G8lS566RunbookContractReceipt),
    ContractRetained(G8lS566RunbookContractReceipt),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS566RunbookContractError {
    WrongPredecessor,
    ZeroAnchor,
    WrongStepCount,
    StepOutOfOrder,
    DuplicateStep,
    EvidenceKindMissing,
    EvidenceKindMismatch,
    ObservationClassMismatch,
    CurrentImageMismatch,
    InvalidDigest,
    ActiveSlotIsStagingSlot,
    DemoBoundaryViolated,
    FieldClaimForbidden,
    ProductClaimForbidden,
    ClaimWithoutEvidence,
    ExecutionClaimed,
    EvidenceRecordsClaimed,
    LedgerComplete,
    HashEvidenceMismatch,
    MarkerMatrixMismatch,
    DecisionMismatch,
    CounterOverflow,
    PublishedStateDrift,
}

impl G8lS566RunbookContractError {
    pub const fn diagnostic_code(self) -> u64 {
        match self {
            Self::WrongPredecessor => 1,
            Self::ZeroAnchor => 2,
            Self::WrongStepCount => 3,
            Self::StepOutOfOrder => 4,
            Self::DuplicateStep => 5,
            Self::EvidenceKindMissing => 6,
            Self::EvidenceKindMismatch => 7,
            Self::ObservationClassMismatch => 8,
            Self::CurrentImageMismatch => 9,
            Self::InvalidDigest => 10,
            Self::ActiveSlotIsStagingSlot => 11,
            Self::DemoBoundaryViolated => 12,
            Self::FieldClaimForbidden => 13,
            Self::ProductClaimForbidden => 14,
            Self::ClaimWithoutEvidence => 15,
            Self::ExecutionClaimed => 16,
            Self::EvidenceRecordsClaimed => 17,
            Self::LedgerComplete => 18,
            Self::HashEvidenceMismatch => 19,
            Self::MarkerMatrixMismatch => 20,
            Self::DecisionMismatch => 21,
            Self::CounterOverflow => 22,
            Self::PublishedStateDrift => 23,
        }
    }
}

const fn hex_nibble(byte: u8) -> Option<u64> {
    match byte {
        b'0'..=b'9' => Some((byte - b'0') as u64),
        b'a'..=b'f' => Some((byte - b'a' + 10) as u64),
        _ => None,
    }
}

/// Validates a 64-character lowercase hex digest and folds its first sixteen
/// characters into a `u64` fingerprint.
pub const fn s566_digest_prefix(digest: &str) -> Option<u64> {
    let bytes = digest.as_bytes();
    if bytes.len() != 64 {
        return None;
    }
    let mut prefix = 0u64;
    let mut index = 0;
    while index < 64 {
        let nibble = match hex_nibble(bytes[index]) {
            Some(nibble) => nibble,
            None => return None,
        };
        if index < 16 {
            prefix = (prefix << 4) | nibble;
        }
        index += 1;
    }
    Some(prefix)
}

const fn str_eq(left: &str, right: &str) -> bool {
    let left = left.as_bytes();
    let right = right.as_bytes();
    if left.len() != right.len() {
        return false;
    }
    let mut index = 0;
    while index < left.len() {
        if left[index] != right[index] {
            return false;
        }
        index += 1;
    }
    true
}

fn artifact_matches(
    observed: G8lS566ArtifactIdentity<'_>,
    expected: G8lS566ArtifactIdentity<'static>,
) -> Result<(), G8lS566RunbookContractError> {
    let observed_prefix =
        s566_digest_prefix(observed.sha256).ok_or(G8lS566RunbookContractError::InvalidDigest)?;
    let expected_prefix =
        s566_digest_prefix(expected.sha256).ok_or(G8lS566RunbookContractError::InvalidDigest)?;
    if !str_eq(observed.name, expected.name)
        || observed.bytes != expected.bytes
        || observed_prefix != expected_prefix
        || !str_eq(observed.sha256, expected.sha256)
    {
        return Err(G8lS566RunbookContractError::CurrentImageMismatch);
    }
    Ok(())
}

pub fn check_s566_current_image(
    observed: G8lS566PackageIdentity<'_>,
) -> Result<u64, G8lS566RunbookContractError> {
    let fixture = s566_current_image_fixture();
    artifact_matches(observed.image, fixture.image)?;
    artifact_matches(observed.dtb, fixture.dtb)?;
    artifact_matches(observed.config, fixture.config)?;
    artifact_matches(observed.manifest, fixture.manifest)?;
    if observed.manifest_lines != fixture.manifest_lines {
        return Err(G8lS566RunbookContractError::CurrentImageMismatch);
    }
    s566_digest_prefix(observed.image.sha256).ok_or(G8lS566RunbookContractError::InvalidDigest)
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub struct G8lS566StepTally {
    pub host_observed: usize,
    pub operator_reported: usize,
    pub hash: usize,
    pub uart_marker: usize,
    pub operator_report: usize,
}

/// Checks the declared step list against the canonical table: exact count,
/// exact order, no duplicates, present and matching evidence kind, matching
/// observation class.  Returns the per-class tallies.
pub fn check_s566_step_declarations(
    steps: &[G8lS566StepDeclaration],
) -> Result<G8lS566StepTally, G8lS566RunbookContractError> {
    if steps.len() != S566_RUNBOOK_STEP_COUNT {
        return Err(G8lS566RunbookContractError::WrongStepCount);
    }
    let mut seen = [false; S566_RUNBOOK_STEP_COUNT];
    let mut tally = G8lS566StepTally::default();
    for (index, declaration) in steps.iter().enumerate() {
        let spec = S566_RUNBOOK_TABLE[index];
        if seen[declaration.step.ordinal()] {
            return Err(G8lS566RunbookContractError::DuplicateStep);
        }
        seen[declaration.step.ordinal()] = true;
        if declaration.ordinal != index || declaration.step != spec.step {
            return Err(G8lS566RunbookContractError::StepOutOfOrder);
        }
        let kind = declaration
            .evidence_kind
            .ok_or(G8lS566RunbookContractError::EvidenceKindMissing)?;
        if kind != spec.evidence_kind {
            return Err(G8lS566RunbookContractError::EvidenceKindMismatch);
        }
        if declaration.observation_class != spec.observation_class {
            return Err(G8lS566RunbookContractError::ObservationClassMismatch);
        }
        let overflow = G8lS566RunbookContractError::CounterOverflow;
        match spec.observation_class {
            G8lS566ObservationClass::HostObserved => {
                tally.host_observed = tally.host_observed.checked_add(1).ok_or(overflow)?
            }
            G8lS566ObservationClass::OperatorReported => {
                tally.operator_reported = tally.operator_reported.checked_add(1).ok_or(overflow)?
            }
        }
        match spec.evidence_kind {
            G8lS566EvidenceKind::Hash => tally.hash = tally.hash.checked_add(1).ok_or(overflow)?,
            G8lS566EvidenceKind::UartMarker => {
                tally.uart_marker = tally.uart_marker.checked_add(1).ok_or(overflow)?
            }
            G8lS566EvidenceKind::OperatorReport => {
                tally.operator_report = tally.operator_report.checked_add(1).ok_or(overflow)?
            }
        }
    }
    Ok(tally)
}

pub const fn check_s566_demo_boundary(
    boundary: G8lS566DemoBoundary,
) -> Result<(), G8lS566RunbookContractError> {
    if boundary.field_claim {
        return Err(G8lS566RunbookContractError::FieldClaimForbidden);
    }
    if boundary.product_claim {
        return Err(G8lS566RunbookContractError::ProductClaimForbidden);
    }
    if !boundary.lab_demonstrator {
        return Err(G8lS566RunbookContractError::DemoBoundaryViolated);
    }
    Ok(())
}

pub fn service_s566_model_runbook_contract(
    state: &mut G8lS566RunbookContractState,
    request: G8lS566RunbookContractRequest<'_>,
) -> Result<G8lS566RunbookContractOutcome, G8lS566RunbookContractError> {
    if request.predecessor_sequence != S566_EXPECTED_PREDECESSOR {
        return Err(G8lS566RunbookContractError::WrongPredecessor);
    }
    if request.contract_anchor_id == 0 {
        return Err(G8lS566RunbookContractError::ZeroAnchor);
    }
    let tally = check_s566_step_declarations(request.steps)?;
    let digest_prefix = check_s566_current_image(request.current_image)?;
    if request.active_slot != S566_ACTIVE_SLOT {
        return Err(G8lS566RunbookContractError::ActiveSlotIsStagingSlot);
    }
    check_s566_demo_boundary(request.boundary)?;
    if request.executed_steps != S566_EXECUTED_STEPS {
        return Err(G8lS566RunbookContractError::ExecutionClaimed);
    }
    if request.evidence_records != S566_EVIDENCE_RECORDS {
        return Err(G8lS566RunbookContractError::EvidenceRecordsClaimed);
    }
    // The contract carries no evidence; the derivable claim set is empty.
    let derived = G8lS566RunbookLedger::new().derive_claims();
    if !request.claims.is_covered_by(derived) {
        return Err(G8lS566RunbookContractError::ClaimWithoutEvidence);
    }
    let receipt = G8lS566RunbookContractReceipt {
        sequence: S566_SEQUENCE,
        predecessor_sequence: S566_EXPECTED_PREDECESSOR,
        next_sequence: S566_NEXT_SEQUENCE,
        r1_stage: S566_R1_STAGE,
        contract_anchor_id: request.contract_anchor_id,
        step_count: S566_RUNBOOK_STEP_COUNT,
        host_observed_steps: tally.host_observed,
        operator_reported_steps: tally.operator_reported,
        hash_evidence_steps: tally.hash,
        uart_marker_evidence_steps: tally.uart_marker,
        operator_report_evidence_steps: tally.operator_report,
        current_image_bytes: request.current_image.image.bytes,
        current_image_digest_prefix: digest_prefix,
        current_package_entries: S566_CURRENT_PACKAGE_ENTRIES,
        active_slot: request.active_slot,
        staging_slot: request.active_slot.other(),
        boundary: request.boundary,
        claims: request.claims,
        contract_published: true,
        executed_steps: S566_EXECUTED_STEPS,
        evidence_records: S566_EVIDENCE_RECORDS,
        s540_physical_verdict_retained_red: S566_S540_PHYSICAL_VERDICT_RETAINED_RED,
        s543_physical_verdict_retained_red: S566_S543_PHYSICAL_VERDICT_RETAINED_RED,
        automatic_promotion: S566_AUTOMATIC_PROMOTION,
        hardware_present: S566_HARDWARE_PRESENT,
        supported_profile_runtime_observations: S566_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS,
        physical_observations: S566_PHYSICAL_OBSERVATIONS,
        boot_to_ui_physically_observed: S566_BOOT_TO_UI_PHYSICALLY_OBSERVED,
        r1_acceptance_complete: S566_R1_ACCEPTANCE_COMPLETE,
        runbook_executed: RUNBOOK_EXECUTED_IN_S566,
    };
    if let Some(published) = state.receipt {
        if published != receipt {
            return Err(G8lS566RunbookContractError::PublishedStateDrift);
        }
        return Ok(G8lS566RunbookContractOutcome::ContractRetained(published));
    }
    state.receipt = Some(receipt);
    Ok(G8lS566RunbookContractOutcome::ContractPublished(receipt))
}

// ---------------------------------------------------------------------------
// Runbook ledger: the host-side model of how a future demonstration would have
// to record evidence.  It is driven only by the focused test with synthetic
// evidence; nothing here executes a step.
// ---------------------------------------------------------------------------

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS566Decision {
    Reboot,
    Commit,
    Rollback,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS566MarkerMatrix {
    pub boot8h: usize,
    pub boot_to_ui_ready: usize,
}

impl G8lS566MarkerMatrix {
    pub const fn total(self) -> Option<usize> {
        self.boot8h.checked_add(self.boot_to_ui_ready)
    }

    pub const fn is_exact(self) -> bool {
        self.boot8h >= S566_BOOT_MARKER_MINIMUM
            && self.boot_to_ui_ready == S566_BOOT_TO_UI_MARKER_EXACT
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS566EvidenceRecord<'a> {
    Hash(G8lS566ArtifactIdentity<'a>),
    UartMarker(G8lS566MarkerMatrix),
    OperatorReport {
        report_id: u64,
        decision: G8lS566Decision,
    },
}

impl G8lS566EvidenceRecord<'_> {
    pub const fn kind(self) -> G8lS566EvidenceKind {
        match self {
            Self::Hash(..) => G8lS566EvidenceKind::Hash,
            Self::UartMarker(..) => G8lS566EvidenceKind::UartMarker,
            Self::OperatorReport { .. } => G8lS566EvidenceKind::OperatorReport,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS566RunbookLedger {
    recorded: [bool; S566_RUNBOOK_STEP_COUNT],
    next: Option<G8lS566RunbookStep>,
    evidence_records: usize,
    terminal_decision: Option<G8lS566Decision>,
}

impl G8lS566RunbookLedger {
    pub const fn new() -> Self {
        Self {
            recorded: [false; S566_RUNBOOK_STEP_COUNT],
            next: Some(G8lS566RunbookStep::FreezePackage),
            evidence_records: 0,
            terminal_decision: None,
        }
    }

    pub const fn next_step(&self) -> Option<G8lS566RunbookStep> {
        self.next
    }

    pub const fn evidence_records(&self) -> usize {
        self.evidence_records
    }

    pub const fn terminal_decision(&self) -> Option<G8lS566Decision> {
        self.terminal_decision
    }

    pub const fn is_complete(&self) -> bool {
        self.next.is_none()
    }

    /// Claims derivable from recorded evidence.  Field and product claims are
    /// never derivable; the boundary is structural.
    pub const fn derive_claims(&self) -> G8lS566ClaimFlags {
        let package_identity_verified = self.recorded[0] && self.recorded[1] && self.recorded[2];
        let boot_markers_confirmed =
            package_identity_verified && self.recorded[3] && self.recorded[4];
        let (update_demonstrated, rollback_demonstrated) = match self.terminal_decision {
            Some(G8lS566Decision::Commit) => (boot_markers_confirmed, false),
            Some(G8lS566Decision::Rollback) => (false, boot_markers_confirmed),
            _ => (false, false),
        };
        G8lS566ClaimFlags {
            update_demonstrated,
            rollback_demonstrated,
            boot_markers_confirmed,
            package_identity_verified,
        }
    }

    /// Records one step.  Fails closed and leaves the ledger unchanged on any
    /// ordering, class, kind or payload violation.
    pub fn record(
        &mut self,
        step: G8lS566RunbookStep,
        reporter: G8lS566ObservationClass,
        evidence: G8lS566EvidenceRecord<'_>,
    ) -> Result<G8lS566ClaimFlags, G8lS566RunbookContractError> {
        let expected = self
            .next
            .ok_or(G8lS566RunbookContractError::LedgerComplete)?;
        if self.recorded[step.ordinal()] {
            return Err(G8lS566RunbookContractError::DuplicateStep);
        }
        if step != expected {
            return Err(G8lS566RunbookContractError::StepOutOfOrder);
        }
        let spec = s566_runbook_spec(step);
        if reporter != spec.observation_class {
            return Err(G8lS566RunbookContractError::ObservationClassMismatch);
        }
        if evidence.kind() != spec.evidence_kind {
            return Err(G8lS566RunbookContractError::EvidenceKindMismatch);
        }
        let fixture = s566_current_image_fixture();
        let mut decision = None;
        match (step, evidence) {
            (G8lS566RunbookStep::FreezePackage, G8lS566EvidenceRecord::Hash(identity))
            | (G8lS566RunbookStep::StageToInactiveSlot, G8lS566EvidenceRecord::Hash(identity)) => {
                artifact_matches(identity, fixture.image)
                    .map_err(|_| G8lS566RunbookContractError::HashEvidenceMismatch)?;
            }
            (G8lS566RunbookStep::VerifyManifest, G8lS566EvidenceRecord::Hash(identity)) => {
                artifact_matches(identity, fixture.manifest)
                    .map_err(|_| G8lS566RunbookContractError::HashEvidenceMismatch)?;
            }
            (
                G8lS566RunbookStep::Reboot,
                G8lS566EvidenceRecord::OperatorReport {
                    report_id,
                    decision: reported,
                },
            ) => {
                if report_id == 0 || reported != G8lS566Decision::Reboot {
                    return Err(G8lS566RunbookContractError::DecisionMismatch);
                }
            }
            (G8lS566RunbookStep::ConfirmBootMarkers, G8lS566EvidenceRecord::UartMarker(matrix)) => {
                matrix
                    .total()
                    .ok_or(G8lS566RunbookContractError::CounterOverflow)?;
                if !matrix.is_exact() {
                    return Err(G8lS566RunbookContractError::MarkerMatrixMismatch);
                }
            }
            (
                G8lS566RunbookStep::CommitOrRollback,
                G8lS566EvidenceRecord::OperatorReport {
                    report_id,
                    decision: reported,
                },
            ) => {
                if report_id == 0 || reported == G8lS566Decision::Reboot {
                    return Err(G8lS566RunbookContractError::DecisionMismatch);
                }
                decision = Some(reported);
            }
            _ => return Err(G8lS566RunbookContractError::EvidenceKindMismatch),
        }
        let evidence_records = self
            .evidence_records
            .checked_add(1)
            .ok_or(G8lS566RunbookContractError::CounterOverflow)?;
        self.recorded[step.ordinal()] = true;
        self.next = step.next();
        self.evidence_records = evidence_records;
        if decision.is_some() {
            self.terminal_decision = decision;
        }
        Ok(self.derive_claims())
    }
}

impl Default for G8lS566RunbookLedger {
    fn default() -> Self {
        Self::new()
    }
}
snippet sha256: f00d03362818file sha256: f00d03362818
02 · Doğrulayan test kodu

Operations komutuna bağlı focused test

tam dosyaL1–L860
simulation/tests/g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s566_r1_lab_update_demonstration_runbook_contract.rs::S566 r1 lab update demonstration runbook contract focused tests
use aselsan_microkernel_simulation::g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s545_r1_reachable_producer_candidate_freeze_prearm_contract::{
    S545_CONFIG_BYTES, S545_CONFIG_NAME, S545_CONFIG_SHA256, S545_DTB_BYTES, S545_DTB_NAME,
    S545_DTB_SHA256, S545_IMAGE_BYTES, S545_IMAGE_NAME, S545_IMAGE_SHA256, S545_MANIFEST_BYTES,
    S545_MANIFEST_LINES, S545_MANIFEST_NAME, S545_MANIFEST_SHA256, S545_PACKAGE_ENTRIES,
};
use aselsan_microkernel_simulation::g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s566_r1_lab_update_demonstration_runbook_contract::*;
use std::collections::BTreeSet;

const SOURCE: &str = include_str!(
    "../../kernel/src/g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s566_r1_lab_update_demonstration_runbook_contract.rs"
);
const MAIN: &str = include_str!("../../kernel/src/main.rs");
const SIMULATION_LIB: &str = include_str!("../src/lib.rs");

const MODULE: &str = "g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s566_r1_lab_update_demonstration_runbook_contract";
const ANCHOR: u64 = 0x5660_0000_0000_0001;

fn request<'a>(
    anchor: u64,
    steps: &'a [G8lS566StepDeclaration],
) -> G8lS566RunbookContractRequest<'a> {
    G8lS566RunbookContractRequest {
        contract_anchor_id: anchor,
        predecessor_sequence: S566_EXPECTED_PREDECESSOR,
        current_image: s566_current_image_fixture(),
        active_slot: S566_ACTIVE_SLOT,
        steps,
        boundary: S566_DEMO_BOUNDARY,
        claims: G8lS566ClaimFlags::NONE,
        executed_steps: 0,
        evidence_records: 0,
    }
}

fn publish(
    state: &mut G8lS566RunbookContractState,
    request: G8lS566RunbookContractRequest<'_>,
) -> Result<G8lS566RunbookContractOutcome, G8lS566RunbookContractError> {
    service_s566_model_runbook_contract(state, request)
}

fn image_hash() -> G8lS566EvidenceRecord<'static> {
    G8lS566EvidenceRecord::Hash(s566_current_image_fixture().image)
}

fn manifest_hash() -> G8lS566EvidenceRecord<'static> {
    G8lS566EvidenceRecord::Hash(s566_current_image_fixture().manifest)
}

fn exact_markers() -> G8lS566EvidenceRecord<'static> {
    G8lS566EvidenceRecord::UartMarker(G8lS566MarkerMatrix {
        boot8h: 1,
        boot_to_ui_ready: 1,
    })
}

fn report(decision: G8lS566Decision) -> G8lS566EvidenceRecord<'static> {
    G8lS566EvidenceRecord::OperatorReport {
        report_id: 7,
        decision,
    }
}

fn drive_ledger_to_decision(decision: G8lS566Decision) -> G8lS566RunbookLedger {
    use G8lS566ObservationClass::{HostObserved, OperatorReported};
    use G8lS566RunbookStep::*;
    let mut ledger = G8lS566RunbookLedger::new();
    ledger.record(FreezePackage, HostObserved, image_hash()).unwrap();
    ledger.record(VerifyManifest, HostObserved, manifest_hash()).unwrap();
    ledger.record(StageToInactiveSlot, HostObserved, image_hash()).unwrap();
    ledger
        .record(Reboot, OperatorReported, report(G8lS566Decision::Reboot))
        .unwrap();
    ledger
        .record(ConfirmBootMarkers, HostObserved, exact_markers())
        .unwrap();
    ledger
        .record(CommitOrRollback, OperatorReported, report(decision))
        .unwrap();
    ledger
}

#[test]
fn sequence_scope_and_nonpromotion_are_exact() {
    assert_eq!(S566_SEQUENCE, 566);
    assert_eq!(S566_EXPECTED_PREDECESSOR, 565);
    assert_eq!(S566_NEXT_SEQUENCE, 567);
    assert_eq!(S566_R1_STAGE, 4);
    assert_eq!(S566_R1_RANGE_FIRST, 536);
    assert_eq!(S566_R1_RANGE_LAST, 568);
    assert_eq!(S566_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS, 0);
    assert_eq!(S566_PHYSICAL_OBSERVATIONS, 0);
    assert_eq!(S566_PHYSICAL_OR_DEVICE_OPERATIONS, 0);
    assert_eq!(S566_SD_WRITES, 0);
    assert_eq!(S566_UART_OPENS, 0);
    assert_eq!(S566_POWER_TRANSITIONS, 0);
    assert_eq!(S566_NEW_IMMUTABLE_RAW_CAPTURES, 0);
    assert!(S566_S540_PHYSICAL_VERDICT_RETAINED_RED);
    assert!(S566_S543_PHYSICAL_VERDICT_RETAINED_RED);
    assert!(!S566_AUTOMATIC_PROMOTION);
    assert!(!S566_BOOT_TO_UI_PHYSICALLY_OBSERVED);
    assert!(!S566_HARDWARE_PRESENT);
    assert!(!S566_R1_ACCEPTANCE_COMPLETE);
    assert!(!RUNBOOK_EXECUTED_IN_S566);
    assert_eq!(S566_EXECUTED_STEPS, 0);
    assert_eq!(S566_EVIDENCE_RECORDS, 0);
    assert_eq!(S566_RUNBOOK_STEP_COUNT, 6);
    assert_eq!(
        S566_HOST_OBSERVED_STEPS + S566_OPERATOR_REPORTED_STEPS,
        S566_RUNBOOK_STEP_COUNT
    );
    assert_eq!(
        S566_HASH_EVIDENCE_STEPS
            + S566_UART_MARKER_EVIDENCE_STEPS
            + S566_OPERATOR_REPORT_EVIDENCE_STEPS,
        S566_RUNBOOK_STEP_COUNT
    );
    assert_eq!(
        S566_DEMO_BOUNDARY_STATEMENT,
        "LAB_DEMONSTRATOR_ONLY; NO_FIELD_CLAIM; NO_PRODUCT_CLAIM"
    );
    assert_eq!(S566_ACTIVE_SLOT, G8lS566Slot::A);
    assert_eq!(S566_INACTIVE_SLOT, G8lS566Slot::B);
    assert_eq!(S566_ACTIVE_SLOT.other(), S566_INACTIVE_SLOT);
}

#[test]
fn module_is_registered_in_kernel_and_simulation() {
    let kernel_line = format!("mod {MODULE};");
    let simulation_line = format!("pub mod {MODULE};");
    assert!(MAIN.lines().any(|line| line == kernel_line));
    assert!(SIMULATION_LIB.lines().any(|line| line == simulation_line));
    assert!(SIMULATION_LIB.contains(&format!("#[path = \"../../kernel/src/{MODULE}.rs\"]")));
}

#[test]
fn source_has_no_device_execution_or_uart_emission_surface() {
    for forbidden in [
        "unsafe",
        "asm!",
        "write_volatile",
        "crate::uart",
        "crate::arch",
        "#[no_mangle]",
        "spin::",
        "std::",
        "/dev/",
        "diskutil",
        "TIOCEXCL",
    ] {
        assert!(!SOURCE.contains(forbidden), "forbidden token: {forbidden}");
    }
    assert!(SOURCE.contains("performs no device operation"));
    assert!(SOURCE.contains("it is never\n//! executed"));
    assert!(SOURCE.contains("RUNBOOK_EXECUTED_IN_S566: bool = false"));
    assert!(SOURCE.contains("S566_HARDWARE_PRESENT: bool = false"));
    assert!(SOURCE.contains("S566_PHYSICAL_OBSERVATIONS: usize = 0"));
    assert!(SOURCE.contains("S566_R1_ACCEPTANCE_COMPLETE: bool = false"));
}

#[test]
fn diagnostic_codes_are_nonzero_and_unique() {
    use G8lS566RunbookContractError::*;
    let errors = [
        WrongPredecessor,
        ZeroAnchor,
        WrongStepCount,
        StepOutOfOrder,
        DuplicateStep,
        EvidenceKindMissing,
        EvidenceKindMismatch,
        ObservationClassMismatch,
        CurrentImageMismatch,
        InvalidDigest,
        ActiveSlotIsStagingSlot,
        DemoBoundaryViolated,
        FieldClaimForbidden,
        ProductClaimForbidden,
        ClaimWithoutEvidence,
        ExecutionClaimed,
        EvidenceRecordsClaimed,
        LedgerComplete,
        HashEvidenceMismatch,
        MarkerMatrixMismatch,
        DecisionMismatch,
        CounterOverflow,
        PublishedStateDrift,
    ];
    let codes: BTreeSet<u64> = errors
        .iter()
        .map(|error| error.diagnostic_code())
        .collect();
    assert_eq!(codes.len(), errors.len());
    assert_eq!(codes.len(), 23);
    assert!(!codes.contains(&0));
}

#[test]
fn current_image_fixture_matches_s545_package_identities() {
    let fixture = s566_current_image_fixture();
    assert_eq!(fixture.image.name, S545_IMAGE_NAME);
    assert_eq!(fixture.image.bytes, S545_IMAGE_BYTES);
    assert_eq!(fixture.image.sha256, S545_IMAGE_SHA256);
    assert_eq!(fixture.dtb.name, S545_DTB_NAME);
    assert_eq!(fixture.dtb.bytes, S545_DTB_BYTES);
    assert_eq!(fixture.dtb.sha256, S545_DTB_SHA256);
    assert_eq!(fixture.config.name, S545_CONFIG_NAME);
    assert_eq!(fixture.config.bytes, S545_CONFIG_BYTES);
    assert_eq!(fixture.config.sha256, S545_CONFIG_SHA256);
    assert_eq!(fixture.manifest.name, S545_MANIFEST_NAME);
    assert_eq!(fixture.manifest.bytes, S545_MANIFEST_BYTES);
    assert_eq!(fixture.manifest.sha256, S545_MANIFEST_SHA256);
    assert_eq!(fixture.manifest_lines, S545_MANIFEST_LINES);
    assert_eq!(S566_CURRENT_PACKAGE_ENTRIES, S545_PACKAGE_ENTRIES);
    assert_eq!(
        check_s566_current_image(fixture).unwrap(),
        0xed19_01a9_91e2_f9e9
    );
}

#[test]
fn runbook_table_is_ordered_with_required_evidence_and_observation_classes() {
    use G8lS566EvidenceKind::*;
    use G8lS566ObservationClass::*;
    use G8lS566RunbookStep::*;
    let expected = [
        (FreezePackage, Hash, HostObserved),
        (VerifyManifest, Hash, HostObserved),
        (StageToInactiveSlot, Hash, HostObserved),
        (Reboot, OperatorReport, OperatorReported),
        (ConfirmBootMarkers, UartMarker, HostObserved),
        (CommitOrRollback, OperatorReport, OperatorReported),
    ];
    for (index, (step, kind, class)) in expected.into_iter().enumerate() {
        let spec = S566_RUNBOOK_TABLE[index];
        assert_eq!(spec.step, step);
        assert_eq!(spec.evidence_kind, kind);
        assert_eq!(spec.observation_class, class);
        assert_eq!(step.ordinal(), index);
        assert_eq!(G8lS566RunbookStep::from_ordinal(index), Some(step));
        assert_eq!(s566_runbook_spec(step), spec);
    }
    assert_eq!(G8lS566RunbookStep::from_ordinal(6), None);
    assert_eq!(CommitOrRollback.next(), None);
    assert_eq!(FreezePackage.next(), Some(VerifyManifest));
    let tally = check_s566_step_declarations(&s566_canonical_declarations()).unwrap();
    assert_eq!(tally.host_observed, S566_HOST_OBSERVED_STEPS);
    assert_eq!(tally.operator_reported, S566_OPERATOR_REPORTED_STEPS);
    assert_eq!(tally.hash, S566_HASH_EVIDENCE_STEPS);
    assert_eq!(tally.uart_marker, S566_UART_MARKER_EVIDENCE_STEPS);
    assert_eq!(tally.operator_report, S566_OPERATOR_REPORT_EVIDENCE_STEPS);
}

#[test]
fn contract_publishes_with_canonical_declarations() {
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::new();
    assert_eq!(state.receipt(), None);
    let G8lS566RunbookContractOutcome::ContractPublished(receipt) =
        publish(&mut state, request(ANCHOR, &steps)).unwrap()
    else {
        panic!("first publication must publish")
    };
    assert_eq!(receipt.sequence, 566);
    assert_eq!(receipt.predecessor_sequence, 565);
    assert_eq!(receipt.next_sequence, 567);
    assert_eq!(receipt.r1_stage, 4);
    assert_eq!(receipt.contract_anchor_id, ANCHOR);
    assert_eq!(receipt.step_count, 6);
    assert_eq!(receipt.host_observed_steps, 4);
    assert_eq!(receipt.operator_reported_steps, 2);
    assert_eq!(receipt.hash_evidence_steps, 3);
    assert_eq!(receipt.uart_marker_evidence_steps, 1);
    assert_eq!(receipt.operator_report_evidence_steps, 2);
    assert_eq!(receipt.current_image_bytes, 945_760);
    assert_eq!(receipt.current_image_digest_prefix, 0xed19_01a9_91e2_f9e9);
    assert_eq!(receipt.current_package_entries, 4);
    assert_eq!(receipt.active_slot, G8lS566Slot::A);
    assert_eq!(receipt.staging_slot, G8lS566Slot::B);
    assert_eq!(receipt.boundary, S566_DEMO_BOUNDARY);
    assert_eq!(receipt.claims, G8lS566ClaimFlags::NONE);
    assert!(!receipt.claims.any());
    assert!(receipt.contract_published);
    assert_eq!(receipt.executed_steps, 0);
    assert_eq!(receipt.evidence_records, 0);
    assert!(receipt.s540_physical_verdict_retained_red);
    assert!(receipt.s543_physical_verdict_retained_red);
    assert!(!receipt.automatic_promotion);
    assert!(!receipt.hardware_present);
    assert_eq!(receipt.supported_profile_runtime_observations, 0);
    assert_eq!(receipt.physical_observations, 0);
    assert!(!receipt.boot_to_ui_physically_observed);
    assert!(!receipt.r1_acceptance_complete);
    assert!(!receipt.runbook_executed);
    assert_eq!(state.receipt(), Some(receipt));
}

#[test]
fn exact_replay_retains_the_same_receipt() {
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::default();
    let first = publish(&mut state, request(ANCHOR, &steps)).unwrap();
    let G8lS566RunbookContractOutcome::ContractPublished(published) = first else {
        panic!("first publication must publish")
    };
    for _ in 0..3 {
        assert_eq!(
            publish(&mut state, request(ANCHOR, &steps)),
            Ok(G8lS566RunbookContractOutcome::ContractRetained(published))
        );
    }
    assert_eq!(state.receipt(), Some(published));
}

#[test]
fn divergent_input_after_publication_fails_closed() {
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::new();
    let G8lS566RunbookContractOutcome::ContractPublished(published) =
        publish(&mut state, request(ANCHOR, &steps)).unwrap()
    else {
        panic!("first publication must publish")
    };
    assert_eq!(
        publish(&mut state, request(ANCHOR + 1, &steps)),
        Err(G8lS566RunbookContractError::PublishedStateDrift)
    );
    assert_eq!(state.receipt(), Some(published));
    let mut reordered = steps;
    reordered.swap(4, 5);
    assert_eq!(
        publish(&mut state, request(ANCHOR, &reordered)),
        Err(G8lS566RunbookContractError::StepOutOfOrder)
    );
    assert_eq!(state.receipt(), Some(published));
}

#[test]
fn reordered_duplicated_or_truncated_steps_fail_closed() {
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::new();
    for (left, right) in [(0, 1), (2, 3), (3, 4), (0, 5)] {
        let mut reordered = steps;
        reordered.swap(left, right);
        assert_eq!(
            publish(&mut state, request(ANCHOR, &reordered)),
            Err(G8lS566RunbookContractError::StepOutOfOrder),
            "swap {left}<->{right}"
        );
    }
    let mut wrong_ordinal = steps;
    wrong_ordinal[2].ordinal = 3;
    assert_eq!(
        publish(&mut state, request(ANCHOR, &wrong_ordinal)),
        Err(G8lS566RunbookContractError::StepOutOfOrder)
    );
    let mut duplicated = steps;
    duplicated[5] = duplicated[4];
    duplicated[5].ordinal = 5;
    assert_eq!(
        publish(&mut state, request(ANCHOR, &duplicated)),
        Err(G8lS566RunbookContractError::DuplicateStep)
    );
    assert_eq!(
        publish(&mut state, request(ANCHOR, &steps[..5])),
        Err(G8lS566RunbookContractError::WrongStepCount)
    );
    let mut seven = steps.to_vec();
    seven.push(steps[5]);
    assert_eq!(
        publish(&mut state, request(ANCHOR, &seven)),
        Err(G8lS566RunbookContractError::WrongStepCount)
    );
    assert_eq!(
        publish(&mut state, request(ANCHOR, &[])),
        Err(G8lS566RunbookContractError::WrongStepCount)
    );
    assert_eq!(state.receipt(), None);
}

#[test]
fn missing_or_mismatched_evidence_kind_or_class_fails_closed() {
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::new();
    for index in 0..S566_RUNBOOK_STEP_COUNT {
        let mut missing = steps;
        missing[index].evidence_kind = None;
        assert_eq!(
            publish(&mut state, request(ANCHOR, &missing)),
            Err(G8lS566RunbookContractError::EvidenceKindMissing),
            "missing kind at {index}"
        );
    }
    let mut hash_for_reboot = steps;
    hash_for_reboot[3].evidence_kind = Some(G8lS566EvidenceKind::Hash);
    assert_eq!(
        publish(&mut state, request(ANCHOR, &hash_for_reboot)),
        Err(G8lS566RunbookContractError::EvidenceKindMismatch)
    );
    let mut report_for_markers = steps;
    report_for_markers[4].evidence_kind = Some(G8lS566EvidenceKind::OperatorReport);
    assert_eq!(
        publish(&mut state, request(ANCHOR, &report_for_markers)),
        Err(G8lS566RunbookContractError::EvidenceKindMismatch)
    );
    let mut operator_freeze = steps;
    operator_freeze[0].observation_class = G8lS566ObservationClass::OperatorReported;
    assert_eq!(
        publish(&mut state, request(ANCHOR, &operator_freeze)),
        Err(G8lS566RunbookContractError::ObservationClassMismatch)
    );
    let mut host_reboot = steps;
    host_reboot[3].observation_class = G8lS566ObservationClass::HostObserved;
    assert_eq!(
        publish(&mut state, request(ANCHOR, &host_reboot)),
        Err(G8lS566RunbookContractError::ObservationClassMismatch)
    );
    assert_eq!(state.receipt(), None);
}

#[test]
fn claim_flags_without_evidence_fail_closed() {
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::new();
    let claims = [
        G8lS566ClaimFlags {
            update_demonstrated: true,
            ..G8lS566ClaimFlags::NONE
        },
        G8lS566ClaimFlags {
            rollback_demonstrated: true,
            ..G8lS566ClaimFlags::NONE
        },
        G8lS566ClaimFlags {
            boot_markers_confirmed: true,
            ..G8lS566ClaimFlags::NONE
        },
        G8lS566ClaimFlags {
            package_identity_verified: true,
            ..G8lS566ClaimFlags::NONE
        },
    ];
    for claim in claims {
        assert!(claim.any());
        let mut claimed = request(ANCHOR, &steps);
        claimed.claims = claim;
        assert_eq!(
            publish(&mut state, claimed),
            Err(G8lS566RunbookContractError::ClaimWithoutEvidence),
            "{claim:?}"
        );
    }
    assert_eq!(G8lS566ClaimFlags::default(), G8lS566ClaimFlags::NONE);
    assert!(G8lS566ClaimFlags::NONE.is_covered_by(G8lS566ClaimFlags::NONE));
    assert_eq!(state.receipt(), None);
}

#[test]
fn demo_boundary_field_or_product_claim_fails_closed() {
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::new();
    let cases = [
        (
            G8lS566DemoBoundary {
                lab_demonstrator: true,
                field_claim: true,
                product_claim: false,
            },
            G8lS566RunbookContractError::FieldClaimForbidden,
        ),
        (
            G8lS566DemoBoundary {
                lab_demonstrator: true,
                field_claim: false,
                product_claim: true,
            },
            G8lS566RunbookContractError::ProductClaimForbidden,
        ),
        (
            G8lS566DemoBoundary {
                lab_demonstrator: false,
                field_claim: true,
                product_claim: true,
            },
            G8lS566RunbookContractError::FieldClaimForbidden,
        ),
        (
            G8lS566DemoBoundary {
                lab_demonstrator: false,
                field_claim: false,
                product_claim: false,
            },
            G8lS566RunbookContractError::DemoBoundaryViolated,
        ),
    ];
    for (boundary, error) in cases {
        assert_eq!(check_s566_demo_boundary(boundary), Err(error));
        let mut violated = request(ANCHOR, &steps);
        violated.boundary = boundary;
        assert_eq!(publish(&mut state, violated), Err(error));
    }
    assert_eq!(check_s566_demo_boundary(S566_DEMO_BOUNDARY), Ok(()));
    assert_eq!(state.receipt(), None);
}

#[test]
fn execution_or_evidence_record_claims_fail_closed() {
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::new();
    for executed in [1, 6, usize::MAX] {
        let mut executed_request = request(ANCHOR, &steps);
        executed_request.executed_steps = executed;
        assert_eq!(
            publish(&mut state, executed_request),
            Err(G8lS566RunbookContractError::ExecutionClaimed)
        );
    }
    for records in [1, 6, usize::MAX] {
        let mut recorded = request(ANCHOR, &steps);
        recorded.evidence_records = records;
        assert_eq!(
            publish(&mut state, recorded),
            Err(G8lS566RunbookContractError::EvidenceRecordsClaimed)
        );
    }
    let mut wrong_slot = request(ANCHOR, &steps);
    wrong_slot.active_slot = S566_INACTIVE_SLOT;
    assert_eq!(
        publish(&mut state, wrong_slot),
        Err(G8lS566RunbookContractError::ActiveSlotIsStagingSlot)
    );
    assert_eq!(state.receipt(), None);
}

#[test]
fn wrong_predecessor_and_zero_anchor_fail_closed() {
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::new();
    for predecessor in [0, 544, 564, 566, 567, usize::MAX] {
        let mut wrong = request(ANCHOR, &steps);
        wrong.predecessor_sequence = predecessor;
        assert_eq!(
            publish(&mut state, wrong),
            Err(G8lS566RunbookContractError::WrongPredecessor)
        );
    }
    assert_eq!(
        publish(&mut state, request(0, &steps)),
        Err(G8lS566RunbookContractError::ZeroAnchor)
    );
    assert_eq!(state.receipt(), None);
}

#[test]
fn current_image_drift_or_malformed_digest_fails_closed() {
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::new();
    let fixture = s566_current_image_fixture();

    let mut wrong_bytes = fixture;
    wrong_bytes.image.bytes = 941_392;
    let mut wrong_name = fixture;
    wrong_name.dtb.name = "bcm2711-rpi-4-b.dtb";
    let mut wrong_lines = fixture;
    wrong_lines.manifest_lines = 4;
    let mut swapped_digest = fixture;
    swapped_digest.config.sha256 = S566_CURRENT_MANIFEST_SHA256;
    for drifted in [wrong_bytes, wrong_name, wrong_lines, swapped_digest] {
        assert_eq!(
            check_s566_current_image(drifted),
            Err(G8lS566RunbookContractError::CurrentImageMismatch)
        );
        let mut drifted_request = request(ANCHOR, &steps);
        drifted_request.current_image = drifted;
        assert_eq!(
            publish(&mut state, drifted_request),
            Err(G8lS566RunbookContractError::CurrentImageMismatch)
        );
    }

    let uppercase = "ED1901A991E2F9E9AE3C16F254147A2B0180686A8D70CA5D7353374FEE08D467";
    let short = "ed1901a991e2f9e9";
    let long = "ed1901a991e2f9e9ae3c16f254147a2b0180686a8d70ca5d7353374fee08d46700";
    let non_hex = "ed1901a991e2f9e9ae3c16f254147a2b0180686a8d70ca5d7353374fee08d46g";
    for digest in [uppercase, short, long, non_hex, ""] {
        assert_eq!(s566_digest_prefix(digest), None, "{digest:?}");
        let mut malformed = fixture;
        malformed.image.sha256 = digest;
        assert_eq!(
            check_s566_current_image(malformed),
            Err(G8lS566RunbookContractError::InvalidDigest)
        );
        let mut malformed_request = request(ANCHOR, &steps);
        malformed_request.current_image = malformed;
        assert_eq!(
            publish(&mut state, malformed_request),
            Err(G8lS566RunbookContractError::InvalidDigest)
        );
    }
    assert_eq!(
        s566_digest_prefix(S566_CURRENT_MANIFEST_SHA256),
        Some(0x4acc_d201_849f_6810)
    );
    assert_eq!(
        s566_digest_prefix(&"0".repeat(64)),
        Some(0)
    );
    assert_eq!(
        s566_digest_prefix(&"f".repeat(64)),
        Some(u64::MAX)
    );
    assert_eq!(state.receipt(), None);
}

#[test]
fn ledger_accepts_exact_ordered_evidence_and_derives_commit_claims() {
    use G8lS566ObservationClass::{HostObserved, OperatorReported};
    use G8lS566RunbookStep::*;
    let mut ledger = G8lS566RunbookLedger::default();
    assert_eq!(ledger.next_step(), Some(FreezePackage));
    assert_eq!(ledger.derive_claims(), G8lS566ClaimFlags::NONE);
    assert!(!ledger.is_complete());

    let after_freeze = ledger.record(FreezePackage, HostObserved, image_hash()).unwrap();
    assert_eq!(after_freeze, G8lS566ClaimFlags::NONE);
    let after_manifest = ledger
        .record(VerifyManifest, HostObserved, manifest_hash())
        .unwrap();
    assert_eq!(after_manifest, G8lS566ClaimFlags::NONE);
    let after_stage = ledger
        .record(StageToInactiveSlot, HostObserved, image_hash())
        .unwrap();
    assert!(after_stage.package_identity_verified);
    assert!(!after_stage.boot_markers_confirmed);
    let after_reboot = ledger
        .record(Reboot, OperatorReported, report(G8lS566Decision::Reboot))
        .unwrap();
    assert!(!after_reboot.boot_markers_confirmed);
    let after_markers = ledger
        .record(ConfirmBootMarkers, HostObserved, exact_markers())
        .unwrap();
    assert!(after_markers.boot_markers_confirmed);
    assert!(!after_markers.update_demonstrated);
    assert_eq!(ledger.next_step(), Some(CommitOrRollback));
    let after_commit = ledger
        .record(
            CommitOrRollback,
            OperatorReported,
            report(G8lS566Decision::Commit),
        )
        .unwrap();
    assert_eq!(
        after_commit,
        G8lS566ClaimFlags {
            update_demonstrated: true,
            rollback_demonstrated: false,
            boot_markers_confirmed: true,
            package_identity_verified: true,
        }
    );
    assert!(ledger.is_complete());
    assert_eq!(ledger.next_step(), None);
    assert_eq!(ledger.evidence_records(), 6);
    assert_eq!(ledger.terminal_decision(), Some(G8lS566Decision::Commit));
    assert_eq!(
        ledger.record(FreezePackage, HostObserved, image_hash()),
        Err(G8lS566RunbookContractError::LedgerComplete)
    );
}

#[test]
fn ledger_rollback_path_derives_rollback_claim_only() {
    let ledger = drive_ledger_to_decision(G8lS566Decision::Rollback);
    assert_eq!(ledger.terminal_decision(), Some(G8lS566Decision::Rollback));
    let claims = ledger.derive_claims();
    assert!(claims.rollback_demonstrated);
    assert!(!claims.update_demonstrated);
    assert!(claims.boot_markers_confirmed);
    assert!(claims.package_identity_verified);
    let commit = drive_ledger_to_decision(G8lS566Decision::Commit).derive_claims();
    assert!(commit.update_demonstrated && !commit.rollback_demonstrated);
    // Even a fully driven ledger cannot make the demo boundary claim anything
    // beyond a lab demonstrator: the claim set has no field/product member and
    // the contract still refuses these flags because it carries no evidence.
    let steps = s566_canonical_declarations();
    let mut state = G8lS566RunbookContractState::new();
    let mut claimed = request(ANCHOR, &steps);
    claimed.claims = claims;
    assert_eq!(
        publish(&mut state, claimed),
        Err(G8lS566RunbookContractError::ClaimWithoutEvidence)
    );
}

#[test]
fn ledger_rejects_out_of_order_wrong_class_wrong_kind_and_bad_payload() {
    use G8lS566ObservationClass::{HostObserved, OperatorReported};
    use G8lS566RunbookStep::*;
    let mut ledger = G8lS566RunbookLedger::new();
    let pristine = ledger;
    assert_eq!(
        ledger.record(VerifyManifest, HostObserved, manifest_hash()),
        Err(G8lS566RunbookContractError::StepOutOfOrder)
    );
    assert_eq!(
        ledger.record(FreezePackage, OperatorReported, image_hash()),
        Err(G8lS566RunbookContractError::ObservationClassMismatch)
    );
    assert_eq!(
        ledger.record(FreezePackage, HostObserved, exact_markers()),
        Err(G8lS566RunbookContractError::EvidenceKindMismatch)
    );
    assert_eq!(
        ledger.record(FreezePackage, HostObserved, manifest_hash()),
        Err(G8lS566RunbookContractError::HashEvidenceMismatch)
    );
    let mut malformed = s566_current_image_fixture().image;
    malformed.sha256 = "not-a-digest";
    assert_eq!(
        ledger.record(FreezePackage, HostObserved, G8lS566EvidenceRecord::Hash(malformed)),
        Err(G8lS566RunbookContractError::HashEvidenceMismatch)
    );
    assert_eq!(ledger, pristine);

    ledger.record(FreezePackage, HostObserved, image_hash()).unwrap();
    assert_eq!(
        ledger.record(FreezePackage, HostObserved, image_hash()),
        Err(G8lS566RunbookContractError::DuplicateStep)
    );
    assert_eq!(
        ledger.record(VerifyManifest, HostObserved, image_hash()),
        Err(G8lS566RunbookContractError::HashEvidenceMismatch)
    );
    ledger.record(VerifyManifest, HostObserved, manifest_hash()).unwrap();
    ledger.record(StageToInactiveSlot, HostObserved, image_hash()).unwrap();

    assert_eq!(
        ledger.record(Reboot, OperatorReported, report(G8lS566Decision::Commit)),
        Err(G8lS566RunbookContractError::DecisionMismatch)
    );
    assert_eq!(
        ledger.record(
            Reboot,
            OperatorReported,
            G8lS566EvidenceRecord::OperatorReport {
                report_id: 0,
                decision: G8lS566Decision::Reboot,
            },
        ),
        Err(G8lS566RunbookContractError::DecisionMismatch)
    );
    ledger
        .record(Reboot, OperatorReported, report(G8lS566Decision::Reboot))
        .unwrap();

    for (boot8h, boot_to_ui_ready) in [(0, 1), (1, 0), (1, 2), (3, 0), (0, 0)] {
        let matrix = G8lS566MarkerMatrix {
            boot8h,
            boot_to_ui_ready,
        };
        assert!(!matrix.is_exact());
        assert_eq!(
            ledger.record(
                ConfirmBootMarkers,
                HostObserved,
                G8lS566EvidenceRecord::UartMarker(matrix),
            ),
            Err(G8lS566RunbookContractError::MarkerMatrixMismatch),
            "{boot8h}/{boot_to_ui_ready}"
        );
    }
    let before_markers = ledger;
    assert_eq!(ledger, before_markers);
    ledger
        .record(ConfirmBootMarkers, HostObserved, exact_markers())
        .unwrap();

    assert_eq!(
        ledger.record(
            CommitOrRollback,
            OperatorReported,
            report(G8lS566Decision::Reboot),
        ),
        Err(G8lS566RunbookContractError::DecisionMismatch)
    );
    assert_eq!(
        ledger.record(CommitOrRollback, HostObserved, report(G8lS566Decision::Commit)),
        Err(G8lS566RunbookContractError::ObservationClassMismatch)
    );
    assert_eq!(ledger.terminal_decision(), None);
    assert_eq!(ledger.evidence_records(), 5);
}

#[test]
fn marker_matrix_total_uses_checked_arithmetic() {
    use G8lS566ObservationClass::{HostObserved, OperatorReported};
    use G8lS566RunbookStep::*;
    let overflowing = G8lS566MarkerMatrix {
        boot8h: usize::MAX,
        boot_to_ui_ready: 1,
    };
    assert_eq!(overflowing.total(), None);
    assert!(overflowing.is_exact());
    let exact = G8lS566MarkerMatrix {
        boot8h: 2,
        boot_to_ui_ready: 1,
    };
    assert_eq!(exact.total(), Some(3));
    assert!(exact.is_exact());

    let mut ledger = G8lS566RunbookLedger::new();
    ledger.record(FreezePackage, HostObserved, image_hash()).unwrap();
    ledger.record(VerifyManifest, HostObserved, manifest_hash()).unwrap();
    ledger.record(StageToInactiveSlot, HostObserved, image_hash()).unwrap();
    ledger
        .record(Reboot, OperatorReported, report(G8lS566Decision::Reboot))
        .unwrap();
    assert_eq!(
        ledger.record(
            ConfirmBootMarkers,
            HostObserved,
            G8lS566EvidenceRecord::UartMarker(overflowing),
        ),
        Err(G8lS566RunbookContractError::CounterOverflow)
    );
    assert_eq!(ledger.next_step(), Some(ConfirmBootMarkers));
    assert_eq!(ledger.evidence_records(), 4);
    assert_eq!(
        ledger.record(
            ConfirmBootMarkers,
            HostObserved,
            G8lS566EvidenceRecord::UartMarker(exact),
        ),
        Ok(G8lS566ClaimFlags {
            update_demonstrated: false,
            rollback_demonstrated: false,
            boot_markers_confirmed: true,
            package_identity_verified: true,
        })
    );
}

#[test]
fn boot_marker_literals_and_slots_match_the_frozen_candidate_contract() {
    assert_eq!(S566_BOOT_MARKER, "ASELSAN/BOOT8H");
    assert_eq!(S566_BOOT_TO_UI_MARKER, "[R1:S536] BOOT_TO_UI_READY");
    assert_eq!(S566_BOOT_MARKER_MINIMUM, 1);
    assert_eq!(S566_BOOT_TO_UI_MARKER_EXACT, 1);
    assert_eq!(G8lS566Slot::A.other(), G8lS566Slot::B);
    assert_eq!(G8lS566Slot::B.other(), G8lS566Slot::A);
    let evidence = [
        (image_hash(), G8lS566EvidenceKind::Hash),
        (exact_markers(), G8lS566EvidenceKind::UartMarker),
        (report(G8lS566Decision::Commit), G8lS566EvidenceKind::OperatorReport),
    ];
    for (record, kind) in evidence {
        assert_eq!(record.kind(), kind);
    }
}
snippet sha256: b686a47b5b8dfile sha256: b686a47b5b8d
03 · Kapı kimlik kaydı

Operations sıra, kimlik ve başlık bağı

tam Operations kaydıL1952–L2011
website/src/lib/operations.ts::g8l-s566-r1-lab-update-demonstration-runbook-contract
  {
    id: "g8l-s566-r1-lab-update-demonstration-runbook-contract",
    date: "2026-08-30",
    sequence: 566,
    status: "passed",
    umbrella_status: "partial",
    title: "S566 · R1 güncelleme: laboratuvar güncelleme gösterimi runbook sözleşmesi",
    summary:
      "S566 laboratuvar güncelleme gösterimi runbook sözleşmesini host tarafında yayımlar; hiçbir runbook adımı yürütülmez ve RUNBOOK_EXECUTED_IN_S566=NO'dur. Sözleşme altı sıralı adımı (paketi dondur → manifest doğrula → inactive slot'a stage et → reboot → boot marker'larını doğrula → commit veya rollback) her adımın zorunlu kanıt türü (hash, UART marker, operatör raporu) ve gözlem sınıfı (host-gözlemli / operatör-bildirimli) ile birlikte fail-closed bir tabloya bağlar; S545'in dondurulmuş dört girdili paketi (945760 B / ed1901a9…08d467 image) fixture 'current image' olarak byte-exact yeniden kullanılır. Adım yeniden sıralama, eksik veya yanlış kanıt türü, yanlış gözlem sınıfı, fixture'dan sapan image kimliği, bozuk digest, saha/ürün iddiası ve kanıtsız kurulan her claim bayrağı fail-closed reddedilir; demo-boundary ifadesi laboratuvar göstericisidir ve saha veya ürün iddiası içermez. Focused 21/21 PASS'tir; S540 ve S543 fiziksel RED değişmez, physical observation=0, SD/UART/power/new-raw=0/0/0/0, Boot-to-UI=false ve R1 acceptance=false'dur. S567 uygulama/recovery/güncelleme kabul matrisi kapısıdır.",
    evidence: [
      "S566, S565'ten ayrı bir kernel/simulation kaynak modülü, 21-test focused binary, proof, status manifest, Operations kaydı ve complete Code kartına sahiptir; hiçbir production callsite'a bağlanmamıştır.",
      "Dar S566 durumu PASS'tir ve yalnız kaynak/host sözleşme kabulüdür; R1 umbrella=PARTIAL ve S540/S543 fiziksel kapı durumları RED olarak ayrı tutulur.",
      "Runbook tablosu altı sıralı adımı sabitler: freeze package (hash, host), verify manifest (hash, host), stage to inactive slot (hash, host), reboot (operatör raporu, operatör), confirm boot markers (UART marker, host), commit veya rollback (operatör raporu, operatör); host-gözlemli adım=4, operatör-bildirimli adım=2'dir.",
      "Fixture 'current image' S545 paket kimlikleriyle byte-exact aynıdır: aselsanos-rpi5.img 945760 B / ed1901a991e2f9e9ae3c16f254147a2b0180686a8d70ca5d7353374fee08d467, bcm2712-rpi-5-b.dtb 78703 B, config.txt 420 B ve SHA256SUMS 248 B / 3 satır; focused test bu sabitleri S545 modül sabitlerine eşitlikle bağlar.",
      "Her digest tam 64 küçük-harf hex olmak zorundadır; receipt image byte sayısını ve ilk on altı hex karakterin u64 katlamasını (0xed1901a991e2f9e9) taşır; büyük harf, kısa, uzun, hex-dışı ve boş digest'ler InvalidDigest ile reddedilir.",
      "Sözleşme servisi yanlış predecessor, sıfır anchor, yanlış adım sayısı (0/5/7), yer değiştirmiş adımlar, yanlış ordinal, tekrarlanan adım, eksik kanıt türü, yanlış kanıt türü, yanlış gözlem sınıfı, fixture'dan sapan image, aktif slot'un staging slot'a eşitlenmesi, saha iddiası, ürün iddiası, gösterici-dışı boundary, kanıtsız claim bayrağı, yürütülmüş adım iddiası ve kanıt kaydı iddiasını fail-closed reddeder.",
      "Demo-boundary ifadesi 'LAB_DEMONSTRATOR_ONLY; NO_FIELD_CLAIM; NO_PRODUCT_CLAIM' sabitine bağlıdır; claim kümesinde saha veya ürün üyesi yoktur ve boundary yalnız {lab_demonstrator=true, field_claim=false, product_claim=false} iken kabul edilir.",
      "Runbook ledger'ı yalnız focused testin sentetik kayıtlarıyla sürülen bir host modelidir: adım ancak sıradaki adımsa, tekrarlanmamışsa, raporlayan gözlem sınıfıyla ve kanıt türü tabloyla eşleşiyorsa ve payload exact ise kabul edilir; ihlal ledger'ı değiştirmeden hata döner.",
      "Ledger payload kuralları: adım 0 ve 2 image kimliği, adım 1 manifest kimliği, adım 3 sıfır-olmayan Reboot raporu, adım 4 exact marker matrisi (ASELSAN/BOOT8H>=1 ve [R1:S536] BOOT_TO_UI_READY tam 1; toplam checked_add ile), adım 5 sıfır-olmayan Commit veya Rollback raporu ister.",
      "Türetilen claim'ler kanıttan gelir: package_identity_verified adım 0–2, boot_markers_confirmed adım 3–4, update_demonstrated yalnız commit ve rollback_demonstrated yalnız rollback sonrası; sözleşmenin kendisi kanıt taşımadığından yayım anında her claim bayrağı false olmak zorundadır ve kanıtsız bayrak ClaimWithoutEvidence verir.",
      "Exact replay aynı receipt'i ContractRetained ile korur; yayından sonra farklı anchor veya farklı adım listesi PublishedStateDrift/StepOutOfOrder ile reddedilir ve receipt değişmez.",
      "Yirmi üç diagnostic code sıfırdan farklı ve benzersizdir.",
      "Focused target 1 grup / 21 passed / 0 failed / 0 ignored / 0 filtered verdi.",
      "Implementation 31689 B / f00d03362818afaea8c3ec225b297e20d327a7c215bd7866012efc892ff11138; focused test 31977 B / b686a47b5b8dec98f2d9fcd37f8e90d6e5a87e5337343ab269288ef2b205f54f SHA-256'dır.",
      "Proof 6763 B'dır.",
      "Kaynakta unsafe, asm!, write_volatile, crate::uart, crate::arch, #[no_mangle] veya spin:: yoktur; modül hiçbir boot, IRQ, scheduler veya driver yolundan çağrılmaz.",
      "S566 sırasında SD write/read-back/eject, UART open/capture, power transition, fiziksel koşu, güncelleme gösterimi veya yeni immutable raw üretimi yapılmadı; sözleşme yürütme yetkisi vermez.",
      "RUNBOOK_EXECUTED_IN_S566=NO; supported-profile runtime observations=0, physical observations=0, Boot-to-UI physically observed=false ve R1 acceptance=false'dur.",
      "S567 uygulama/recovery/güncelleme kabul matrisi host-only kapıdır; bu runbook'u yürütemez ve laboratuvar gösterimi ayrı kapı, fresh target revalidation ve açık operatör yetkisi ister.",
    ],
    commands: [
      "CARGO_INCREMENTAL=0 cargo test -p aselsan_microkernel_simulation --test g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s566_r1_lab_update_demonstration_runbook_contract -- --test-threads=1",
    ],
    terminalSessions: [
      {
        id: "s566-focused",
        title: "S566 laboratuvar güncelleme gösterimi runbook sözleşmesi focused kabulü",
        commandLines: [
          "CARGO_INCREMENTAL=0 cargo test -p aselsan_microkernel_simulation --test g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s566_r1_lab_update_demonstration_runbook_contract -- --test-threads=1",
        ],
        outputLines: [
          "test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s",
          "S566 focused=1 group / 21 passed / 0 failed",
          "hardware=none physical=0 runbook=NO",
        ],
        exitCode: 0,
        outputMode: "complete",
      },
    ],
    terminalSessionsNote:
      "S566 kaynak/host sözleşme kabulü PASS'tir; hiçbir runbook adımı yürütülmemiştir ve güncelleme gösterimi iddiası yoktur. S540 ve S543 RED raw'ları ve kararları değişmez.",
    limitations: [
      "S566 yalnız sözleşmeyi yayımlar; laboratuvar güncelleme gösterimi yürütülmemiştir, update_demonstrated ve rollback_demonstrated false'dur ve RUNBOOK_EXECUTED_IN_S566=NO'dur.",
      "S540 ve S543 fiziksel RED immutable kalır; hiçbir sözleşme veya ledger sonucu bunları yükseltemez.",
      "Bu kapıda hiçbir donanım/panel/modem/board gözlemi yoktur; SD write, UART open, power transition ve yeni raw sayıları 0'dır.",
      "Demo-boundary laboratuvar göstericisiyle sınırlıdır; saha veya ürün iddiası bu sözleşmeden türetilemez ve BOOT_TO_UI_READY gerçek UART'ta bu kapıda görülmemiştir.",
      "Operatör-bildirimli adımların bağımsız tanığı yoktur; ledger modeli yalnız sentetik kanıtla focused testte sürülmüştür.",
      "S567 kabul matrisi host-only kapıdır ve bu runbook'un yürütülmesine yetki vermez; gösterim ayrı kapı, fresh target revalidation ve açık operatör yetkisi ister.",
    ],
  },
snippet sha256: fd4b97c99775file sha256: 9726dbf00f84
Focused test komutu
CARGO_INCREMENTAL=0 cargo test -p aselsan_microkernel_simulation --test g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s566_r1_lab_update_demonstration_runbook_contract -- --test-threads=1
proof: docs/M8.1-RPi5-G8l-S566-R1-Lab-Update-Demonstration-Runbook-Contract-Proof.md
Registry schema v5 · generator website/scripts/generate-code-gates.mjs · Tam SHA-256: 3050638b71a684d8f8f947a8a6faa237a17fa8db5dc0db04fb207b668b462af9