check_flashos_capabilities.fsh

#!/usr/bin/env fsh
# Taplo decodes tracked TOML and jq exposes field names. ripgrep exposes exact
# source markers and the live Rust enum. Flash owns schema, ordering, evidence
# closure, repository boundaries, and all public diagnostics.

import { require_jq, require_rg, toml_to_json } from './lib/tools.fsh'
import { repository_root } from './lib/repository.fsh'

def capability_error(message) {
    ^printf 'FlashOS capability evidence: %s\n' $message 1>&2
    exit 1
}

def nonempty(value, label) {
    if $value == null || $value == '' {
        throw "$label must be a non-empty string"
    }
    return $value
}

def validate_strings(values, label, nonempty_required) {
    if $values == null {
        throw "$label must be a list of non-empty strings"
    }
    if $nonempty_required && $values == [] {
        throw "$label must not be empty"
    }
    for selected in $values {
        if $selected == null || $selected == '' {
            throw "$label must be a list of non-empty strings"
        }
        mut occurrences = 0
        for candidate in $values {
            if $candidate == $selected {
                $occurrences = $occurrences + 1
            }
        }
        if $occurrences != 1 {
            throw "$label contains duplicates"
        }
    }
    return $values
}

def same_set(observed, expected) -> Bool {
    for value in $observed {
        if !($value in $expected) {
            return false
        }
    }
    for value in $expected {
        if !($value in $observed) {
            return false
        }
    }
    return true
}

def validate_records(records, fields, ids, label) {
    if $records == null || $records == [] {
        throw "$label must be a non-empty array of tables"
    }
    let expected_fields = ['id', 'markers', 'observation', 'path']
    mut index = 0
    for record in $records {
        let item = "$label[$index]"
        if $fields[$index] != $expected_fields {
            throw "$item fields are unexpected"
        }
        let identifier = nonempty($record.id, "$item.id")
        mut occurrences = 0
        for candidate in $ids {
            if $candidate == $identifier {
                $occurrences = $occurrences + 1
            }
        }
        if $occurrences != 1 {
            throw "duplicate $label id '$identifier'"
        }
        nonempty($record.path, "$item.path")
        validate_strings($record.markers, "$item.markers", true)
        nonempty($record.observation, "$item.observation")
        $index = $index + 1
    }
    return true
}

def validate(bundle) {
    let expected_fields = [
    'architecture',
    'capability',
    'classification',
    'contract_source',
    'platform',
    'platform_baseline',
    'runtime_evidence',
    'schema_version',
    'selected_adapter',
    'source_evidence',
    'target',
    ]
    if $bundle.fields != $expected_fields {
        throw 'document fields are unexpected'
    }
    let document = $bundle.document
    let expected = {
        schema_version: 1,
        platform: 'flashos',
        architecture: 'x86_64',
        target: 'x86_64-unknown-redox',
        platform_baseline: 'flashos-x86_64.toml',
        contract_source: 'components/flash/crates/flash-platform/src/lib.rs',
        selected_adapter: 'flash-platform-flashos::FlashOsPlatform',
        classification: 'deferred',
    }
    for field in [
    'schema_version',
    'platform',
    'architecture',
    'target',
    'platform_baseline',
    'contract_source',
    'selected_adapter',
    'classification',
    ] {
        if $document[$field] != $expected[$field] {
            throw "$field is '${$document[$field]}', expected '${$expected[$field]}'"
        }
    }
    if $bundle.baseline.architecture != $document.architecture {
        throw 'architecture does not match the platform baseline'
    }
    if $bundle.baseline.target.triple != $document.target {
        throw 'target does not match the platform baseline'
    }

    validate_records(
    $document.source_evidence,
    $bundle.source_fields,
    $bundle.source_ids,
    'source_evidence',
    )
    validate_records(
    $document.runtime_evidence,
    $bundle.runtime_fields,
    $bundle.runtime_ids,
    'runtime_evidence',
    )
    if $document.capability == null || $document.capability == [] {
        throw 'capability must be a non-empty array of tables'
    }
    let expected_capability_fields = [
    'classification',
    'name',
    'requirements',
    'runtime_evidence',
    'runtime_observation',
    'rust_variant',
    'source_evidence',
    'source_observation',
    ]
    mut index = 0
    for capability in $document.capability {
        let label = "capability[$index]"
        if $bundle.capability_fields[$index] != $expected_capability_fields {
            throw "$label fields are unexpected"
        }
        let name = nonempty($capability.name, "$label.name")
        let variant = nonempty($capability.rust_variant, "$label.rust_variant")
        validate_strings($capability.requirements, "$label.requirements", true)
        let selected_source = validate_strings(
        $capability.source_evidence,
        "$label.source_evidence",
        true,
        )
        let selected_runtime = validate_strings(
        $capability.runtime_evidence,
        "$label.runtime_evidence",
        false,
        )
        for identifier in $selected_source {
            if !($identifier in $bundle.source_ids) {
                throw "$label references unknown source evidence '$identifier'"
            }
        }
        for identifier in $selected_runtime {
            if !($identifier in $bundle.runtime_ids) {
                throw "$label references unknown runtime evidence '$identifier'"
            }
        }
        nonempty($capability.source_observation, "$label.source_observation")
        nonempty($capability.runtime_observation, "$label.runtime_observation")
        if $capability.classification != 'deferred' {
            throw "$label.classification must remain 'deferred'"
        }
        $index = $index + 1
    }
    for name in $bundle.capability_names {
        mut occurrences = 0
        for candidate in $bundle.capability_names {
            if $candidate == $name {
                $occurrences = $occurrences + 1
            }
        }
        if $occurrences != 1 {
            throw 'capability names contain duplicates'
        }
    }
    for variant in $bundle.capability_variants {
        mut occurrences = 0
        for candidate in $bundle.capability_variants {
            if $candidate == $variant {
                $occurrences = $occurrences + 1
            }
        }
        if $occurrences != 1 {
            throw 'capability rust_variant values contain duplicates'
        }
    }
    if $bundle.capability_variants != $bundle.contract_variants {
        throw 'manifest variants do not match the live contract enum'
    }
    if !same_set($bundle.used_source, $bundle.source_ids) {
        throw 'unreferenced source evidence ids'
    }
    if !same_set($bundle.used_runtime, $bundle.runtime_ids) {
        throw 'unreferenced runtime evidence ids'
    }
    return true
}

def validate_record_files(bundle, section, label, root, rg, jq) {
    mut index = 0
    mut records_remaining = true
    while $records_remaining {
        let exists = "$(^env $jq --raw-output --arg section $section --argjson index $index '.document[$section] | has($index)' $bundle)"
        if $exists != 'true' {
            $records_remaining = false
            continue
        }
        let item = "$label[$index]"
        let relative = "$(^env $jq --raw-output --arg section $section --argjson index $index '.document[$section][$index].path' $bundle)"
        if ^printf '%s' $relative | ^env $rg --quiet '(^/|(^|/)\.\.(/|$))' {
            throw "$item.path must stay inside the repository"
        }
        let path = "$root/$relative"
        if ^test -f $path {
        } else {
            throw "cannot read $relative"
        }
        mut marker_index = 0
        mut markers_remaining = true
        while $markers_remaining {
            let marker_exists = "$(^env $jq --raw-output --arg section $section --argjson index $index --argjson marker $marker_index '.document[$section][$index].markers | has($marker)' $bundle)"
            if $marker_exists != 'true' {
                $markers_remaining = false
                continue
            }
            let marker = "$(^env $jq --raw-output --arg section $section --argjson index $index --argjson marker $marker_index '.document[$section][$index].markers[$marker]' $bundle)"
            if ^env $rg --fixed-strings --quiet -- $marker $path {
            } else {
                throw "$item marker is absent from $relative: '$marker'"
            }
            $marker_index = $marker_index + 1
        }
        $index = $index + 1
    }
}

let root = repository_root('versions.env')
let rg = require_rg()
let jq = require_jq()
mut temporary_parent = env('TMPDIR')
if $temporary_parent == null {
    $temporary_parent = '/tmp'
}
let temporary = "$(^mktemp -d "$temporary_parent/flashos-capabilities.XXXXXX")"
if !$status.ok {
    capability_error('cannot create temporary directory')
}
let errors = "$temporary/errors"
let evidence = "$temporary/evidence.json"
let baseline = "$temporary/baseline.json"
let bundle = "$temporary/bundle.json"
let evidence_document = toml_to_json(
"$root/components/flash/platforms/flashos-x86_64-capability-evidence.toml",
$errors,
)
^printf '%s\n' $evidence_document > $evidence
let baseline_document = toml_to_json(
"$root/components/flash/platforms/flashos-x86_64.toml",
$errors,
)
^printf '%s\n' $baseline_document > $baseline
let variants = "$temporary/variants.json"
^sed -n '/^pub enum Capability {$/,/^}$/p' "$root/components/flash/crates/flash-platform/src/lib.rs" \
| ^env $rg --only-matching --replace '$1' '^    ([A-Z][A-Za-z0-9]+),$' \
| ^env $jq --raw-input --slurp 'split("\n") | map(select(length > 0))' > $variants
if !$status.ok {
    capability_error('cannot locate the Capability enum')
}
^env $jq \
--slurpfile baseline $baseline \
--slurpfile variants $variants \
'{document:., fields:(keys|sort), baseline:$baseline[0], contract_variants:$variants[0], source_fields:(try [.source_evidence[]|(keys|sort)] catch []), source_ids:(try [.source_evidence[].id] catch []), runtime_fields:(try [.runtime_evidence[]|(keys|sort)] catch []), runtime_ids:(try [.runtime_evidence[].id] catch []), capability_fields:(try [.capability[]|(keys|sort)] catch []), capability_names:(try [.capability[].name] catch []), capability_variants:(try [.capability[].rust_variant] catch []), used_source:(try [.capability[].source_evidence[]] catch []), used_runtime:(try [.capability[].runtime_evidence[]] catch [])}' \
$evidence > $bundle 2> $errors
if !$status.ok {
    capability_error('cannot project the capability evidence contract')
}
try {
    open $bundle | from json | each {|document| validate($document)} | to json > /dev/null
} catch error {
    let message = $error.message
    ^rm -rf $temporary
    capability_error($message)
}
try {
    validate_record_files($bundle, 'source_evidence', 'source_evidence', $root, $rg, $jq)
    validate_record_files($bundle, 'runtime_evidence', 'runtime_evidence', $root, $rg, $jq)
} catch error {
    let message = $error.message
    ^rm -rf $temporary
    capability_error($message)
}
if ^env $rg --fixed-strings --quiet -- 'build/flash-bootstrap/134635a5e1282b5d8455a4b2aeb754be5a3a77c1/fsh ci/check_flashos_capabilities.fsh' "$root/.github/workflows/ci.yml" {
} else {
    ^rm -rf $temporary
    capability_error('standard CI does not validate the capability evidence inventory')
}
^rm -rf $temporary
^printf 'FlashOS capability evidence: source/runtime comparison contract passed for x86_64-unknown-redox\n'

Return to documentation →