check_flashos_target_matrix.fsh

#!/usr/bin/env fsh
# The native matrix renderer validates the versioned matrix schema and projects
# its JSON-v1 byte boundary. Taplo decodes linked platform records, jq exposes
# ordered associations and byte observations, and ripgrep exposes the selected
# adapter and consumers. Flash owns advertised capability policy, case/operation
# ownership, UART limits, coverage, and diagnostics.

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

def matrix_error(message) {
    ^printf 'FlashOS target matrix: %s\n' $message 1>&2
    exit 1
}

def require_runtime(program) {
    let observed = "$(^env $program --version 2>/dev/null)"
    if !$status.ok || $observed != 'fsh 1.0.0' {
        matrix_error("fsh version differs: expected fsh 1.0.0, observed $observed")
    }
    return $program
}

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(bundle) {
    let required_surfaces = [
    'startup',
    'config-options',
    'script-execution',
    'builtins',
    'argv-environment',
    'working-directory',
    'pipelines',
    'redirections',
    'cancellation',
    'history',
    'completion',
    'structured-data',
    'typed-capture',
    'structured-errors',
    'dynamic-external',
    'status-conditions',
    'glob',
    'unicode-multiline-editing',
    'job-semantics',
    'clean-exit',
    'language-values',
    'language-control',
    'functions-modules',
    'intrinsics',
    'launcher-frontends',
    'language-server',
    'documentation-examples',
    'intentional-refusals',
    ]
    let matrix = $bundle.matrix
    if $matrix.required_surfaces != $required_surfaces {
        throw 'required surfaces do not preserve the complete ordered target set'
    }
    if $bundle.report.target_matrix != 'flashos-x86_64-target-matrix-v1.toml' {
        throw 'capability report does not reference the selected target matrix'
    }
    if $bundle.report_count == 0 {
        throw 'capability report has no capability array'
    }
    if $bundle.classification_count != $bundle.report_count {
        throw 'capability classification does not cover the report'
    }
    if $bundle.operation_count == 0 {
        throw 'capability classification has no operation array'
    }
    if $bundle.omitted_name == null {
        throw 'selected adapter withholds an unknown capability'
    }
    if $matrix.withheld_capabilities != [$bundle.omitted_name] {
        throw 'withheld capabilities do not match the selected adapter'
    }
    if $bundle.reported_withheld != [$bundle.omitted_name] {
        throw 'capability report and selected adapter disagree on the withheld set'
    }
    mut case_index = 0
    for selected_case in $matrix.cases {
        let identifier = $selected_case.id
        for surface in $selected_case.surfaces {
            if !($surface in $required_surfaces) {
                throw "case '$identifier' has unknown surfaces"
            }
        }
        for capability in $selected_case.capabilities {
            if !($capability in $bundle.advertised) {
                throw "case '$identifier' has unadvertised capabilities"
            }
        }
        mut operation_index = 0
        for operation_id in $selected_case.operation_ids {
            let capability = $bundle.case_operation_capabilities[$case_index][$operation_index]
            if $capability == null {
                throw "case '$identifier' references unknown operation '$operation_id'"
            }
            if !($capability in $selected_case.capabilities) {
                throw "case '$identifier' operation '$operation_id' belongs to undeclared capability '$capability'"
            }
            $operation_index = $operation_index + 1
        }
        mut step_index = 0
        for step in $selected_case.steps {
            let observation = $bundle.step_observations[$case_index][$step_index]
            if $step.send == 'script' {
                if $observation.script_reader_bytes + $bundle.terminator_bytes > $matrix.max_interaction_bytes {
                    throw "case '$identifier' script reader exceeds the target UART boundary"
                }
            }
            if $step.send == 'line' && !$observation.starts_declared_prompt {
                throw "case '$identifier' line rendering does not start with a declared prompt"
            }
            mut interaction_bytes = $observation.payload_bytes
            if $step.send == 'line' {
                $interaction_bytes = $interaction_bytes + $bundle.terminator_bytes
            }
            if $step.send in ['line', 'keys'] && $interaction_bytes > $matrix.max_interaction_bytes {
                throw "case '$identifier' interactive input exceeds the target UART boundary"
            }
            $step_index = $step_index + 1
        }
        $case_index = $case_index + 1
    }
    if !same_set($bundle.seen_surfaces, $required_surfaces) {
        throw 'matrix cases do not cover every required surface'
    }
    if !same_set($bundle.seen_capabilities, $bundle.advertised) {
        throw 'matrix cases do not cover every advertised capability'
    }
    if !same_set($bundle.seen_operations, $bundle.expected_operations) {
        throw 'matrix operations must have complete single ownership'
    }
    for operation in $bundle.seen_operations {
        mut occurrences = 0
        for candidate in $bundle.seen_operations {
            if $candidate == $operation {
                $occurrences = $occurrences + 1
            }
        }
        if $occurrences != 1 {
            throw 'matrix operations must have complete single ownership'
        }
    }
    return true
}

let root = repository_root('versions.env')
let jq = require_jq()
let rg = require_rg()
let runtime_program = selected_tool(
'FLASH_AUTOMATION_RUNTIME',
'components/flash/target/debug/fsh',
)
let runtime = require_runtime($runtime_program)
mut temporary_parent = env('TMPDIR')
if $temporary_parent == null {
    $temporary_parent = '/tmp'
}
let temporary = "$(^mktemp -d "$temporary_parent/flashos-target-validation.XXXXXX")"
if !$status.ok {
    matrix_error('cannot create temporary directory')
}
let errors = "$temporary/errors"
let matrix = "$temporary/matrix.json"
let report = "$temporary/report.json"
let classification = "$temporary/classification.json"
let bundle = "$temporary/bundle.json"
^env $runtime ci/flashos_target_matrix.fsh --output json-v1 > $matrix 2> $errors
if !$status.ok {
    ^cat $errors 1>&2
    ^rm -rf $temporary
    matrix_error('target-matrix contract is invalid')
}
let report_document = toml_to_json(
"$root/components/flash/platforms/flashos-x86_64-capability-report-v1.toml",
$errors,
)
^printf '%s\n' $report_document > $report
let classification_document = toml_to_json(
"$root/components/flash/platforms/flashos-x86_64-capability-classification.toml",
$errors,
)
^printf '%s\n' $classification_document > $classification
let omitted_variant = "$(^env $rg --only-matching --replace '$1' 'Capabilities::full_without\(Capability::([A-Za-z]+)\)' "$root/components/flash/crates/flash-platform-flashos/src/lib.rs")"
if $omitted_variant == '' {
    ^rm -rf $temporary
    matrix_error('selected adapter capability declaration has an unknown shape')
}
^env $jq \
--slurpfile report $report \
--slurpfile classification $classification \
--arg omitted_variant $omitted_variant \
'. as $matrix | ($report[0].capability | map(select(.rust_variant == $omitted_variant)) | .[0].name) as $omitted_name | {matrix:$matrix, report:$report[0], classification:$classification[0], report_count:(try ($report[0].capability|length) catch 0), classification_count:(try ($classification[0].capability|length) catch 0), operation_count:(try ($classification[0].operation|length) catch 0), omitted_name:$omitted_name, advertised:(try [$report[0].capability[]|select(.advertised == true)|.name] catch []), reported_withheld:(try [$report[0].capability[]|select(.advertised == false)|.name] catch []), case_operation_capabilities:(try [.cases[] | [.operation_ids[] as $id | ([$classification[0].operation[] | select(.id == $id) | .capability][0] // null)]] catch []), seen_surfaces:(try [.cases[].surfaces[]] catch []), seen_capabilities:(try [.cases[].capabilities[]] catch []), seen_operations:(try [.cases[].operation_ids[]] catch []), expected_operations:(try [$classification[0].operation[] | .capability as $name | select(any($report[0].capability[]; .name == $name and .advertised == true)) | .id] catch []), terminator_bytes:(.terminator.data|length/2), step_observations:(try [.cases[] | [.steps[] | . as $step | {payload_bytes:(if $step.payload.encoding == "utf8" then ($step.payload.text|utf8bytelength) else ($step.payload.data|length/2) end), script_reader_bytes:("^head -c" + ((if $step.payload.encoding == "utf8" then ($step.payload.text|length) else ($step.payload.data|length/2) end)|tostring) + ">m" | utf8bytelength), starts_declared_prompt:($step.rendered != null and ([ $matrix.prompts[].text | . as $prompt | ($step.rendered.text | startswith($prompt)) ] | any))}]] catch [])}' \
$matrix > $bundle 2> $errors
if !$status.ok {
    matrix_error('cannot project the target-matrix relationships')
}
try {
    open $bundle | from json | each {|document| validate($document)} | to json > /dev/null
} catch error {
    let message = $error.message
    ^rm -rf $temporary
    matrix_error($message)
}
for marker in [
'load_target_matrix(args.target_matrix)',
'script_transport_chunks(',
'for case in target_matrix.cases:',
'for step in case.steps:',
] {
    if ^env $rg --fixed-strings --quiet -- $marker "$root/ci/qemu_smoke.py" {
    } else {
        ^rm -rf $temporary
        matrix_error("QEMU runner does not consume the target matrix: $marker")
    }
}
if ^env $rg --fixed-strings --quiet -- 'ci/check_flashos_target_matrix.fsh' "$root/.github/workflows/ci.yml" {
} else {
    ^rm -rf $temporary
    matrix_error('standard CI does not validate the target matrix')
}
^rm -rf $temporary
^printf 'FlashOS target matrix: advertised capability contract passed\n'

Return to documentation →