check_developer_interface.fsh

#!/usr/bin/env fsh
# Bash and Zsh are the public interfaces under test. ripgrep exposes bounded
# command inventories; Flash owns the expected surface, comparisons, refusal
# probes, and diagnostics.

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

def interface_error(message) {
    ^printf 'developer interface contract: %s\n' $message 1>&2
    exit 1
}

def require_success(program, arguments, label, errors) {
    let output = "$(^env $program ...$arguments 2> $errors)"
    if !$status.ok {
        mut details = "$(^cat $errors)"
        if $details == '' {
            $details = $output
        }
        if $details == '' {
            $details = "exit $status.code"
        }
        interface_error("$label failed: $details")
    }
    return $output
}

def require_absent(path, relative) {
    if ^test -e $path {
        interface_error("removed helper still exists: $relative")
    }
}

let commands = [
'status', 'doctor', 'version', 'versions', 'profile', 'env', 'build',
'run', 'smoke', 'qualify', 'recipe', 'artifacts', 'logs', 'changes',
'check', 'shell', 'podman', 'clean', 'root', 'list', 'help',
]
let direct_helpers = [
'flash-check', 'flashos', 'flashos-artifacts', 'flashos-build',
'flashos-changes', 'flashos-check', 'flashos-clean', 'flashos-doctor',
'flashos-env', 'flashos-list', 'flashos-logs', 'flashos-podman',
'flashos-profile', 'flashos-qualify', 'flashos-recipe', 'flashos-run',
'flashos-smoke', 'flashos-status', 'flashos-version', 'flashos-versions',
'fos',
]
let removed_commands = ['ask', 'commit', 'setup', 'log', 'change']
let command_lines = "$(^printf '%s\n' ...$commands)"
let direct_helper_lines = "$(^printf '%s\n' ...$direct_helpers)"
let removed_paths = [
'tools/flashos/flashos-ask.py',
'tools/flashos/flashos-commit.py',
'tools/flashos/flashos_ai.py',
'tools/flashos/contexts/flashos-ask-context.json',
'tools/flashos/contexts/flashos-commit-context.json',
]

let root = repository_root('versions.env')
let rg = require_rg()
mut temporary_parent = env('TMPDIR')
if $temporary_parent == null {
    $temporary_parent = '/tmp'
}
let temporary = "$(^mktemp -d "$temporary_parent/flash-developer-interface.XXXXXX")"
if !$status.ok {
    interface_error('cannot create temporary directory')
}
let errors = "$temporary/errors"
let public_text = "$temporary/public-text"

for relative in $removed_paths {
    require_absent("$root/$relative", $relative)
}
for relative in [
'flashos.sh',
'flashos.zsh',
'docs/development.md',
'docs/verification.md',
'.github/workflows/ci.yml',
] {
    ^cat "$root/$relative" >> $public_text
    ^printf '\n' >> $public_text
}
for command in ['ask', 'commit'] {
    if ^env $rg --fixed-strings --quiet -- "flashos $command" $public_text {
        interface_error("removed command remains documented or exposed: $command")
    }
}
for marker in ['tools/flashos/', 'flashshell-check'] {
    if ^env $rg --fixed-strings --quiet -- $marker $public_text {
        interface_error("removed helper boundary remains exposed: $marker")
    }
}

require_success('bash', ['-n', 'flashos.sh'], 'Bash syntax check', $errors)
let help_output = require_success(
'bash',
['-c', 'source ./flashos.sh; flashos help'],
'Bash help',
$errors,
)
let help_inventory = "$(^printf '%s\n' $help_output | ^env $rg --only-matching --replace '$1' '^  ([a-z][a-z-]*)(?:\s|\[|<)')"
if $help_inventory != $command_lines {
    interface_error('Bash help command inventory drifted')
}
let alias_output = require_success(
'bash',
['-c', 'source ./flashos.sh; fos help'],
'fos alias',
$errors,
)
if $alias_output != $help_output {
    interface_error('fos help differs from flashos help')
}
let completion_output = require_success(
'bash',
[
'-c',
'source ./flashos.sh; COMP_WORDS=(flashos ""); COMP_CWORD=1; _flashos_bash_completion; printf "%s\\n" "${COMPREPLY[@]}"',
],
'Bash completion',
$errors,
)
if $completion_output != $command_lines {
    interface_error('Bash completion command inventory drifted')
}
let list_output = require_success(
'bash',
['-c', 'source ./flashos.sh; flashos list'],
'direct helper list',
$errors,
)
let listed = "$(^printf '%s\n' $list_output | ^sed -n '/^== Direct helper functions ==$/,$p' | ^sed '1d' | ^env LC_ALL=C sort)"
if $listed != $direct_helper_lines {
    interface_error('direct helper inventory drifted')
}

let removed = "$(^printf '%s ' ...$removed_commands | ^sed 's/ $//')"
require_success(
'bash',
[
'-c',
"source ./flashos.sh; for name in $removed; do if flashos \"\$name\" >/dev/null 2>&1; then exit 1; fi; done",
],
'removed command rejection',
$errors,
)
require_success(
'bash',
[
'-c',
'source ./flashos.sh; for name in status doctor version root list help; do if flashos "$name" unexpected >/dev/null 2>&1; then exit 1; fi; done; if flashos profile dev unexpected >/dev/null 2>&1; then exit 1; fi',
],
'unexpected argument rejection',
$errors,
)
require_success(
'bash',
[
'-c',
'source ./flashos.sh; if flashos profile development >/dev/null 2>&1; then exit 1; fi; if flashos build harddrive >/dev/null 2>&1; then exit 1; fi; if flashos run iso >/dev/null 2>&1; then exit 1; fi; if flashos smoke harddrive >/dev/null 2>&1; then exit 1; fi; if flashos artifacts path harddrive >/dev/null 2>&1; then exit 1; fi; if flashos logs iso >/dev/null 2>&1; then exit 1; fi',
],
'legacy alias rejection',
$errors,
)
require_success(
'bash',
[
'-c',
'source ./flashos.sh; podman() { :; }; if flashos podman list >/dev/null 2>&1; then exit 1; fi',
],
'legacy Podman alias rejection',
$errors,
)

let static_commands = "$(^env $rg --only-matching --replace '$1' '^\s+\u0027([a-z][a-z-]*):' "$root/flashos.zsh")"
if $static_commands != $command_lines {
    interface_error('Zsh completion command inventory drifted')
}
^env zsh --version >/dev/null 2> $errors
if !$status.ok {
    ^printf 'developer interface contract: zsh unavailable; runtime Zsh checks skipped\n' 1>&2
} else {
    require_success('zsh', ['-n', 'flashos.sh'], 'Zsh shared syntax check', $errors)
    require_success('zsh', ['-n', 'flashos.zsh'], 'Zsh entrypoint syntax check', $errors)
    let zsh_help = require_success(
    'zsh',
    ['-f', '-c', 'source ./flashos.zsh; flashos help'],
    'Zsh help',
    $errors,
    )
    let zsh_help_inventory = "$(^printf '%s\n' $zsh_help | ^env $rg --only-matching --replace '$1' '^  ([a-z][a-z-]*)(?:\s|\[|<)')"
    if $zsh_help_inventory != $command_lines {
        interface_error('Zsh help command inventory drifted')
    }
    let zsh_completion = require_success(
    'zsh',
    [
    '-f',
    '-c',
    'compdef() { :; }; source ./flashos.zsh; _describe() { local name="$4"; eval "print -rl -- \${${name}[@]}"; }; CURRENT=2; words=(flashos ""); _flashos_zsh_completion',
    ],
    'Zsh completion',
    $errors,
    )
    let completed = "$(^printf '%s\n' $zsh_completion | ^sed 's/:.*$//')"
    if $completed != $command_lines {
        interface_error('Zsh runtime completion command inventory drifted')
    }
}

^rm -rf $temporary
^printf 'developer interface contract: ok\n'

Return to documentation →