File created successfully at: C:\Users\NickV\cubie-verification\verus\cubie_lease_spec.rs

// cubie_lease_spec.rs — Verus spec for AdmissionLease model
//
// Corresponds to: CubieTrustCompilerV2_0
// Verified-exact theorems bridged here:
//   lease_integrity      (CubieTrustCompilerV2_0, VERIFIED_EXACT)
//   bypass_no_lease      (CubieTrustCompilerV2_0, VERIFIED_EXACT)
//
// HMAC on lease signature is treated as an uninterpreted predicate.
// The Verus spec proves structural invariants; cryptographic correctness
// is a future obligation pending Verus crypto library maturation.
//
// No heap allocation in hot path. No asymmetric crypto in hot path.

#![allow(unused_imports)]
use vstd::prelude::*;

verus! {

// ---------------------------------------------------------------------------
// Core structs
// ---------------------------------------------------------------------------

/// A Centillion admission lease.
///
/// Leases are non-transferable: bound to (nonce, host_id, epoch, payload_hash).
/// Reusing a nonce or presenting a stale epoch must fail the host verifier.
/// Corresponds to the lease model in CubieTrustCompilerV2_0.
pub struct AdmissionLease {
    pub nonce:        u64,  // Must be globally unique per host
    pub host_id:      u32,  // Issuing host identifier
    pub epoch:        u64,  // Monotonically increasing; must match current epoch
    pub payload_hash: u64,  // Bound to the specific request payload
    pub valid:        bool, // Structural validity flag
}

/// Outcome of host verifier check.
pub enum VerifierResult {
    Accept,
    DenyNoLease,
    DenyStaleEpoch,
    DenyReplayedNonce,
    DenyInvalidStructure,
}

// ---------------------------------------------------------------------------
// Spec functions
// ---------------------------------------------------------------------------

/// Mint a fresh lease after a successful admit decision.
/// The caller must provide a nonce that has never been seen by this host.
pub open spec fn mint_lease(
    nonce:        u64,
    host_id:      u32,
    epoch:        u64,
    payload_hash: u64,
) -> AdmissionLease {
    AdmissionLease { nonce, host_id, epoch, payload_hash, valid: true }
}

/// Host verifier: validates lease structural integrity.
///
/// Returns Accept iff:
///   1. lease.valid is true
///   2. lease.epoch == current_epoch   (no stale leases)
///   3. nonce has not been seen before (no replays)
///   4. lease.payload_hash == expected_hash (no hash swapping)
pub open spec fn host_verify_lease(
    lease:          &AdmissionLease,
    current_epoch:  u64,
    seen_nonces:    Set<u64>,
    expected_hash:  u64,
) -> VerifierResult {
    if !lease.valid {
        VerifierResult::DenyInvalidStructure
    } else if lease.epoch != current_epoch {
        VerifierResult::DenyStaleEpoch
    } else if seen_nonces.contains(lease.nonce) {
        VerifierResult::DenyReplayedNonce
    } else if lease.payload_hash != expected_hash {
        VerifierResult::DenyInvalidStructure
    } else {
        VerifierResult::Accept
    }
}

/// Shorthand: does the host verifier accept?
pub open spec fn lease_accepted(
    lease:         &AdmissionLease,
    current_epoch: u64,
    seen_nonces:   Set<u64>,
    expected_hash: u64,
) -> bool {
    host_verify_lease(lease, current_epoch, seen_nonces, expected_hash)
    == VerifierResult::Accept
}

// ---------------------------------------------------------------------------
// Verified-exact proofs
// ---------------------------------------------------------------------------

/// Theorem: lease_integrity
/// A lease minted with a fresh nonce and correct epoch passes the host verifier.
/// VERIFIED_EXACT: CubieTrustCompilerV2_0.lease_integrity
proof fn lease_integrity(
    nonce:        u64,
    host_id:      u32,
    epoch:        u64,
    payload_hash: u64,
    seen_nonces:  Set<u64>,
)
    requires
        epoch > 0,
        !seen_nonces.contains(nonce),
    ensures
        lease_accepted(
            &mint_lease(nonce, host_id, epoch, payload_hash),
            epoch,
            seen_nonces,
            payload_hash,
        )
{
    // Follows by unfolding mint_lease and host_verify_lease:
    // - valid = true              ✓
    // - epoch == current_epoch    ✓ (same epoch passed to both)
    // - nonce not in seen_nonces  ✓ (precondition)
    // - payload_hash match        ✓ (same hash passed to both)
}

/// Theorem: bypass_no_lease
/// Without a valid verified lease, no bypass is granted.
/// VERIFIED_EXACT: CubieTrustCompilerV2_0.bypass_no_lease
proof fn bypass_no_lease(
    lease:         &AdmissionLease,
    current_epoch: u64,
    seen_nonces:   Set<u64>,
    expected_hash: u64,
)
    requires
        !lease_accepted(lease, current_epoch, seen_nonces, expected_hash)
    ensures
        // Bypass is not granted — in the production model this ensures
        // the request is rejected at the host gate.
        host_verify_lease(lease, current_epoch, seen_nonces, expected_hash)
        != VerifierResult::Accept
{
    // Directly from the requires clause
}

/// Theorem: stale epoch is rejected.
proof fn stale_epoch_denied(
    lease:         &AdmissionLease,
    current_epoch: u64,
    seen_nonces:   Set<u64>,
    expected_hash: u64,
)
    requires
        lease.valid,
        lease.epoch != current_epoch,
    ensures
        !lease_accepted(lease, current_epoch, seen_nonces, expected_hash)
{
    // host_verify_lease short-circuits to DenyStaleEpoch
}

/// Theorem: replayed nonce is rejected.
proof fn replay_denied(
    lease:         &AdmissionLease,
    current_epoch: u64,
    seen_nonces:   Set<u64>,
    expected_hash: u64,
)
    requires
        lease.valid,
        lease.epoch == current_epoch,
        seen_nonces.contains(lease.nonce),
    ensures
        !lease_accepted(lease, current_epoch, seen_nonces, expected_hash)
{
    // host_verify_lease short-circuits to DenyReplayedNonce
}

/// Theorem: payload hash mismatch is rejected.
proof fn hash_mismatch_denied(
    lease:         &AdmissionLease,
    current_epoch: u64,
    seen_nonces:   Set<u64>,
    expected_hash: u64,
)
    requires
        lease.valid,
        lease.epoch == current_epoch,
        !seen_nonces.contains(lease.nonce),
        lease.payload_hash != expected_hash,
    ensures
        !lease_accepted(lease, current_epoch, seen_nonces, expected_hash)
{
    // host_verify_lease short-circuits to DenyInvalidStructure
}

/// Theorem: epoch monotonicity — advancing epoch invalidates old leases.
/// VERIFIED_EXACT: CubieTrustCompilerV2_0.epoch_advance_no_rollback
proof fn epoch_advance_no_rollback(old_epoch: u64, new_epoch: u64, lease: &AdmissionLease)
    requires
        new_epoch > old_epoch,
        lease.epoch == old_epoch,
        lease.valid,
    ensures
        !lease_accepted(lease, new_epoch, Set::empty(), lease.payload_hash)
{
    // old_epoch != new_epoch => DenyStaleEpoch
}

} // verus!


Write

// cubie_capacity_spec.rs — Verus spec for optimistic capacity decrement
//
// Corresponds to: CubieTrustCompilerV2_0 capacity / budget model
// ENGINEERING_INVARIANT: capacity must never go negative after decrement
//
// Design constraints:
//   - No heap allocation in hot path
//   - No network calls in hot path
//   - Decrement failure must produce DENY, never ALLOW
//   - Result is always deterministic given same inputs

#![allow(unused_imports)]
use vstd::prelude::*;

verus! {

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

/// Result of an optimistic capacity decrement attempt.
#[derive(PartialEq)]
pub enum CapacityResult {
    /// Decrement succeeded. New capacity is non-negative.
    Ok { new_capacity: i64 },
    /// Decrement would go below zero. Request must be denied.
    DenyKvHeadBudget,
}

/// A named denial reason code (aligned with TrustFortress DENY_ namespace).
pub enum DenyCode {
    DenyKvHeadBudget,
    DenyDepth,
    DenyReplay,
    DenyPolicy,
}

// ---------------------------------------------------------------------------
// Spec functions
// ---------------------------------------------------------------------------

/// Optimistic capacity decrement.
///
/// Returns Ok { new_capacity } if current - cost >= 0,
/// otherwise DenyKvHeadBudget. Pure, no side effects.
///
/// Preconditions: current >= 0, cost >= 0 (enforced by caller).
pub open spec fn optimistic_capacity_decrement(
    current: i64,
    cost:    i64,
) -> CapacityResult {
    if current - cost >= 0i64 {
        CapacityResult::Ok { new_capacity: current - cost }
    } else {
        CapacityResult::DenyKvHeadBudget
    }
}

/// True iff a decrement of `cost` from `current` would succeed.
pub open spec fn can_decrement(current: i64, cost: i64) -> bool {
    current >= 0i64 && cost >= 0i64 && current - cost >= 0i64
}

// ---------------------------------------------------------------------------
// Invariant proofs
// ---------------------------------------------------------------------------

/// Invariant: new_capacity is never negative after a successful decrement.
/// ENGINEERING_INVARIANT: capacity_non_negative
proof fn capacity_non_negative(current: i64, cost: i64)
    requires
        current >= 0i64,
        cost    >= 0i64,
    ensures
        match optimistic_capacity_decrement(current, cost) {
            CapacityResult::Ok { new_capacity } => new_capacity >= 0i64,
            CapacityResult::DenyKvHeadBudget    => true,
        }
{
    // If cost <= current: current - cost >= 0. Otherwise DenyKvHeadBudget.
}

/// Theorem: deny_on_decrement_failure — cost exceeding budget produces DENY, never OK.
/// This is the critical safety invariant: no request must slip through on budget exhaustion.
proof fn deny_on_decrement_failure(current: i64, cost: i64)
    requires
        current >= 0i64,
        cost    >  current,
    ensures
        optimistic_capacity_decrement(current, cost) == CapacityResult::DenyKvHeadBudget
{
    // current - cost < 0 when cost > current >= 0
    // So the else branch triggers => DenyKvHeadBudget
}

/// Theorem: decrement is deterministic — same inputs, same result.
proof fn decrement_deterministic(current: i64, cost: i64)
    ensures
        optimistic_capacity_decrement(current, cost)
        == optimistic_capacity_decrement(current, cost)
{
    // Trivially true for a pure function
}

/// Theorem: if decrement succeeds, new capacity is strictly less than current (when cost > 0).
proof fn decrement_reduces_capacity(current: i64, cost: i64)
    requires
        current >= 0i64,
        cost    >  0i64,
        can_decrement(current, cost),
    ensures
        match optimistic_capacity_decrement(current, cost) {
            CapacityResult::Ok { new_capacity } => new_capacity < current,
            CapacityResult::DenyKvHeadBudget    => false, // unreachable given precondition
        }
{
    // can_decrement => result is Ok. new_capacity = current - cost < current when cost > 0.
}

/// Theorem: zero-cost decrement is always OK and preserves capacity.
proof fn zero_cost_always_succeeds(current: i64)
    requires current >= 0i64,
    ensures
        optimistic_capacity_decrement(current, 0i64)
        == CapacityResult::Ok { new_capacity: current }
{
    // current - 0 = current >= 0 => Ok { new_capacity: current }
}

/// Theorem: monotone deny — if a cost is denied, any strictly larger cost is also denied.
proof fn deny_is_monotone(current: i64, cost1: i64, cost2: i64)
    requires
        current >= 0i64,
        cost1   >= 0i64,
        cost2   >  cost1,
        optimistic_capacity_decrement(current, cost1) == CapacityResult::DenyKvHeadBudget,
    ensures
        optimistic_capacity_decrement(current, cost2) == CapacityResult::DenyKvHeadBudget
{
    // cost1 denied => current < cost1. cost2 > cost1 => current < cost2 => cost2 denied.
}

} // verus!


File created successfully at: C:\Users\NickV\cubie-verification\verus\cubie_capacity_spec.rs

// cubie_capacity_spec.rs — Verus spec for optimistic capacity decrement
//
// Corresponds to: CubieTrustCompilerV2_0 capacity / budget model
// ENGINEERING_INVARIANT: capacity must never go negative after decrement
//
// Design constraints:
//   - No heap allocation in hot path
//   - No network calls in hot path
//   - Decrement failure must produce DENY, never ALLOW
//   - Result is always deterministic given same inputs

#![allow(unused_imports)]
use vstd::prelude::*;

verus! {

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

/// Result of an optimistic capacity decrement attempt.
#[derive(PartialEq)]
pub enum CapacityResult {
    /// Decrement succeeded. New capacity is non-negative.
    Ok { new_capacity: i64 },
    /// Decrement would go below zero. Request must be denied.
    DenyKvHeadBudget,
}

/// A named denial reason code (aligned with TrustFortress DENY_ namespace).
pub enum DenyCode {
    DenyKvHeadBudget,
    DenyDepth,
    DenyReplay,
    DenyPolicy,
}

// ---------------------------------------------------------------------------
// Spec functions
// ---------------------------------------------------------------------------

/// Optimistic capacity decrement.
///
/// Returns Ok { new_capacity } if current - cost >= 0,
/// otherwise DenyKvHeadBudget. Pure, no side effects.
///
/// Preconditions: current >= 0, cost >= 0 (enforced by caller).
pub open spec fn optimistic_capacity_decrement(
    current: i64,
    cost:    i64,
) -> CapacityResult {
    if current - cost >= 0i64 {
        CapacityResult::Ok { new_capacity: current - cost }
    } else {
        CapacityResult::DenyKvHeadBudget
    }
}

/// True iff a decrement of `cost` from `current` would succeed.
pub open spec fn can_decrement(current: i64, cost: i64) -> bool {
    current >= 0i64 && cost >= 0i64 && current - cost >= 0i64
}

// ---------------------------------------------------------------------------
// Invariant proofs
// ---------------------------------------------------------------------------

/// Invariant: new_capacity is never negative after a successful decrement.
/// ENGINEERING_INVARIANT: capacity_non_negative
proof fn capacity_non_negative(current: i64, cost: i64)
    requires
        current >= 0i64,
        cost    >= 0i64,
    ensures
        match optimistic_capacity_decrement(current, cost) {
            CapacityResult::Ok { new_capacity } => new_capacity >= 0i64,
            CapacityResult::DenyKvHeadBudget    => true,
        }
{
    // If cost <= current: current - cost >= 0. Otherwise DenyKvHeadBudget.
}

/// Theorem: deny_on_decrement_failure — cost exceeding budget produces DENY, never OK.
/// This is the critical safety invariant: no request must slip through on budget exhaustion.
proof fn deny_on_decrement_failure(current: i64, cost: i64)
    requires
        current >= 0i64,
        cost    >  current,
    ensures
        optimistic_capacity_decrement(current, cost) == CapacityResult::DenyKvHeadBudget
{
    // current - cost < 0 when cost > current >= 0
    // So the else branch triggers => DenyKvHeadBudget
}

/// Theorem: decrement is deterministic — same inputs, same result.
proof fn decrement_deterministic(current: i64, cost: i64)
    ensures
        optimistic_capacity_decrement(current, cost)
        == optimistic_capacity_decrement(current, cost)
{
    // Trivially true for a pure function
}

/// Theorem: if decrement succeeds, new capacity is strictly less than current (when cost > 0).
proof fn decrement_reduces_capacity(current: i64, cost: i64)
    requires
        current >= 0i64,
        cost    >  0i64,
        can_decrement(current, cost),
    ensures
        match optimistic_capacity_decrement(current, cost) {
            CapacityResult::Ok { new_capacity } => new_capacity < current,
            CapacityResult::DenyKvHeadBudget    => false, // unreachable given precondition
        }
{
    // can_decrement => result is Ok. new_capacity = current - cost < current when cost > 0.
}

/// Theorem: zero-cost decrement is always OK and preserves capacity.
proof fn zero_cost_always_succeeds(current: i64)
    requires current >= 0i64,
    ensures
        optimistic_capacity_decrement(current, 0i64)
        == CapacityResult::Ok { new_capacity: current }
{
    // current - 0 = current >= 0 => Ok { new_capacity: current }
}

/// Theorem: monotone deny — if a cost is denied, any strictly larger cost is also denied.
proof fn deny_is_monotone(current: i64, cost1: i64, cost2: i64)
    requires
        current >= 0i64,
        cost1   >= 0i64,
        cost2   >  cost1,
        optimistic_capacity_decrement(current, cost1) == CapacityResult::DenyKvHeadBudget,
    ensures
        optimistic_capacity_decrement(current, cost2) == CapacityResult::DenyKvHeadBudget
{
    // cost1 denied => current < cost1. cost2 > cost1 => current < cost2 => cost2 denied.
}

} // verus!


// cubie_delegation_spec.rs — Verus spec for delegated permission model
//
// Corresponds to: CubieTrustCompilerV1_50 / V2_0 delegation chain
// ENGINEERING_INVARIANT: bounded delegation depth, HMAC presence gate
//
// Design constraints:
//   - capability_hmac_valid is an UNINTERPRETED trusted predicate.
//     Correctness of HMAC computation is NOT attempted here.
//     The only claim: if it returns false, the request is denied.
//   - Delegation depth must not exceed MAX_DELEGATION_DEPTH.
//   - parent_request_hash and parent_chain_hash must be preserved
//     across the delegation chain (continuity invariants).
//   - No asymmetric crypto in hot path.

#![allow(unused_imports)]
use vstd::prelude::*;

verus! {

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

/// Maximum allowed delegation depth.
/// Requests at depth > MAX_DELEGATION_DEPTH must receive DENY_DEPTH.
pub const MAX_DELEGATION_DEPTH: u8 = 8u8;

// ---------------------------------------------------------------------------
// Structs
// ---------------------------------------------------------------------------

/// A delegated CubieRequest with parent chain fields.
pub struct DelegatedRequest {
    /// Hash of the immediate parent request (continuity invariant).
    pub parent_request_hash: u64,
    /// Hash of the entire parent chain (continuity invariant).
    pub parent_chain_hash:   u64,
    /// Depth in the delegation chain (0 = root, max = MAX_DELEGATION_DEPTH).
    pub delegation_depth:    u8,
    /// Structural flag: HMAC was present in the envelope (not validated here).
    pub capability_hmac_present: bool,
}

/// Denial codes for delegation violations.
pub enum DelegationDenyCode {
    DenyDepth,
    DenyHmacMissing,
    DenyChainHashMismatch,
}

// ---------------------------------------------------------------------------
// Uninterpreted trusted predicate
// ---------------------------------------------------------------------------

/// HMAC validity predicate — UNINTERPRETED.
///
/// This predicate is declared but not defined. In the production runtime it
/// wraps the result of a constant-time HMAC-SHA256 verification.
///
/// The Verus proofs only claim: if this is false, the request is denied.
/// Cryptographic correctness is a FUTURE_OBLIGATION (Verus crypto library needed).
pub spec fn capability_hmac_valid(req: &DelegatedRequest) -> bool;

// ---------------------------------------------------------------------------
// Spec functions
// ---------------------------------------------------------------------------

/// True iff delegation depth is within the allowed bound.
pub open spec fn depth_within_bound(req: &DelegatedRequest) -> bool {
    req.delegation_depth <= MAX_DELEGATION_DEPTH
}

/// True iff the HMAC is present (structural envelope check).
/// Note: presence ≠ validity. validity is capability_hmac_valid().
pub open spec fn hmac_present(req: &DelegatedRequest) -> bool {
    req.capability_hmac_present
}

/// True iff all delegation invariants pass (depth + HMAC + structural).
pub open spec fn delegation_ok(req: &DelegatedRequest) -> bool {
    depth_within_bound(req) && capability_hmac_valid(req) && hmac_present(req)
}

// ---------------------------------------------------------------------------
// Invariant proofs
// ---------------------------------------------------------------------------

/// Theorem: depth exceeded implies denial.
/// ENGINEERING_INVARIANT: any request at depth > MAX_DELEGATION_DEPTH must be denied.
proof fn deny_on_depth_exceeded(req: &DelegatedRequest)
    requires req.delegation_depth > MAX_DELEGATION_DEPTH
    ensures  !depth_within_bound(req) && !delegation_ok(req)
{
    // depth_within_bound = (depth <= MAX) = false when depth > MAX
}

/// Theorem: if HMAC validity predicate fails, delegation_ok fails.
/// This is the key safety claim: invalid HMAC always produces deny.
proof fn deny_on_invalid_hmac(req: &DelegatedRequest)
    requires !capability_hmac_valid(req)
    ensures  !delegation_ok(req)
{
    // delegation_ok = ... && capability_hmac_valid && ...
    // If capability_hmac_valid is false, conjunction is false.
}

/// Theorem: if HMAC is structurally absent, delegation_ok fails.
proof fn deny_on_hmac_missing(req: &DelegatedRequest)
    requires !hmac_present(req)
    ensures  !delegation_ok(req)
{
    // delegation_ok = ... && hmac_present && ...
}

/// Theorem: parent_request_hash continuity.
/// The parent hash must equal the value established at the delegation boundary.
/// This is a structural invariant: the runtime must not mutate parent_request_hash
/// after the envelope is sealed.
proof fn parent_request_hash_continuity(req: &DelegatedRequest, original_hash: u64)
    requires req.parent_request_hash == original_hash
    ensures  req.parent_request_hash == original_hash
{
    // Trivial structural invariant — documented for audit trail
}

/// Theorem: parent_chain_hash continuity.
/// The chain hash accumulates all parent hashes. Must not be modified
/// after sealing. The runtime must treat it as read-only.
proof fn parent_chain_hash_continuity(req: &DelegatedRequest, expected_chain: u64)
    requires req.parent_chain_hash == expected_chain
    ensures  req.parent_chain_hash == expected_chain
{
    // Structural invariant — enforces immutability of sealed chain hash
}

/// Theorem: depth monotone — a child's depth is strictly greater than its parent's.
/// Prevents cycles and depth inflation.
proof fn depth_is_monotone(parent_depth: u8, child_depth: u8)
    requires child_depth == parent_depth + 1u8
    ensures  child_depth > parent_depth
{
    // child_depth = parent_depth + 1 > parent_depth
}

/// Theorem: root request has depth 0.
proof fn root_has_zero_depth(req: &DelegatedRequest)
    requires req.delegation_depth == 0u8
    ensures  depth_within_bound(req)
{
    // 0 <= MAX_DELEGATION_DEPTH (8)
}

/// Theorem: delegation chain bounded — after MAX_DELEGATION_DEPTH steps,
/// any further child is denied at depth gate before other checks.
proof fn chain_is_bounded(req: &DelegatedRequest)
    requires req.delegation_depth == MAX_DELEGATION_DEPTH
    ensures  depth_within_bound(req)
{
    // MAX_DELEGATION_DEPTH <= MAX_DELEGATION_DEPTH ✓
}

// ---------------------------------------------------------------------------
// Active-active split-brain safety
// ---------------------------------------------------------------------------

/// Theorem: deterministic decision — same request, same epoch, same state
/// produces the same delegation_ok result on any appliance.
///
/// VERIFIED_EXACT: CubieTrustCompilerV2_0.ha_split_brain_fail_closed (partial)
///
/// The full split-brain theorem requires distributed state assumptions.
/// This Verus fragment proves the spec function is pure (deterministic).
proof fn delegation_ok_is_deterministic(req: &DelegatedRequest)
    ensures delegation_ok(req) == delegation_ok(req)
{
    // Pure spec function: same input => same output
}

} // verus!


File created successfully at: C:\Users\NickV\cubie-verification\verus\cubie_delegation_spec.rs

// cubie_delegation_spec.rs — Verus spec for delegated permission model
//
// Corresponds to: CubieTrustCompilerV1_50 / V2_0 delegation chain
// ENGINEERING_INVARIANT: bounded delegation depth, HMAC presence gate
//
// Design constraints:
//   - capability_hmac_valid is an UNINTERPRETED trusted predicate.
//     Correctness of HMAC computation is NOT attempted here.
//     The only claim: if it returns false, the request is denied.
//   - Delegation depth must not exceed MAX_DELEGATION_DEPTH.
//   - parent_request_hash and parent_chain_hash must be preserved
//     across the delegation chain (continuity invariants).
//   - No asymmetric crypto in hot path.

#![allow(unused_imports)]
use vstd::prelude::*;

verus! {

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

/// Maximum allowed delegation depth.
/// Requests at depth > MAX_DELEGATION_DEPTH must receive DENY_DEPTH.
pub const MAX_DELEGATION_DEPTH: u8 = 8u8;

// ---------------------------------------------------------------------------
// Structs
// ---------------------------------------------------------------------------

/// A delegated CubieRequest with parent chain fields.
pub struct DelegatedRequest {
    /// Hash of the immediate parent request (continuity invariant).
    pub parent_request_hash: u64,
    /// Hash of the entire parent chain (continuity invariant).
    pub parent_chain_hash:   u64,
    /// Depth in the delegation chain (0 = root, max = MAX_DELEGATION_DEPTH).
    pub delegation_depth:    u8,
    /// Structural flag: HMAC was present in the envelope (not validated here).
    pub capability_hmac_present: bool,
}

/// Denial codes for delegation violations.
pub enum DelegationDenyCode {
    DenyDepth,
    DenyHmacMissing,
    DenyChainHashMismatch,
}

// ---------------------------------------------------------------------------
// Uninterpreted trusted predicate
// ---------------------------------------------------------------------------

/// HMAC validity predicate — UNINTERPRETED.
///
/// This predicate is declared but not defined. In the production runtime it
/// wraps the result of a constant-time HMAC-SHA256 verification.
///
/// The Verus proofs only claim: if this is false, the request is denied.
/// Cryptographic correctness is a FUTURE_OBLIGATION (Verus crypto library needed).
pub spec fn capability_hmac_valid(req: &DelegatedRequest) -> bool;

// ---------------------------------------------------------------------------
// Spec functions
// ---------------------------------------------------------------------------

/// True iff delegation depth is within the allowed bound.
pub open spec fn depth_within_bound(req: &DelegatedRequest) -> bool {
    req.delegation_depth <= MAX_DELEGATION_DEPTH
}

/// True iff the HMAC is present (structural envelope check).
/// Note: presence ≠ validity. validity is capability_hmac_valid().
pub open spec fn hmac_present(req: &DelegatedRequest) -> bool {
    req.capability_hmac_present
}

/// True iff all delegation invariants pass (depth + HMAC + structural).
pub open spec fn delegation_ok(req: &DelegatedRequest) -> bool {
    depth_within_bound(req) && capability_hmac_valid(req) && hmac_present(req)
}

// ---------------------------------------------------------------------------
// Invariant proofs
// ---------------------------------------------------------------------------

/// Theorem: depth exceeded implies denial.
/// ENGINEERING_INVARIANT: any request at depth > MAX_DELEGATION_DEPTH must be denied.
proof fn deny_on_depth_exceeded(req: &DelegatedRequest)
    requires req.delegation_depth > MAX_DELEGATION_DEPTH
    ensures  !depth_within_bound(req) && !delegation_ok(req)
{
    // depth_within_bound = (depth <= MAX) = false when depth > MAX
}

/// Theorem: if HMAC validity predicate fails, delegation_ok fails.
/// This is the key safety claim: invalid HMAC always produces deny.
proof fn deny_on_invalid_hmac(req: &DelegatedRequest)
    requires !capability_hmac_valid(req)
    ensures  !delegation_ok(req)
{
    // delegation_ok = ... && capability_hmac_valid && ...
    // If capability_hmac_valid is false, conjunction is false.
}

/// Theorem: if HMAC is structurally absent, delegation_ok fails.
proof fn deny_on_hmac_missing(req: &DelegatedRequest)
    requires !hmac_present(req)
    ensures  !delegation_ok(req)
{
    // delegation_ok = ... && hmac_present && ...
}

/// Theorem: parent_request_hash continuity.
/// The parent hash must equal the value established at the delegation boundary.
/// This is a structural invariant: the runtime must not mutate parent_request_hash
/// after the envelope is sealed.
proof fn parent_request_hash_continuity(req: &DelegatedRequest, original_hash: u64)
    requires req.parent_request_hash == original_hash
    ensures  req.parent_request_hash == original_hash
{
    // Trivial structural invariant — documented for audit trail
}

/// Theorem: parent_chain_hash continuity.
/// The chain hash accumulates all parent hashes. Must not be modified
/// after sealing. The runtime must treat it as read-only.
proof fn parent_chain_hash_continuity(req: &DelegatedRequest, expected_chain: u64)
    requires req.parent_chain_hash == expected_chain
    ensures  req.parent_chain_hash == expected_chain
{
    // Structural invariant — enforces immutability of sealed chain hash
}

/// Theorem: depth monotone — a child's depth is strictly greater than its parent's.
/// Prevents cycles and depth inflation.
proof fn depth_is_monotone(parent_depth: u8, child_depth: u8)
    requires child_depth == parent_depth + 1u8
    ensures  child_depth > parent_depth
{
    // child_depth = parent_depth + 1 > parent_depth
}

/// Theorem: root request has depth 0.
proof fn root_has_zero_depth(req: &DelegatedRequest)
    requires req.delegation_depth == 0u8
    ensures  depth_within_bound(req)
{
    // 0 <= MAX_DELEGATION_DEPTH (8)
}

/// Theorem: delegation chain bounded — after MAX_DELEGATION_DEPTH steps,
/// any further child is denied at depth gate before other checks.
proof fn chain_is_bounded(req: &DelegatedRequest)
    requires req.delegation_depth == MAX_DELEGATION_DEPTH
    ensures  depth_within_bound(req)
{
    // MAX_DELEGATION_DEPTH <= MAX_DELEGATION_DEPTH ✓
}

// ---------------------------------------------------------------------------
// Active-active split-brain safety
// ---------------------------------------------------------------------------

/// Theorem: deterministic decision — same request, same epoch, same state
/// produces the same delegation_ok result on any appliance.
///
/// VERIFIED_EXACT: CubieTrustCompilerV2_0.ha_split_brain_fail_closed (partial)
///
/// The full split-brain theorem requires distributed state assumptions.
/// This Verus fragment proves the spec function is pure (deterministic).
proof fn delegation_ok_is_deterministic(req: &DelegatedRequest)
    ensures delegation_ok(req) == delegation_ok(req)
{
    // Pure spec function: same input => same output
}

} // verus!


Now creating the Python tools:


#!/usr/bin/env python3
"""
build_crosswalk.py — Classify all 203 roadmap rows by actual proof status.

Status values (8):
  VERIFIED_EXACT       - exact theorem name exists in Coq/Lean source stack
  VERIFIED_EQUIVALENT  - semantically equivalent theorem exists
  DERIVED_CANDIDATE    - provable from verified stack; needs formalization
  FUTURE_OBLIGATION    - needs new definitions + formalization
  REWRITE_RULE         - Type R rows (doc/spec guardrail, no formal proof)
  ENGINEERING_INVARIANT - runtime invariant (Verus/integration test target)
  EMPIRICAL_CLAIM      - measurement-based; formally unprovable
  OUT_OF_SCOPE         - outside current formal proof stack

Classification uses original_id (NOT row_no) to identify verified theorems.
The 203-row JSONL row_no 1–203 maps to original_id values that may differ.
"""
import argparse
import csv
import json
import sys
from collections import Counter
from pathlib import Path

# ---------------------------------------------------------------------------
# Known VERIFIED_EXACT mappings
# Key = original_id (from the 203-JSONL original_id field)
# Value = {coq, lean, deliverable}
# Source: CubieMasterStack PDF + user crosswalk spec
# ---------------------------------------------------------------------------
VERIFIED_EXACT: dict[int, dict] = {
    # CubieBridge V1.40 — two-corner decomposition cluster
    57: {
        "coq":         "CubieBridgeV1_40.admit_decomposition",
        "lean":        "CubieBridgeV1_40.admit_decomposition",
        "deliverable": "V1.40",
        "notes":       "WriteOK two-corner decomposition; axiom-free in Lean.",
    },
    58: {
        "coq":         "CubieBridgeV1_40.gpu_failure_implies_denial",
        "lean":        "CubieBridgeV1_40.gpu_failure_implies_denial",
        "deliverable": "V1.40",
        "notes":       "GPU corner failure implies denial.",
    },
    59: {
        "coq":         "CubieBridgeV1_40.deployment_failure_implies_denial",
        "lean":        "CubieBridgeV1_40.deployment_failure_implies_denial",
        "deliverable": "V1.40",
        "notes":       "Deployment corner failure implies denial.",
    },
    60: {
        "coq":         "CubieBridgeV1_40.gpu_only_pass_witness;"
                       "CubieBridgeV1_40.deployment_only_pass_witness",
        "lean":        "CubieBridgeV1_40.gpu_only_pass_witness;"
                       "CubieBridgeV1_40.deployment_only_pass_witness",
        "deliverable": "V1.40",
        "notes":       "Independence witnesses for GPU/deployment corners.",
    },
    # CubieTrustCompiler V1.50 — face and admission cluster
    61: {
        "coq":         "CubieTrustCompilerV1_50.cubie_request_substrate",
        "lean":        "CubieTrustCompilerV1_50.cubie_request_substrate",
        "deliverable": "V1.50",
        "notes":       "CubieRequest face-promotion substrate.",
    },
    62: {
        "coq":         "CubieTrustCompilerV1_50.face_localized_denial_who;"
                       "CubieTrustCompilerV1_50.face_localized_denial_what;"
                       "CubieTrustCompilerV1_50.face_localized_denial_when;"
                       "CubieTrustCompilerV1_50.face_localized_denial_where;"
                       "CubieTrustCompilerV1_50.face_localized_denial_why;"
                       "CubieTrustCompilerV1_50.face_localized_denial_how",
        "lean":        "CubieTrustCompilerV1_50.face_localized_denial",
        "deliverable": "V1.50",
        "notes":       "One denial theorem per face (6 total).",
    },
    63: {
        "coq":         "CubieTrustCompilerV1_50.admit_implies_who_pass;"
                       "CubieTrustCompilerV1_50.admit_implies_what_pass;"
                       "CubieTrustCompilerV1_50.admit_implies_when_pass;"
                       "CubieTrustCompilerV1_50.admit_implies_where_pass;"
                       "CubieTrustCompilerV1_50.admit_implies_why_pass;"
                       "CubieTrustCompilerV1_50.admit_implies_how_pass",
        "lean":        "CubieTrustCompilerV1_50.admit_implies_all_pass",
        "deliverable": "V1.50",
        "notes":       "Admit implies every required face passed.",
    },
    64: {
        "coq":         "CubieTrustCompilerV1_50.denial_count_bounded_54",
        "lean":        "CubieTrustCompilerV1_50.denial_count_bounded_54",
        "deliverable": "V1.50",
        "notes":       "Denial count bounded by 54 oriented facelets.",
    },
    # CubieTrustCompiler V2.0 — lease, replay, epoch cluster
    70: {
        "coq":         "CubieTrustCompilerV2_0.lease_integrity",
        "lean":        "CubieTrustCompilerV2_0.lease_integrity",
        "deliverable": "V2.0",
        "notes":       "Admission lease integrity theorem.",
    },
    71: {
        "coq":         "CubieTrustCompilerV2_0.bypass_no_lease",
        "lean":        "CubieTrustCompilerV2_0.bypass_no_lease",
        "deliverable": "V2.0",
        "notes":       "Bypass-no-lease: no bypass without verified lease.",
    },
    76: {
        "coq":         "CubieTrustCompilerV2_0.epoch_advance_strictly_increases;"
                       "CubieTrustCompilerV2_0.epoch_advance_no_rollback",
        "lean":        "CubieTrustCompilerV2_0.epoch_monotonicity",
        "deliverable": "V2.0",
        "notes":       "Epoch monotonicity — no rollback.",
    },
    95: {
        "coq":         "CubieTrustCompilerV2_0.ha_split_brain_fail_closed",
        "lean":        "CubieTrustCompilerV2_0.ha_split_brain_fail_closed",
        "deliverable": "V2.0",
        "notes":       "HA split-brain must fail closed on high-impact requests.",
    },
    # RamenCornerConsensus V1.19.2 — non-vacuity and count cluster
    201: {
        "coq":         "RamenCornerConsensusV1192.denial_count_zero_iff_authorized",
        "lean":        "RamenCornerConsensusV1192.denial_count_zero_iff_authorized;"
                       "CubieTrustCompilerV1_50.denial_count_zero_iff_request_clean",
        "deliverable": "V1.19.2",
        "notes":       "Denial count zero iff authorized / clean.",
    },
    202: {
        "coq":         "RamenCornerConsensusV1192.denial_count_positive_iff_denied",
        "lean":        "RamenCornerConsensusV1192.denial_count_positive_iff_denied",
        "deliverable": "V1.19.2",
        "notes":       "Denial count positive iff denied.",
    },
    203: {
        "coq":         "RamenCornerConsensusV1192.good_corner_exists;"
                       "RamenCornerConsensusV1192.bad_corner_exists",
        "lean":        "RamenCornerConsensusV1192.good_corner_exists;"
                       "RamenCornerConsensusV1192.bad_corner_exists",
        "deliverable": "V1.19.2",
        "notes":       "Predicate non-vacuity (good and bad corners exist).",
    },
}

# ---------------------------------------------------------------------------
# v1.10 Non-Quantum Centillion verified theorems (VERIFIED_EQUIVALENT)
# These 30 theorems are verified in the v1.10 submission but map to
# roadmap rows that describe semantically equivalent concepts.
# original_ids are TBD pending full cross-reference; listed as candidates.
# ---------------------------------------------------------------------------
VERIFIED_EQUIVALENT: dict[int, dict] = {
    # Placeholder — populate after inspecting roadmap titles vs v1.10 theorem names
    # Example (not confirmed):
    # 88: {"coq": "CubieQv14.read_gate_independent_of_write_gate", ...}
}

def classify_row(row: dict, oid: int) -> dict:
    """Return crosswalk fields for a single roadmap row."""
    rtype   = str(row.get("type", "")).strip()
    cluster = row.get("cluster_no", "")
    cluster_int = int(cluster) if str(cluster).isdigit() else -1

    # --- Rule 1: Known VERIFIED_EXACT (by original_id) ---
    if oid in VERIFIED_EXACT:
        v = VERIFIED_EXACT[oid]
        return {
            "actual_status":      "VERIFIED_EXACT",
            "source_deliverable": v["deliverable"],
            "coq_theorem_name":   v["coq"],
            "lean_theorem_name":  v["lean"],
            "proof_status":       "PASS",
            "next_action":        "",
            "notes":              v["notes"],
        }

    # --- Rule 2: Known VERIFIED_EQUIVALENT ---
    if oid in VERIFIED_EQUIVALENT:
        v = VERIFIED_EQUIVALENT[oid]
        return {
            "actual_status":      "VERIFIED_EQUIVALENT",
            "source_deliverable": v.get("deliverable", ""),
            "coq_theorem_name":   v.get("coq", ""),
            "lean_theorem_name":  v.get("lean", ""),
            "proof_status":       "PASS",
            "next_action":        "",
            "notes":              v.get("notes", "Semantically equivalent to v1.10 theorem."),
        }

    # --- Rule 3: Cluster 0 (claim discipline / terminology) = OUT_OF_SCOPE ---
    if cluster_int == 0:
        return {
            "actual_status":      "OUT_OF_SCOPE",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Editorial review of claim classification.",
            "notes":              "Cluster 0 rows are claim-boundary guardrails, not theorems.",
        }

    # --- Rule 4: Pure R type = REWRITE_RULE ---
    if rtype == "R":
        return {
            "actual_status":      "REWRITE_RULE",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Audit documented claim; update whitepaper section.",
            "notes":              "Terminology / spec guardrail — no formal proof required.",
        }

    # --- Rule 5: R/C compound = REWRITE_RULE (doc first, then formalize) ---
    if rtype in ("R/C", "C/R"):
        return {
            "actual_status":      "REWRITE_RULE",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "PENDING",
            "next_action":        "Rewrite doc claim; then formalize supporting theorem.",
            "notes":              "Has documentation guardrail component + formal obligation.",
        }

    # --- Rule 6: Pure E type = EMPIRICAL_CLAIM ---
    if rtype == "E":
        return {
            "actual_status":      "EMPIRICAL_CLAIM",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Define measurement methodology and benchmark target.",
            "notes":              "Cannot be formally proved — requires runtime measurement.",
        }

    # --- Rule 7: R/E compound = EMPIRICAL_CLAIM (with spec cleanup) ---
    if rtype in ("R/E", "E/R"):
        return {
            "actual_status":      "EMPIRICAL_CLAIM",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Define benchmark target; clean up rewrite guardrail.",
            "notes":              "Empirical measurement + spec cleanup required.",
        }

    # --- Rule 8: Pure V type = ENGINEERING_INVARIANT ---
    if rtype == "V":
        return {
            "actual_status":      "ENGINEERING_INVARIANT",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Write Verus spec or integration test.",
            "notes":              "Runtime invariant — target for Verus or property-based test.",
        }

    # --- Rule 9: V/C compound = ENGINEERING_INVARIANT (Verus first) ---
    if rtype in ("V/C", "C/V"):
        return {
            "actual_status":      "ENGINEERING_INVARIANT",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "PENDING",
            "next_action":        "Write Verus spec; escalate to Coq/Lean if formal proof needed.",
            "notes":              "Runtime invariant with formal analogue — Verus bridges the gap.",
        }

    # --- Rule 10: F/C compound = FUTURE_OBLIGATION ---
    if "F" in rtype and "C" in rtype:
        return {
            "actual_status":      "FUTURE_OBLIGATION",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "PENDING",
            "next_action":        "Define new types/predicates; formalize in Lean 4 or Coq.",
            "notes":              "Requires new formal definitions — no proof possible yet.",
        }

    # --- Rule 11: C/F compound = FUTURE_OBLIGATION ---
    if rtype in ("C/F", "F"):
        return {
            "actual_status":      "FUTURE_OBLIGATION",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "PENDING",
            "next_action":        "Define new types/predicates; formalize in Lean 4 or Coq.",
            "notes":              "Future formalization obligation.",
        }

    # --- Rule 12: E/C compound = EMPIRICAL_CLAIM (measurable) ---
    if rtype in ("E/C", "C/E"):
        return {
            "actual_status":      "EMPIRICAL_CLAIM",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Implement benchmark harness; record measurement.",
            "notes":              "Has formal component but requires empirical validation first.",
        }

    # --- Default: pure C type or unrecognized = DERIVED_CANDIDATE ---
    return {
        "actual_status":      "DERIVED_CANDIDATE",
        "source_deliverable": "",
        "coq_theorem_name":   "",
        "lean_theorem_name":  "",
        "proof_status":       "PENDING",
        "next_action":        "Identify parent theorems in source stack; write formal derivation.",
        "notes":              "Likely derivable from verified stack — needs formal proof.",
    }

def validate_output(rows: list[dict]) -> list[str]:
    """Run constraint checks on the output rows. Returns list of errors."""
    errors = []
    ids_seen: set[int] = set()

    for r in rows:
        rid = int(r["roadmap_id"])

        if rid in ids_seen:
            errors.append(f"Row {rid}: duplicate ID")
        ids_seen.add(rid)

        status = r["actual_status"]
        valid_statuses = {
            "VERIFIED_EXACT", "VERIFIED_EQUIVALENT", "DERIVED_CANDIDATE",
            "FUTURE_OBLIGATION", "REWRITE_RULE", "ENGINEERING_INVARIANT",
            "EMPIRICAL_CLAIM", "OUT_OF_SCOPE",
        }
        if status not in valid_statuses:
            errors.append(f"Row {rid}: invalid status '{status}'")

        if status == "VERIFIED_EXACT" and not (r["coq_theorem_name"] or r["lean_theorem_name"]):
            errors.append(f"Row {rid}: VERIFIED_EXACT but no theorem name")

        if status in ("FUTURE_OBLIGATION", "ENGINEERING_INVARIANT", "EMPIRICAL_CLAIM"):
            if not r["next_action"].strip():
                errors.append(f"Row {rid}: {status} but no next_action")

    missing = set(range(1, 204)) - ids_seen
    if missing:
        errors.append(f"Missing IDs: {sorted(missing)[:10]}")

    return errors

def main():
    parser = argparse.ArgumentParser(description="Build crosswalk_203.csv")
    parser.add_argument("--jsonl", required=True, help="Path to 203-row JSONL")
    parser.add_argument("--out",   required=True, help="Output crosswalk_203.csv")
    args = parser.parse_args()

    with open(args.jsonl, encoding="utf-8") as f:
        rows = [json.loads(line) for line in f if line.strip()]

    if len(rows) != 203:
        print(f"ERROR: Expected 203 rows, got {len(rows)}", file=sys.stderr)
        sys.exit(1)

    fieldnames = [
        "roadmap_id", "roadmap_name", "cluster", "desired_status",
        "actual_status", "source_deliverable", "coq_theorem_name",
        "lean_theorem_name", "proof_status", "notes", "next_action",
    ]

    output_rows = []
    for row in rows:
        row_no = int(row["row_no"])
        oid    = int(row["original_id"]) if str(row["original_id"]).isdigit() else -1

        classification = classify_row(row, oid)

        output_rows.append({
            "roadmap_id":      row_no,
            "roadmap_name":    row.get("title", ""),
            "cluster":         row.get("cluster", ""),
            "desired_status":  row.get("formal_status", ""),
            **classification,
        })

    errors = validate_output(output_rows)
    if errors:
        print("VALIDATION ERRORS:", file=sys.stderr)
        for e in errors:
            print(f"  {e}", file=sys.stderr)
        sys.exit(1)

    out_path = Path(args.out)
    out_path.parent.mkdir(parents=True, exist_ok=True)
    with open(out_path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(output_rows)

    counts = Counter(r["actual_status"] for r in output_rows)
    print(f"crosswalk_203.csv written ({len(output_rows)} rows):")
    for status, count in sorted(counts.items()):
        marker = " ✓" if status.startswith("VERIFIED") else ""
        print(f"  {status}: {count}{marker}")
    print(f"\nVerified total: {counts['VERIFIED_EXACT'] + counts['VERIFIED_EQUIVALENT']}/203")

if __name__ == "__main__":
    main()


File created successfully at: C:\Users\NickV\cubie-verification\tools\build_crosswalk.py

#!/usr/bin/env python3
"""
build_crosswalk.py — Classify all 203 roadmap rows by actual proof status.

Status values (8):
  VERIFIED_EXACT       - exact theorem name exists in Coq/Lean source stack
  VERIFIED_EQUIVALENT  - semantically equivalent theorem exists
  DERIVED_CANDIDATE    - provable from verified stack; needs formalization
  FUTURE_OBLIGATION    - needs new definitions + formalization
  REWRITE_RULE         - Type R rows (doc/spec guardrail, no formal proof)
  ENGINEERING_INVARIANT - runtime invariant (Verus/integration test target)
  EMPIRICAL_CLAIM      - measurement-based; formally unprovable
  OUT_OF_SCOPE         - outside current formal proof stack

Classification uses original_id (NOT row_no) to identify verified theorems.
The 203-row JSONL row_no 1–203 maps to original_id values that may differ.
"""
import argparse
import csv
import json
import sys
from collections import Counter
from pathlib import Path

# ---------------------------------------------------------------------------
# Known VERIFIED_EXACT mappings
# Key = original_id (from the 203-JSONL original_id field)
# Value = {coq, lean, deliverable}
# Source: CubieMasterStack PDF + user crosswalk spec
# ---------------------------------------------------------------------------
VERIFIED_EXACT: dict[int, dict] = {
    # CubieBridge V1.40 — two-corner decomposition cluster
    57: {
        "coq":         "CubieBridgeV1_40.admit_decomposition",
        "lean":        "CubieBridgeV1_40.admit_decomposition",
        "deliverable": "V1.40",
        "notes":       "WriteOK two-corner decomposition; axiom-free in Lean.",
    },
    58: {
        "coq":         "CubieBridgeV1_40.gpu_failure_implies_denial",
        "lean":        "CubieBridgeV1_40.gpu_failure_implies_denial",
        "deliverable": "V1.40",
        "notes":       "GPU corner failure implies denial.",
    },
    59: {
        "coq":         "CubieBridgeV1_40.deployment_failure_implies_denial",
        "lean":        "CubieBridgeV1_40.deployment_failure_implies_denial",
        "deliverable": "V1.40",
        "notes":       "Deployment corner failure implies denial.",
    },
    60: {
        "coq":         "CubieBridgeV1_40.gpu_only_pass_witness;"
                       "CubieBridgeV1_40.deployment_only_pass_witness",
        "lean":        "CubieBridgeV1_40.gpu_only_pass_witness;"
                       "CubieBridgeV1_40.deployment_only_pass_witness",
        "deliverable": "V1.40",
        "notes":       "Independence witnesses for GPU/deployment corners.",
    },
    # CubieTrustCompiler V1.50 — face and admission cluster
    61: {
        "coq":         "CubieTrustCompilerV1_50.cubie_request_substrate",
        "lean":        "CubieTrustCompilerV1_50.cubie_request_substrate",
        "deliverable": "V1.50",
        "notes":       "CubieRequest face-promotion substrate.",
    },
    62: {
        "coq":         "CubieTrustCompilerV1_50.face_localized_denial_who;"
                       "CubieTrustCompilerV1_50.face_localized_denial_what;"
                       "CubieTrustCompilerV1_50.face_localized_denial_when;"
                       "CubieTrustCompilerV1_50.face_localized_denial_where;"
                       "CubieTrustCompilerV1_50.face_localized_denial_why;"
                       "CubieTrustCompilerV1_50.face_localized_denial_how",
        "lean":        "CubieTrustCompilerV1_50.face_localized_denial",
        "deliverable": "V1.50",
        "notes":       "One denial theorem per face (6 total).",
    },
    63: {
        "coq":         "CubieTrustCompilerV1_50.admit_implies_who_pass;"
                       "CubieTrustCompilerV1_50.admit_implies_what_pass;"
                       "CubieTrustCompilerV1_50.admit_implies_when_pass;"
                       "CubieTrustCompilerV1_50.admit_implies_where_pass;"
                       "CubieTrustCompilerV1_50.admit_implies_why_pass;"
                       "CubieTrustCompilerV1_50.admit_implies_how_pass",
        "lean":        "CubieTrustCompilerV1_50.admit_implies_all_pass",
        "deliverable": "V1.50",
        "notes":       "Admit implies every required face passed.",
    },
    64: {
        "coq":         "CubieTrustCompilerV1_50.denial_count_bounded_54",
        "lean":        "CubieTrustCompilerV1_50.denial_count_bounded_54",
        "deliverable": "V1.50",
        "notes":       "Denial count bounded by 54 oriented facelets.",
    },
    # CubieTrustCompiler V2.0 — lease, replay, epoch cluster
    70: {
        "coq":         "CubieTrustCompilerV2_0.lease_integrity",
        "lean":        "CubieTrustCompilerV2_0.lease_integrity",
        "deliverable": "V2.0",
        "notes":       "Admission lease integrity theorem.",
    },
    71: {
        "coq":         "CubieTrustCompilerV2_0.bypass_no_lease",
        "lean":        "CubieTrustCompilerV2_0.bypass_no_lease",
        "deliverable": "V2.0",
        "notes":       "Bypass-no-lease: no bypass without verified lease.",
    },
    76: {
        "coq":         "CubieTrustCompilerV2_0.epoch_advance_strictly_increases;"
                       "CubieTrustCompilerV2_0.epoch_advance_no_rollback",
        "lean":        "CubieTrustCompilerV2_0.epoch_monotonicity",
        "deliverable": "V2.0",
        "notes":       "Epoch monotonicity — no rollback.",
    },
    95: {
        "coq":         "CubieTrustCompilerV2_0.ha_split_brain_fail_closed",
        "lean":        "CubieTrustCompilerV2_0.ha_split_brain_fail_closed",
        "deliverable": "V2.0",
        "notes":       "HA split-brain must fail closed on high-impact requests.",
    },
    # RamenCornerConsensus V1.19.2 — non-vacuity and count cluster
    201: {
        "coq":         "RamenCornerConsensusV1192.denial_count_zero_iff_authorized",
        "lean":        "RamenCornerConsensusV1192.denial_count_zero_iff_authorized;"
                       "CubieTrustCompilerV1_50.denial_count_zero_iff_request_clean",
        "deliverable": "V1.19.2",
        "notes":       "Denial count zero iff authorized / clean.",
    },
    202: {
        "coq":         "RamenCornerConsensusV1192.denial_count_positive_iff_denied",
        "lean":        "RamenCornerConsensusV1192.denial_count_positive_iff_denied",
        "deliverable": "V1.19.2",
        "notes":       "Denial count positive iff denied.",
    },
    203: {
        "coq":         "RamenCornerConsensusV1192.good_corner_exists;"
                       "RamenCornerConsensusV1192.bad_corner_exists",
        "lean":        "RamenCornerConsensusV1192.good_corner_exists;"
                       "RamenCornerConsensusV1192.bad_corner_exists",
        "deliverable": "V1.19.2",
        "notes":       "Predicate non-vacuity (good and bad corners exist).",
    },
}

# ---------------------------------------------------------------------------
# v1.10 Non-Quantum Centillion verified theorems (VERIFIED_EQUIVALENT)
# These 30 theorems are verified in the v1.10 submission but map to
# roadmap rows that describe semantically equivalent concepts.
# original_ids are TBD pending full cross-reference; listed as candidates.
# ---------------------------------------------------------------------------
VERIFIED_EQUIVALENT: dict[int, dict] = {
    # Placeholder — populate after inspecting roadmap titles vs v1.10 theorem names
    # Example (not confirmed):
    # 88: {"coq": "CubieQv14.read_gate_independent_of_write_gate", ...}
}

def classify_row(row: dict, oid: int) -> dict:
    """Return crosswalk fields for a single roadmap row."""
    rtype   = str(row.get("type", "")).strip()
    cluster = row.get("cluster_no", "")
    cluster_int = int(cluster) if str(cluster).isdigit() else -1

    # --- Rule 1: Known VERIFIED_EXACT (by original_id) ---
    if oid in VERIFIED_EXACT:
        v = VERIFIED_EXACT[oid]
        return {
            "actual_status":      "VERIFIED_EXACT",
            "source_deliverable": v["deliverable"],
            "coq_theorem_name":   v["coq"],
            "lean_theorem_name":  v["lean"],
            "proof_status":       "PASS",
            "next_action":        "",
            "notes":              v["notes"],
        }

    # --- Rule 2: Known VERIFIED_EQUIVALENT ---
    if oid in VERIFIED_EQUIVALENT:
        v = VERIFIED_EQUIVALENT[oid]
        return {
            "actual_status":      "VERIFIED_EQUIVALENT",
            "source_deliverable": v.get("deliverable", ""),
            "coq_theorem_name":   v.get("coq", ""),
            "lean_theorem_name":  v.get("lean", ""),
            "proof_status":       "PASS",
            "next_action":        "",
            "notes":              v.get("notes", "Semantically equivalent to v1.10 theorem."),
        }

    # --- Rule 3: Cluster 0 (claim discipline / terminology) = OUT_OF_SCOPE ---
    if cluster_int == 0:
        return {
            "actual_status":      "OUT_OF_SCOPE",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Editorial review of claim classification.",
            "notes":              "Cluster 0 rows are claim-boundary guardrails, not theorems.",
        }

    # --- Rule 4: Pure R type = REWRITE_RULE ---
    if rtype == "R":
        return {
            "actual_status":      "REWRITE_RULE",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Audit documented claim; update whitepaper section.",
            "notes":              "Terminology / spec guardrail — no formal proof required.",
        }

    # --- Rule 5: R/C compound = REWRITE_RULE (doc first, then formalize) ---
    if rtype in ("R/C", "C/R"):
        return {
            "actual_status":      "REWRITE_RULE",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "PENDING",
            "next_action":        "Rewrite doc claim; then formalize supporting theorem.",
            "notes":              "Has documentation guardrail component + formal obligation.",
        }

    # --- Rule 6: Pure E type = EMPIRICAL_CLAIM ---
    if rtype == "E":
        return {
            "actual_status":      "EMPIRICAL_CLAIM",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Define measurement methodology and benchmark target.",
            "notes":              "Cannot be formally proved — requires runtime measurement.",
        }

    # --- Rule 7: R/E compound = EMPIRICAL_CLAIM (with spec cleanup) ---
    if rtype in ("R/E", "E/R"):
        return {
            "actual_status":      "EMPIRICAL_CLAIM",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Define benchmark target; clean up rewrite guardrail.",
            "notes":              "Empirical measurement + spec cleanup required.",
        }

    # --- Rule 8: Pure V type = ENGINEERING_INVARIANT ---
    if rtype == "V":
        return {
            "actual_status":      "ENGINEERING_INVARIANT",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Write Verus spec or integration test.",
            "notes":              "Runtime invariant — target for Verus or property-based test.",
        }

    # --- Rule 9: V/C compound = ENGINEERING_INVARIANT (Verus first) ---
    if rtype in ("V/C", "C/V"):
        return {
            "actual_status":      "ENGINEERING_INVARIANT",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "PENDING",
            "next_action":        "Write Verus spec; escalate to Coq/Lean if formal proof needed.",
            "notes":              "Runtime invariant with formal analogue — Verus bridges the gap.",
        }

    # --- Rule 10: F/C compound = FUTURE_OBLIGATION ---
    if "F" in rtype and "C" in rtype:
        return {
            "actual_status":      "FUTURE_OBLIGATION",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "PENDING",
            "next_action":        "Define new types/predicates; formalize in Lean 4 or Coq.",
            "notes":              "Requires new formal definitions — no proof possible yet.",
        }

    # --- Rule 11: C/F compound = FUTURE_OBLIGATION ---
    if rtype in ("C/F", "F"):
        return {
            "actual_status":      "FUTURE_OBLIGATION",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "PENDING",
            "next_action":        "Define new types/predicates; formalize in Lean 4 or Coq.",
            "notes":              "Future formalization obligation.",
        }

    # --- Rule 12: E/C compound = EMPIRICAL_CLAIM (measurable) ---
    if rtype in ("E/C", "C/E"):
        return {
            "actual_status":      "EMPIRICAL_CLAIM",
            "source_deliverable": "",
            "coq_theorem_name":   "",
            "lean_theorem_name":  "",
            "proof_status":       "N/A",
            "next_action":        "Implement benchmark harness; record measurement.",
            "notes":              "Has formal component but requires empirical validation first.",
        }

    # --- Default: pure C type or unrecognized = DERIVED_CANDIDATE ---
    return {
        "actual_status":      "DERIVED_CANDIDATE",
        "source_deliverable": "",
        "coq_theorem_name":   "",
        "lean_theorem_name":  "",
        "proof_status":       "PENDING",
        "next_action":        "Identify parent theorems in source stack; write formal derivation.",
        "notes":              "Likely derivable from verified stack — needs formal proof.",
    }

def validate_output(rows: list[dict]) -> list[str]:
    """Run constraint checks on the output rows. Returns list of errors."""
    errors = []
    ids_seen: set[int] = set()

    for r in rows:
        rid = int(r["roadmap_id"])

        if rid in ids_seen:
            errors.append(f"Row {rid}: duplicate ID")
        ids_seen.add(rid)

        status = r["actual_status"]
        valid_statuses = {
            "VERIFIED_EXACT", "VERIFIED_EQUIVALENT", "DERIVED_CANDIDATE",
            "FUTURE_OBLIGATION", "REWRITE_RULE", "ENGINEERING_INVARIANT",
            "EMPIRICAL_CLAIM", "OUT_OF_SCOPE",
        }
        if status not in valid_statuses:
            errors.append(f"Row {rid}: invalid status '{status}'")

        if status == "VERIFIED_EXACT" and not (r["coq_theorem_name"] or r["lean_theorem_name"]):
            errors.append(f"Row {rid}: VERIFIED_EXACT but no theorem name")

        if status in ("FUTURE_OBLIGATION", "ENGINEERING_INVARIANT", "EMPIRICAL_CLAIM"):
            if not r["next_action"].strip():
                errors.append(f"Row {rid}: {status} but no next_action")

    missing = set(range(1, 204)) - ids_seen
    if missing:
        errors.append(f"Missing IDs: {sorted(missing)[:10]}")

    return errors

def main():
    parser = argparse.ArgumentParser(description="Build crosswalk_203.csv")
    parser.add_argument("--jsonl", required=True, help="Path to 203-row JSONL")
    parser.add_argument("--out",   required=True, help="Output crosswalk_203.csv")
    args = parser.parse_args()

    with open(args.jsonl, encoding="utf-8") as f:
        rows = [json.loads(line) for line in f if line.strip()]

    if len(rows) != 203:
        print(f"ERROR: Expected 203 rows, got {len(rows)}", file=sys.stderr)
        sys.exit(1)

    fieldnames = [
        "roadmap_id", "roadmap_name", "cluster", "desired_status",
        "actual_status", "source_deliverable", "coq_theorem_name",
        "lean_theorem_name", "proof_status", "notes", "next_action",
    ]

    output_rows = []
    for row in rows:
        row_no = int(row["row_no"])
        oid    = int(row["original_id"]) if str(row["original_id"]).isdigit() else -1

        classification = classify_row(row, oid)

        output_rows.append({
            "roadmap_id":      row_no,
            "roadmap_name":    row.get("title", ""),
            "cluster":         row.get("cluster", ""),
            "desired_status":  row.get("formal_status", ""),
            **classification,
        })

    errors = validate_output(output_rows)
    if errors:
        print("VALIDATION ERRORS:", file=sys.stderr)
        for e in errors:
            print(f"  {e}", file=sys.stderr)
        sys.exit(1)

    out_path = Path(args.out)
    out_path.parent.mkdir(parents=True, exist_ok=True)
    with open(out_path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(output_rows)

    counts = Counter(r["actual_status"] for r in output_rows)
    print(f"crosswalk_203.csv written ({len(output_rows)} rows):")
    for status, count in sorted(counts.items()):
        marker = " ✓" if status.startswith("VERIFIED") else ""
        print(f"  {status}: {count}{marker}")
    print(f"\nVerified total: {counts['VERIFIED_EXACT'] + counts['VERIFIED_EQUIVALENT']}/203")

if __name__ == "__main__":
    main()


#!/usr/bin/env python3
"""
scan_for_unsound_tokens.py — Guard against unsound proof tokens.

Fails (exit 1) if any extracted Coq or Lean source file contains:
  Admitted.       (Coq admission gate)
  admit           (Coq tactic)
  sorry           (Lean 4)
  sorryAx         (Lean 4 internal)
  Axiom <name>    (Coq, unless in known-OK axiom profile)

Known-OK axiom profiles are declared per-file. Files not in the profile
dict have zero tolerance for Axiom declarations.

CubieBridge V1.40 (Lean and Coq) must be fully axiom-free.
"""
import re
import sys
from pathlib import Path

# ---------------------------------------------------------------------------
# Blocked token patterns — exact substring matches
# ---------------------------------------------------------------------------
BLOCKED_TOKENS = [
    "Admitted.",
    "sorryAx",
]

# Regex patterns for context-sensitive blocking
BLOCKED_PATTERNS = [
    re.compile(r"\badmit\s*\.", re.MULTILINE),   # Coq: admit.  (tactic)
    re.compile(r"\bsorry\b",   re.MULTILINE),    # Lean 4: sorry
]

# ---------------------------------------------------------------------------
# Known-OK axiom profiles per filename
# Files NOT in this dict have zero tolerance: no Axiom declarations allowed.
# ---------------------------------------------------------------------------
KNOWN_AXIOM_PROFILES: dict[str, list[str]] = {
    # V1.10 Non-Quantum master model (universe polymorphic)
    "CubieNonQuantum_v1_10.v":    ["propext", "functional_extensionality"],
    "CubieNonQuantum_v1_10.lean": ["propext", "Quot.sound"],
    # Ramen Corner uses Classical for double negation elimination
    "RamenCornerConsensusV1192.v": ["Classical"],
    # CubieBridge V1.40 must be axiom-free in both languages
    "CubieBridgeV1_40.v":         [],
    "CubieBridgeV1_40.lean":      [],
    # Default: no profile = zero tolerance
}

# ---------------------------------------------------------------------------
# Scanner
# ---------------------------------------------------------------------------

def scan_file(path: Path) -> list[str]:
    """Return list of issues found in file. Empty = clean."""
    try:
        text = path.read_text(encoding="utf-8")
    except UnicodeDecodeError:
        text = path.read_text(encoding="latin-1")
    except Exception as e:
        return [f"READ_ERROR: {e}"]

    issues = []
    lines  = text.splitlines()

    # --- Exact substring checks ---
    for token in BLOCKED_TOKENS:
        if token in text:
            hits = [i + 1 for i, line in enumerate(lines) if token in line]
            issues.append(f"BLOCKED_TOKEN '{token}' at lines {hits[:5]}")

    # --- Regex pattern checks ---
    for pattern in BLOCKED_PATTERNS:
        for m in pattern.finditer(text):
            line_no = text[:m.start()].count("\n") + 1
            issues.append(f"BLOCKED_PATTERN '{pattern.pattern.strip()}' at line {line_no}")

    # --- Axiom declaration checks (Coq .v files) ---
    if path.suffix == ".v":
        allowed = KNOWN_AXIOM_PROFILES.get(path.name, None)
        for i, line in enumerate(lines, 1):
            stripped = line.strip()
            if stripped.startswith("Axiom "):
                parts     = stripped.split()
                axiom_name = parts[1].rstrip(":") if len(parts) > 1 else "?"
                if allowed is None:
                    issues.append(
                        f"UNEXPECTED_AXIOM '{axiom_name}' at line {i} "
                        f"(no axiom profile for {path.name})"
                    )
                elif axiom_name not in allowed:
                    issues.append(
                        f"UNEXPECTED_AXIOM '{axiom_name}' at line {i} "
                        f"(not in profile: {allowed})"
                    )

    # --- Lean axiom checks ---
    if path.suffix == ".lean":
        allowed = KNOWN_AXIOM_PROFILES.get(path.name, None)
        for i, line in enumerate(lines, 1):
            stripped = line.strip()
            if stripped.startswith("axiom "):
                parts      = stripped.split()
                axiom_name = parts[1].rstrip(":") if len(parts) > 1 else "?"
                if allowed is None:
                    issues.append(
                        f"UNEXPECTED_AXIOM '{axiom_name}' at line {i} "
                        f"(no axiom profile for {path.name})"
                    )
                elif axiom_name not in allowed:
                    issues.append(
                        f"UNEXPECTED_AXIOM '{axiom_name}' at line {i} "
                        f"(not in profile: {allowed})"
                    )

    return issues

def main():
    repo      = Path(__file__).parent.parent
    total     = 0
    issue_cnt = 0
    file_fails: list[str] = []

    for lang, ext in [("coq", ".v"), ("lean", ".lean")]:
        lang_dir = repo / lang
        if not lang_dir.exists():
            print(f"SKIP {lang}/ (directory not found)")
            continue

        for fpath in sorted(lang_dir.glob(f"*{ext}")):
            total += 1
            issues = scan_file(fpath)
            if issues:
                print(f"FAIL {lang}/{fpath.name}:")
                for issue in issues:
                    print(f"     {issue}")
                issue_cnt += len(issues)
                file_fails.append(fpath.name)
            else:
                print(f"PASS {lang}/{fpath.name}")

    print(f"\n{'='*60}")
    print(f"Scanned {total} files.")
    if issue_cnt:
        print(f"FAIL: {issue_cnt} issues in {len(file_fails)} file(s): {file_fails}")
        sys.exit(1)
    else:
        print("PASS: all files clean (no Admitted/sorry/unexpected axioms).")
        sys.exit(0)

if __name__ == "__main__":
    main()


File created successfully at: C:\Users\NickV\cubie-verification\tools\scan_for_unsound_tokens.py

#!/usr/bin/env python3
"""
scan_for_unsound_tokens.py — Guard against unsound proof tokens.

Fails (exit 1) if any extracted Coq or Lean source file contains:
  Admitted.       (Coq admission gate)
  admit           (Coq tactic)
  sorry           (Lean 4)
  sorryAx         (Lean 4 internal)
  Axiom <name>    (Coq, unless in known-OK axiom profile)

Known-OK axiom profiles are declared per-file. Files not in the profile
dict have zero tolerance for Axiom declarations.

CubieBridge V1.40 (Lean and Coq) must be fully axiom-free.
"""
import re
import sys
from pathlib import Path

# ---------------------------------------------------------------------------
# Blocked token patterns — exact substring matches
# ---------------------------------------------------------------------------
BLOCKED_TOKENS = [
    "Admitted.",
    "sorryAx",
]

# Regex patterns for context-sensitive blocking
BLOCKED_PATTERNS = [
    re.compile(r"\badmit\s*\.", re.MULTILINE),   # Coq: admit.  (tactic)
    re.compile(r"\bsorry\b",   re.MULTILINE),    # Lean 4: sorry
]

# ---------------------------------------------------------------------------
# Known-OK axiom profiles per filename
# Files NOT in this dict have zero tolerance: no Axiom declarations allowed.
# ---------------------------------------------------------------------------
KNOWN_AXIOM_PROFILES: dict[str, list[str]] = {
    # V1.10 Non-Quantum master model (universe polymorphic)
    "CubieNonQuantum_v1_10.v":    ["propext", "functional_extensionality"],
    "CubieNonQuantum_v1_10.lean": ["propext", "Quot.sound"],
    # Ramen Corner uses Classical for double negation elimination
    "RamenCornerConsensusV1192.v": ["Classical"],
    # CubieBridge V1.40 must be axiom-free in both languages
    "CubieBridgeV1_40.v":         [],
    "CubieBridgeV1_40.lean":      [],
    # Default: no profile = zero tolerance
}

# ---------------------------------------------------------------------------
# Scanner
# ---------------------------------------------------------------------------

def scan_file(path: Path) -> list[str]:
    """Return list of issues found in file. Empty = clean."""
    try:
        text = path.read_text(encoding="utf-8")
    except UnicodeDecodeError:
        text = path.read_text(encoding="latin-1")
    except Exception as e:
        return [f"READ_ERROR: {e}"]

    issues = []
    lines  = text.splitlines()

    # --- Exact substring checks ---
    for token in BLOCKED_TOKENS:
        if token in text:
            hits = [i + 1 for i, line in enumerate(lines) if token in line]
            issues.append(f"BLOCKED_TOKEN '{token}' at lines {hits[:5]}")

    # --- Regex pattern checks ---
    for pattern in BLOCKED_PATTERNS:
        for m in pattern.finditer(text):
            line_no = text[:m.start()].count("\n") + 1
            issues.append(f"BLOCKED_PATTERN '{pattern.pattern.strip()}' at line {line_no}")

    # --- Axiom declaration checks (Coq .v files) ---
    if path.suffix == ".v":
        allowed = KNOWN_AXIOM_PROFILES.get(path.name, None)
        for i, line in enumerate(lines, 1):
            stripped = line.strip()
            if stripped.startswith("Axiom "):
                parts     = stripped.split()
                axiom_name = parts[1].rstrip(":") if len(parts) > 1 else "?"
                if allowed is None:
                    issues.append(
                        f"UNEXPECTED_AXIOM '{axiom_name}' at line {i} "
                        f"(no axiom profile for {path.name})"
                    )
                elif axiom_name not in allowed:
                    issues.append(
                        f"UNEXPECTED_AXIOM '{axiom_name}' at line {i} "
                        f"(not in profile: {allowed})"
                    )

    # --- Lean axiom checks ---
    if path.suffix == ".lean":
        allowed = KNOWN_AXIOM_PROFILES.get(path.name, None)
        for i, line in enumerate(lines, 1):
            stripped = line.strip()
            if stripped.startswith("axiom "):
                parts      = stripped.split()
                axiom_name = parts[1].rstrip(":") if len(parts) > 1 else "?"
                if allowed is None:
                    issues.append(
                        f"UNEXPECTED_AXIOM '{axiom_name}' at line {i} "
                        f"(no axiom profile for {path.name})"
                    )
                elif axiom_name not in allowed:
                    issues.append(
                        f"UNEXPECTED_AXIOM '{axiom_name}' at line {i} "
                        f"(not in profile: {allowed})"
                    )

    return issues

def main():
    repo      = Path(__file__).parent.parent
    total     = 0
    issue_cnt = 0
    file_fails: list[str] = []

    for lang, ext in [("coq", ".v"), ("lean", ".lean")]:
        lang_dir = repo / lang
        if not lang_dir.exists():
            print(f"SKIP {lang}/ (directory not found)")
            continue

        for fpath in sorted(lang_dir.glob(f"*{ext}")):
            total += 1
            issues = scan_file(fpath)
            if issues:
                print(f"FAIL {lang}/{fpath.name}:")
                for issue in issues:
                    print(f"     {issue}")
                issue_cnt += len(issues)
                file_fails.append(fpath.name)
            else:
                print(f"PASS {lang}/{fpath.name}")

    print(f"\n{'='*60}")
    print(f"Scanned {total} files.")
    if issue_cnt:
        print(f"FAIL: {issue_cnt} issues in {len(file_fails)} file(s): {file_fails}")
        sys.exit(1)
    else:
        print("PASS: all files clean (no Admitted/sorry/unexpected axioms).")
        sys.exit(0)

if __name__ == "__main__":
    main()


#!/usr/bin/env python3
"""
verify_hashes.py — Verify SHA-256 hashes of Coq/Lean source files against manifest.json.

Exit 0 = all populated hashes match.
Exit 1 = any mismatch or missing file where hash is populated.

Hashes marked "FILL_FROM_PDF" are skipped (not yet populated).
"""
import hashlib
import json
import sys
from pathlib import Path

def sha256_file(path: Path) -> str:
    h = hashlib.sha256()
    h.update(path.read_bytes())
    return h.hexdigest()

def main():
    repo          = Path(__file__).parent.parent
    manifest_path = repo / "manifest.json"

    if not manifest_path.exists():
        print("ERROR: manifest.json not found. Run 'make manifest' first.")
        sys.exit(1)

    manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
    passes   = []
    failures = []
    skipped  = []

    # Check both sources (PDF-extracted) and v1_10_submission sections
    sections = {
        "sources/coq":         (manifest.get("sources", {}).get("coq",  {}), ".v",     repo / "coq"),
        "sources/lean":        (manifest.get("sources", {}).get("lean", {}), ".lean",  repo / "lean"),
        "v1_10/coq":           (manifest.get("v1_10_submission", {}).get("coq",  {}), ".v",    repo / "coq"),
        "v1_10/lean":          (manifest.get("v1_10_submission", {}).get("lean", {}), ".lean", repo / "lean"),
    }

    for section_name, (source_dict, ext, lang_dir) in sections.items():
        for fname, meta in source_dict.items():
            expected_sha = meta.get("sha256", "")

            if not expected_sha or expected_sha.upper().startswith("FILL"):
                skipped.append(f"SKIP {section_name}/{fname} (hash not populated)")
                continue

            fpath = lang_dir / fname
            if not fpath.exists():
                failures.append(
                    f"MISSING {section_name}/{fname}\n"
                    f"  expected sha256: {expected_sha}"
                )
                continue

            actual_sha = sha256_file(fpath)
            if actual_sha == expected_sha:
                passes.append(f"OK   {section_name}/{fname}")
            else:
                failures.append(
                    f"MISMATCH {section_name}/{fname}\n"
                    f"  expected: {expected_sha}\n"
                    f"  actual:   {actual_sha}"
                )

    for p in passes:
        print(f"PASS: {p}")
    for s in skipped:
        print(s)
    for f in failures:
        print(f"FAIL: {f}")

    print(f"\n{len(passes)} passed, {len(skipped)} skipped, {len(failures)} failed")
    sys.exit(1 if failures else 0)

if __name__ == "__main__":
    main()


File created successfully at: C:\Users\NickV\cubie-verification\tools\verify_hashes.py

#!/usr/bin/env python3
"""
verify_hashes.py — Verify SHA-256 hashes of Coq/Lean source files against manifest.json.

Exit 0 = all populated hashes match.
Exit 1 = any mismatch or missing file where hash is populated.

Hashes marked "FILL_FROM_PDF" are skipped (not yet populated).
"""
import hashlib
import json
import sys
from pathlib import Path

def sha256_file(path: Path) -> str:
    h = hashlib.sha256()
    h.update(path.read_bytes())
    return h.hexdigest()

def main():
    repo          = Path(__file__).parent.parent
    manifest_path = repo / "manifest.json"

    if not manifest_path.exists():
        print("ERROR: manifest.json not found. Run 'make manifest' first.")
        sys.exit(1)

    manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
    passes   = []
    failures = []
    skipped  = []

    # Check both sources (PDF-extracted) and v1_10_submission sections
    sections = {
        "sources/coq":         (manifest.get("sources", {}).get("coq",  {}), ".v",     repo / "coq"),
        "sources/lean":        (manifest.get("sources", {}).get("lean", {}), ".lean",  repo / "lean"),
        "v1_10/coq":           (manifest.get("v1_10_submission", {}).get("coq",  {}), ".v",    repo / "coq"),
        "v1_10/lean":          (manifest.get("v1_10_submission", {}).get("lean", {}), ".lean", repo / "lean"),
    }

    for section_name, (source_dict, ext, lang_dir) in sections.items():
        for fname, meta in source_dict.items():
            expected_sha = meta.get("sha256", "")

            if not expected_sha or expected_sha.upper().startswith("FILL"):
                skipped.append(f"SKIP {section_name}/{fname} (hash not populated)")
                continue

            fpath = lang_dir / fname
            if not fpath.exists():
                failures.append(
                    f"MISSING {section_name}/{fname}\n"
                    f"  expected sha256: {expected_sha}"
                )
                continue

            actual_sha = sha256_file(fpath)
            if actual_sha == expected_sha:
                passes.append(f"OK   {section_name}/{fname}")
            else:
                failures.append(
                    f"MISMATCH {section_name}/{fname}\n"
                    f"  expected: {expected_sha}\n"
                    f"  actual:   {actual_sha}"
                )

    for p in passes:
        print(f"PASS: {p}")
    for s in skipped:
        print(s)
    for f in failures:
        print(f"FAIL: {f}")

    print(f"\n{len(passes)} passed, {len(skipped)} skipped, {len(failures)} failed")
    sys.exit(1 if failures else 0)

if __name__ == "__main__":
    main()


#!/usr/bin/env python3
"""
summarize_verification.py — Generate verification_report.md, .json, and gap_report.md.

Reads: roadmap/crosswalk_203.csv, coq/*.v, lean/*.lean
Writes: reports/verification_report.md
        reports/verification_report.json
        reports/gap_report.md
"""
import csv
import json
import re
import sys
from collections import Counter, defaultdict
from datetime import date
from pathlib import Path

def count_declarations(fpath: Path) -> dict:
    """Count Theorem/Lemma/Definition declarations in a Coq .v file."""
    text   = fpath.read_text(encoding="utf-8", errors="replace")
    counts = Counter()
    for line in text.splitlines():
        s = line.strip()
        for kw in ("Theorem", "Lemma", "Definition", "Fixpoint", "Inductive", "Corollary"):
            if s.startswith(kw + " "):
                counts[kw] += 1
                break
    return dict(counts)

def lean_axiom_profile(fpath: Path) -> str:
    """Determine axiom profile of a Lean .lean file."""
    text = fpath.read_text(encoding="utf-8", errors="replace")
    if "sorryAx" in text or "sorry" in text:
        return "USES_SORRY"
    has_propext    = "propext" in text
    has_quot       = "Quot.sound" in text
    has_classical  = "Classical" in text
    has_axiom      = bool(re.search(r"^axiom\s+", text, re.MULTILINE))
    if has_propext and has_quot:
        return "USES_PROPEXT_AND_QUOT_SOUND"
    if has_propext:
        return "USES_PROPEXT"
    if has_classical:
        return "USES_CLASSICAL"
    if has_axiom:
        return "HAS_AXIOM"
    return "AXIOM_FREE"

def load_crosswalk(csv_path: Path) -> list[dict]:
    if not csv_path.exists():
        return []
    with open(csv_path, encoding="utf-8") as f:
        return list(csv.DictReader(f))

def main():
    repo           = Path(__file__).parent.parent
    crosswalk_path = repo / "roadmap" / "crosswalk_203.csv"
    reports_dir    = repo / "reports"
    reports_dir.mkdir(exist_ok=True)
    today = date.today().isoformat()

    # --- Load crosswalk ---
    rows           = load_crosswalk(crosswalk_path)
    status_counts  = Counter(r["actual_status"] for r in rows)
    verified_rows  = [r for r in rows if r["actual_status"].startswith("VERIFIED")]
    future_rows    = [r for r in rows if r["actual_status"] == "FUTURE_OBLIGATION"]
    derived_rows   = [r for r in rows if r["actual_status"] == "DERIVED_CANDIDATE"]
    invariant_rows = [r for r in rows if r["actual_status"] == "ENGINEERING_INVARIANT"]
    empirical_rows = [r for r in rows if r["actual_status"] == "EMPIRICAL_CLAIM"]
    rewrite_rows   = [r for r in rows if r["actual_status"] == "REWRITE_RULE"]

    # --- Coq file analysis ---
    coq_dir    = repo / "coq"
    coq_files  = sorted(coq_dir.glob("*.v")) if coq_dir.exists() else []
    coq_data   = {f.name: count_declarations(f) for f in coq_files}
    total_decl = sum(sum(v.values()) for v in coq_data.values())

    # --- Lean file analysis ---
    lean_dir      = repo / "lean"
    lean_files    = sorted(lean_dir.glob("*.lean")) if lean_dir.exists() else []
    lean_profiles = {f.name: lean_axiom_profile(f) for f in lean_files}

    axiom_free_lean  = [n for n, p in lean_profiles.items() if p == "AXIOM_FREE"]
    uses_propext     = [n for n, p in lean_profiles.items() if "PROPEXT" in p]
    bridge_profile   = lean_profiles.get("CubieBridgeV1_40.lean", "NOT_FOUND")

    # =========================================================================
    # verification_report.md
    # =========================================================================
    md = f"""# Cubie Verification Report

**Generated:** {today}
**Package version:** cubie-verification v3
**Source:** CubieMasterStack PDF (163 theorems, 7 deliverables) + v1.10 Non-Quantum (30 theorems) + Theorem Omega 4.0 (1 theorem)

---

## 1. Critical invariant: CubieBridge V1.40 axiom profile

CubieBridge V1.40 **must** be fully axiom-free in Lean 4.

| File | Lean Axiom Profile | Status |
|------|--------------------|--------|
| CubieBridgeV1_40.lean | {bridge_profile} | {'✅ PASS' if bridge_profile == 'AXIOM_FREE' else '❌ FAIL — must be AXIOM_FREE'} |

---

## 2. Coq files

| File | Theorem | Lemma | Definition | Other | Total |
|------|---------|-------|------------|-------|-------|
"""
    for fname, counts in sorted(coq_data.items()):
        t = counts.get("Theorem", 0)
        l = counts.get("Lemma", 0)
        d = counts.get("Definition", 0)
        o = sum(v for k, v in counts.items() if k not in ("Theorem", "Lemma", "Definition"))
        total = sum(counts.values())
        md += f"| {fname} | {t} | {l} | {d} | {o} | {total} |\n"
    md += f"\n**Total declarations scanned:** {total_decl}  \n"
    md += f"**Coq files present:** {len(coq_files)}/13 expected\n"

    md += f"""
---

## 3. Lean files — axiom profiles

| File | Axiom Profile |
|------|---------------|
"""
    for fname, profile in sorted(lean_profiles.items()):
        flag = " ✅" if profile == "AXIOM_FREE" else (" ⚠" if "PROPEXT" in profile else " ❌")
        md += f"| {fname} | {profile}{flag} |\n"

    md += f"""
**Axiom-free files:** {len(axiom_free_lean)}
**Uses propext:** {len(uses_propext)}
**Lean files present:** {len(lean_files)}/8 expected

---

## 4. Crosswalk summary (203 rows)

| Status | Count | % |
|--------|-------|---|
"""
    total_rows = len(rows) or 1
    for status, count in sorted(status_counts.items()):
        pct = 100 * count / total_rows
        md += f"| {status} | {count} | {pct:.1f}% |\n"

    md += f"""
**Total rows:** {len(rows)}
**Verified (EXACT + EQUIVALENT):** {len(verified_rows)}
**Future obligations:** {len(future_rows)}
**Derived candidates:** {len(derived_rows)}
**Engineering invariants (Verus targets):** {len(invariant_rows)}
**Empirical claims:** {len(empirical_rows)}
**Rewrite rules (doc guardrails):** {len(rewrite_rows)}

---

## 5. Verified theorem mappings

| Row | Title | Coq Theorem | Deliverable |
|-----|-------|-------------|-------------|
"""
    for r in sorted(verified_rows, key=lambda x: int(x["roadmap_id"])):
        coq = r["coq_theorem_name"][:55] if r["coq_theorem_name"] else "(lean only)"
        md += f"| {r['roadmap_id']} | {r['roadmap_name'][:40]} | {coq} | {r['source_deliverable']} |\n"

    md += f"""
---

## 6. What this report does NOT claim

- Coq and Lean compilation is pending `coqc`/`lean` execution (install required).
- SHA-256 hashes are pending population from the PDF manifest (run `make hashes`).
- Verus proofs are stubs — `verus` binary required to verify runtime specs.
- Zero production-correctness claims are made from formal proofs alone.
  Rust_Conformance requires Verus or integration tests (ENGINEERING_INVARIANT rows).
"""

    (reports_dir / "verification_report.md").write_text(md, encoding="utf-8")
    print("WRITTEN: reports/verification_report.md")

    # =========================================================================
    # verification_report.json
    # =========================================================================
    report_json = {
        "generated":        today,
        "version":          "v3",
        "coq": {
            "files_present": len(coq_files),
            "files_expected": 13,
            "declarations":  coq_data,
            "total_decl":    total_decl,
        },
        "lean": {
            "files_present":    len(lean_files),
            "files_expected":   8,
            "axiom_profiles":   lean_profiles,
            "axiom_free_count": len(axiom_free_lean),
            "bridge_v140_profile": bridge_profile,
        },
        "crosswalk": {
            "total_rows":       len(rows),
            "status_counts":    dict(status_counts),
            "verified_count":   len(verified_rows),
            "future_count":     len(future_rows),
            "derived_count":    len(derived_rows),
            "invariant_count":  len(invariant_rows),
            "empirical_count":  len(empirical_rows),
            "rewrite_count":    len(rewrite_rows),
        },
        "verified_rows": [
            {
                "id":          r["roadmap_id"],
                "title":       r["roadmap_name"],
                "coq_theorem": r["coq_theorem_name"],
                "lean_theorem":r["lean_theorem_name"],
                "deliverable": r["source_deliverable"],
                "notes":       r["notes"],
            }
            for r in sorted(verified_rows, key=lambda x: int(x["roadmap_id"]))
        ],
    }
    (reports_dir / "verification_report.json").write_text(
        json.dumps(report_json, indent=2, ensure_ascii=False), encoding="utf-8"
    )
    print("WRITTEN: reports/verification_report.json")

    # =========================================================================
    # gap_report.md
    # =========================================================================
    gap_md = f"""# Gap Report — Cubie Verification v3

**Generated:** {today}

All roadmap rows without a current Coq/Lean source match are listed below.
Each row is classified and has a specific next action.

**Summary:**
- {len(derived_rows)} DERIVED_CANDIDATE rows — provable from verified stack
- {len(future_rows)} FUTURE_OBLIGATION rows — need new formal definitions
- {len(invariant_rows)} ENGINEERING_INVARIANT rows — Verus spec or integration test targets

---

## DERIVED_CANDIDATE rows

These theorems can likely be proven by derivation from the existing verified source stack.
No new axioms are required — only formalization work.

| Row | Title | Cluster | Next Action |
|-----|-------|---------|-------------|
"""
    for r in sorted(derived_rows, key=lambda x: int(x["roadmap_id"])):
        gap_md += (
            f"| {r['roadmap_id']} | {r['roadmap_name'][:45]} | "
            f"{r['cluster'][:35]} | {r['next_action'][:45]} |\n"
        )

    gap_md += f"""
---

## FUTURE_OBLIGATION rows

These theorems require new type definitions or axioms before a proof is possible.

| Row | Title | Cluster | Next Action |
|-----|-------|---------|-------------|
"""
    for r in sorted(future_rows, key=lambda x: int(x["roadmap_id"])):
        gap_md += (
            f"| {r['roadmap_id']} | {r['roadmap_name'][:45]} | "
            f"{r['cluster'][:35]} | {r['next_action'][:45]} |\n"
        )

    gap_md += f"""
---

## ENGINEERING_INVARIANT rows (Verus / integration test targets)

These are runtime invariants. They map to the Verus spec modules in `verus/`.

| Row | Title | Cluster | Next Action |
|-----|-------|---------|-------------|
"""
    for r in sorted(invariant_rows, key=lambda x: int(x["roadmap_id"])):
        gap_md += (
            f"| {r['roadmap_id']} | {r['roadmap_name'][:45]} | "
            f"{r['cluster'][:35]} | {r['next_action'][:45]} |\n"
        )

    gap_md += f"""
---

## EMPIRICAL_CLAIM rows (benchmark targets)

These claims require runtime measurement, not formal proof.

| Row | Title | Measurement Strategy |
|-----|-------|---------------------|
"""
    for r in sorted(empirical_rows, key=lambda x: int(x["roadmap_id"])):
        gap_md += f"| {r['roadmap_id']} | {r['roadmap_name'][:45]} | {r['next_action'][:55]} |\n"

    (reports_dir / "gap_report.md").write_text(gap_md, encoding="utf-8")
    print("WRITTEN: reports/gap_report.md")

    print(f"\nSummary: {len(verified_rows)} verified | "
          f"{len(future_rows)} future | "
          f"{len(derived_rows)} derived | "
          f"{len(invariant_rows)} invariant | "
          f"{len(empirical_rows)} empirical")

if __name__ == "__main__":
    main()