Files
serverAppConfigs/tests/frank_archon_unit.bats
T
Frank 9de58ed6fd feat(frank-archon): move the endpoint to radiance 1.8.1 and its budget model
The decode stall is step contention (measured: 18.7 ms decode-only steps
against 317 ms steps that carry a prefill chunk, decode at 13 t/s median
while prefill runs), and the two engine knobs that fix it arrived after this
endpoint's pin. Moving to 1.8.1 (2026-10-18) brings both, and brings two CLI
breaks with them:

  - --vram-weights-mib / --vram-kv-mib were REMOVED at 1.3.0. The per-card
    split between the expert slab and the KV pool is now derived by the
    engine's budget resolver from --gpu-headroom-mib, the cache bound
    (--max-model-len, --max-num-seqs) and --host-pool-mib, and the cache may
    lend idle blocks to the expert slab. The slab size and the pool's token
    count are boot-ledger lines from here on, so the capacity contract
    (3 x 200,000 fp8) is verified live by the probe's capacity leg instead of
    computed from an env number.
  - The prefix cache has no disk tier any more: --prefix-cache-dir and
    --prefix-cache-disk-mib are refusals and --prefix-cache-host-mib is
    --prefix-cache-mib. 64 GiB of parked KV on NVMe becomes re-prefill, so
    prefix_hit_rate is the thing to watch after this boot.

env: image re-pinned to the 1.8.1 digest; the pair and the disk tier dropped;
FRANK_ARCHON_PREFIX_CACHE_MIB replaces the host/disk pair; new
FRANK_ARCHON_BUSY_PREFILL_CHUNK=auto (explicit, because auto vs off is the
difference between decode waiting one chunk and waiting a whole step) and
FRANK_ARCHON_PREFILL_INTERLEAVE=0 (opt-in through a guarded argv array).

serve: argv follows the new CLI, /data mount and the pre-created kvcache path
go with the disk tier, and the retired env keys are no longer required (they
would abort ExecStart). autoresearch-frank-archon.sh takes the same CLI, with
a cold prefill now coming from --prefix-cache-mib 0 instead of a per-run disk
directory. run-swift15-bf16.sh is untouched: it pins its own engines by design.

tests: the two budget-arithmetic seams asserted knobs that no longer exist and
are replaced by one seam that asserts the retired knobs are ABSENT from env and
argv - a stale line is a boot refusal, not a no-op. The container-contract seam
now asserts the tune mount and the absence of /data.
2026-10-19 10:18:24 -07:00

427 lines
22 KiB
Bash
Executable File

#!/usr/bin/env bats
# Seam: frank-archon — the Dankserver LLM endpoint on :7770.
#
# This file asserts the RADIANCE shape (StillDeadcode engine, digest-pinned
# stilldeadcode/radiance image, BOTH R9700s TP=2 on the wht6 wire, serving
# the publisher's 4-bit-codebook .rad of the official Qwen3.8-Flash-Next
# (fetched by scripts/download-frank-archon-iq4r.sh; the in-house
# Swift1.5 w4dn8 .rad of scripts/convert-swift15-w4dn8.sh is the on-disk
# rollback). It replaces the FreeToken seam of
# 2026-10-05 (git tag frank-archon-freetoken-pr587-20261005) and pins what
# flags alone do not say: the served window clears the harness worst case,
# the retired per-card budget knobs are ABSENT (the pinned engine refuses
# them at ExecStart), the pinned-host ledger has a ttm pages cap floor, the
# serve script ENFORCES
# it and RAISES the knob itself when a reboot reset it (the cap's default is
# half of RAM), the served window is what models.yml must
# quote, the host-networking contract (no --publish), the image gate on a
# name@sha256 ref, the .rad payload gate with its size floor, the
# chat-template override mount, the self-healing law (the run returns before
# the engine is up, so the serve script must wait on the container for
# Restart=always to mean anything), the deployer's false-green MainPID check,
# the vendored hfget fetch path (no token for this ungated repo), and the
# client contract (reasoning auto -> reasoning_content; models.yml maps the
# field on the harness side, not here).
load helpers
UNIT="$REPO_ROOT/scripts/frank-archon.service"
SERVE="$REPO_ROOT/scripts/serve-frank-archon.sh"
TTM_LIB="$REPO_ROOT/scripts/ttm-preflight.sh"
ENV_FILE="$REPO_ROOT/scripts/frank-archon.env"
REMOTE_DEPLOY="$REPO_ROOT/scripts/deploy-remote.sh"
DOWNLOAD="$REPO_ROOT/scripts/download-frank-archon-iq4r.sh"
BUILD="$REPO_ROOT/scripts/convert-swift15-w4dn8.sh"
HFGET="$REPO_ROOT/scripts/hfget.sh"
TEMPLATE="$REPO_ROOT/scripts/qwen38-froggeric-v22.5.jinja"
# A PATH with `cat` (the gate reads knobs with it) and NO `sudo`, so the
# privileged fallback cannot rescue a write the test means to fail.
nosudo_bin() {
local bin="$BATS_TEST_TMPDIR/nosudo-bin"
mkdir -p "$bin"
ln -sf "$(command -v cat)" "$bin/cat"
ln -sf "$(command -v bash)" "$bin/bash"
echo "$bin"
}
@test "frank-archon unit, serve script, env, fetch and build scripts and template exist" {
[ -f "$UNIT" ]
[ -x "$SERVE" ]
[ -f "$TTM_LIB" ]
[ -f "$ENV_FILE" ]
[ -f "$DOWNLOAD" ]
[ -x "$BUILD" ]
[ -x "$HFGET" ]
[ -f "$TEMPLATE" ]
# The FreeToken-era Dockerfile is retired with the runtime it built:
# the radiance image is the publisher's, pulled and digest-pinned, not
# built on the host.
[ ! -f "$REPO_ROOT/scripts/docker/frank-archon-freetoken.Dockerfile" ]
}
@test "frank-archon unit is boot-persistent, self-healing and unprivileged" {
# Root unit parity with frank-dragoon/frank-zealot: enable is the [Install]
# section, the restart policy survives a crash, and the pinned host RAM
# (expert pools + PLE table + prefix host tier, ~132 GiB) needs an
# unbounded memlock (a root unit grants it via PID1; the dank session's
# own hard limit is 8 MiB).
grep -q '^WantedBy=multi-user.target$' "$UNIT"
# Restart=always, not on-failure: a clean front-end exit after an
# engine fault reads as success to systemd, so on-failure never fires.
grep -q '^Restart=always$' "$UNIT"
! grep -q '^Restart=on-failure$' "$UNIT"
# ExecStop runs `podman stop -t 120`, so the stop timeout MUST exceed
# 120 s or systemd kills the stop and records 'Failed with result
# timeout' (measured 2026-10-01). Guard the relationship, not the
# literal.
stop_t=$(awk -F= '/^TimeoutStopSec=/{print $2}' "$UNIT")
[ -n "$stop_t" ] && [ "$stop_t" -gt 120 ]
grep -q '^ExecStop=.*podman stop -t 120' "$UNIT"
# StartLimitIntervalSec belongs to [Unit]; a copy in [Service] is an
# 'Unknown key' warning systemd logs on every load.
[ "$(grep -c '^StartLimitIntervalSec=' "$UNIT")" = "1" ]
awk '/^\[Service\]/{in_svc=1} /^StartLimitIntervalSec=/{if(in_svc) exit 1}' "$UNIT"
grep -q '^RestartSec=' "$UNIT"
grep -q '^User=dank$' "$UNIT"
grep -q '^LimitMEMLOCK=infinity$' "$UNIT"
}
@test "frank-archon runs the repo's own serve script with the repo's env" {
grep -q '^ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-archon.sh$' "$UNIT"
grep -q 'FRANK_ARCHON_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-archon.env' "$UNIT"
}
@test "the unit stops the container child explicitly — container identity is the unit name" {
# The serve script blocks on podman wait, which cannot stop the
# container child; systemd stop goes through ExecStop or the engine
# outlives the unit and holds the GPU. The container name IS the
# service name across runtimes (Frank's 2026-10-05 call).
grep -q '^ExecStop=/usr/bin/podman stop' "$UNIT"
grep -q 'podman stop -t 120 frank-archon$' "$UNIT"
grep -q '^FRANK_ARCHON_CONTAINER=frank-archon$' "$ENV_FILE"
grep -q 'frank-archon-qwen' "$ENV_FILE"
}
@test "the serve shape is direct podman run, not a launcher or a predecessor runtime" {
grep -q 'podman run' "$SERVE"
! grep -q 'launch.sh' "$SERVE"
! grep -q 'podman-shim' "$SERVE"
! grep -q 'R9V_STATE_FILE' "$SERVE"
# Every predecessor runtime is RETIRED — its image name and its
# engine-specific flags must not survive in the radiance run line.
! grep -q 'tcclaviger' "$SERVE"
! grep -q 'tcclaviger' "$ENV_FILE"
! grep -q 'freetoken\|ft serve' "$SERVE"
! grep -q 'freetoken' "$ENV_FILE"
! grep -q 'HIP_VISIBLE_DEVICES' "$SERVE"
! grep -q -- '--tensor-parallel-size' "$SERVE"
! grep -q -- '--moe-cache-rate' "$SERVE"
! grep -q -- '--num-tokens ' "$SERVE"
}
@test "the image is the publisher's, digest-pinned by manifest" {
# stilldeadcode/radiance@sha256:<64 hex> — a name@digest ref exists in
# storage only if THAT manifest was pulled. The tag form would float.
grep -qE '^FRANK_ARCHON_IMAGE=stilldeadcode/radiance@sha256:[0-9a-f]{64}$' "$ENV_FILE"
}
@test "the radiance engine shape is in the env file" {
# Served window must clear the harness worst case: OMP compacts at 70%
# of contextWindow; for 200,000 that is 140,000, and a pre-compaction
# prompt can legally reach that height — 140,492 + 36,864 = 177,356 at
# the FreeToken-era window was the record; the same relation must hold
# here (140,000 + 36,864 = 176,864 <= 200,000).
grep -q '^FRANK_ARCHON_MAX_MODEL_LEN=200000$' "$ENV_FILE"
ml=$(awk -F= '/^FRANK_ARCHON_MAX_MODEL_LEN=/{print $2}' "$ENV_FILE")
[ "$ml" -ge 177356 ]
# Concurrency: 3 slots x the served window is the capacity contract, and
# the fused TP all-reduce is declared over seqs x (1 + spec) = 9 rows,
# under the 25-row bound at which the wht6 wire stays bit-exact. The
# "seqs 2 cannot boot" law belongs to the retired tcclaviger vLLM fork
# (torch.compile query_start_loc constraint, 2026-10-02); radiance booted
# seqs 2 in the fp8-native era and the 2026-10-06 sweep had seqs 2 and 3
# tied inside the +-3% noise floor — slots buy concurrency, not speed.
grep -q '^FRANK_ARCHON_MAX_SEQS=3$' "$ENV_FILE"
grep -q '^FRANK_ARCHON_TP=2$' "$ENV_FILE"
grep -q '^FRANK_ARCHON_TP_WIRE=wht6$' "$ENV_FILE"
grep -q '^FRANK_ARCHON_SPEC_TOKENS=2$' "$ENV_FILE"
# expert_tiered placement: the pinned host pool plus whatever expert VRAM the
# budget resolver leaves serve the per-rank resident set TOGETHER, so the pool
# size is also the lever on which experts stay on the card. KV is QUANTIZED
# fp8 (the engine takes only bf16|fp8 — no per-tensor K/V split), the PLE
# n-gram table is pinned in host RAM per Frank's law. The per-card VRAM pair
# is NOT asserted here: 1.3.0 removed it and the resolver derives the split,
# so the pool's token count is a boot-ledger line — verified live by the
# capacity leg of scripts/probe-frank-archon-kv.py at every cutover.
grep -q '^FRANK_ARCHON_PLACEMENT=expert_tiered$' "$ENV_FILE"
grep -q '^FRANK_ARCHON_HOST_POOL_MIB=44928$' "$ENV_FILE"
grep -q '^FRANK_ARCHON_KV_DTYPE=fp8$' "$ENV_FILE"
grep -q '^FRANK_ARCHON_NGRAM_PLACEMENT=ram$' "$ENV_FILE"
# Prefix cache: pinned host RAM only (the disk tier was removed), and the
# step-sharing cap is stated explicitly — auto vs off is the difference
# between decode waiting one chunk and waiting a whole step.
grep -q '^FRANK_ARCHON_PREFIX_CACHE_MIB=8192$' "$ENV_FILE"
grep -q '^FRANK_ARCHON_BUSY_PREFILL_CHUNK=auto$' "$ENV_FILE"
# The pinned ledger (2 x pool + 47.7 GiB PLE + prefix host) must be
# under the ttm pages cap, and the env states the floor the serve
# script enforces (>= 140,000 MiB — the kernel's own default cap, half of
# RAM, fails it, which is the point) plus the value it raises the knob to
# when a reboot reset it (the target must clear the floor with slack).
grep -q '^FRANK_ARCHON_TTM_MIN_MIB=' "$ENV_FILE"
ttmm=$(awk -F= '/^FRANK_ARCHON_TTM_MIN_MIB=/{print $2}' "$ENV_FILE")
ttmt=$(awk -F= '/^FRANK_ARCHON_TTM_TARGET_MIB=/{print $2}' "$ENV_FILE")
[ "$ttmt" -gt "$ttmm" ]
pool=$(awk -F= '/^FRANK_ARCHON_HOST_POOL_MIB=/{print $2}' "$ENV_FILE")
ph=$(awk -F= '/^FRANK_ARCHON_PREFIX_CACHE_MIB=/{print $2}' "$ENV_FILE")
# 2 pools + PLE 47.7 GiB (48,824 MiB) + prefix host, rounded up.
ledger=$(( 2 * pool + 48824 + ph + 2048 ))
[ "$ttmm" -ge "$ledger" ]
# The .rad size floor is positive (a truncated convert must fail fast).
grep -q '^FRANK_ARCHON_RAD_MIN_MIB=' "$ENV_FILE"
[ "$(awk -F= '/^FRANK_ARCHON_RAD_MIN_MIB=/{print $2}' "$ENV_FILE")" -gt 0 ]
}
@test "the retired budget knobs are absent from the env and the argv" {
# Not unused leftovers: the pinned engine REFUSES to start on any of them
# (--vram-weights-mib/--vram-kv-mib went away at 1.3.0, the prefix-cache disk
# tier outright), and a unit that refuses at ExecStart sits in
# `activating (auto-restart)` while the port still looks alive from whatever
# held it last. A stale line here is an outage, so it is asserted away.
for key in FRANK_ARCHON_VRAM_WEIGHTS_MIB FRANK_ARCHON_VRAM_KV_MIB \
FRANK_ARCHON_PREFIX_HOST_MIB FRANK_ARCHON_PREFIX_DISK_MIB; do
! grep -q "^${key}=" "$ENV_FILE"
! grep -q "$key" "$SERVE"
done
for flag in --vram-weights-mib --vram-kv-mib --prefix-cache-host-mib \
--prefix-cache-dir --prefix-cache-disk-mib; do
! grep -q -- "$flag" "$SERVE"
done
}
@test "the serve script wires the engine flags from the env" {
grep -q -- '--model "/models/$FRANK_ARCHON_MODEL_FILE"' "$SERVE"
grep -q -- '--served-model-name "$FRANK_ARCHON_SERVED_NAME"' "$SERVE"
grep -q -- '--host :: --port "$FRANK_ARCHON_PORT"' "$SERVE"
grep -q -- '--override-chat-template /templates/template.jinja' "$SERVE"
grep -q -- '--tp "$FRANK_ARCHON_TP" --tp-wire "$FRANK_ARCHON_TP_WIRE"' "$SERVE"
grep -q -- '--max-num-seqs "$FRANK_ARCHON_MAX_SEQS"' "$SERVE"
grep -q -- '--max-model-len "$FRANK_ARCHON_MAX_MODEL_LEN"' "$SERVE"
grep -q -- '--placement "$FRANK_ARCHON_PLACEMENT"' "$SERVE"
grep -q -- '--host-pool-mib "$FRANK_ARCHON_HOST_POOL_MIB"' "$SERVE"
grep -q -- '--kv-cache-dtype "$FRANK_ARCHON_KV_DTYPE"' "$SERVE"
grep -q -- '--prefix-cache-mib "$FRANK_ARCHON_PREFIX_CACHE_MIB"' "$SERVE"
grep -q -- '--busy-prefill-chunk "$FRANK_ARCHON_BUSY_PREFILL_CHUNK"' "$SERVE"
# Interleave is opt-in: it reaches argv only through the guarded array, so a
# 0 in the env leaves the engine's own default in place.
grep -q -- 'PREFILL_ARGS=(--prefill-interleave "$FRANK_ARCHON_PREFILL_INTERLEAVE")' "$SERVE"
grep -qF -- '"${PREFILL_ARGS[@]}"' "$SERVE"
grep -q -- '--ngram-placement "$FRANK_ARCHON_NGRAM_PLACEMENT"' "$SERVE"
grep -q -- '--num-speculative-tokens "$FRANK_ARCHON_SPEC_TOKENS"' "$SERVE"
# The boot-ladder escape hatch is opt-in: unset renders NO extra argv.
grep -q -- '${FRANK_ARCHON_EXTRA_ARGS:+$FRANK_ARCHON_EXTRA_ARGS}' "$SERVE"
! grep -q '^FRANK_ARCHON_EXTRA_ARGS=.' "$ENV_FILE"
}
@test "the container contract is the publisher's (host network, io_uring, pin, cards)" {
# docs/DOCKER.md: EVERY one of these is needed — host networking (the
# server binds the host's addresses; no --publish), --init (the engine
# releases the cards on SIGTERM), seccomp=unconfined (the default
# profile refuses io_uring, which the n-gram reads use, and the NUMA
# binding calls), memlock unbounded (the pinned ledger), kfd + dri.
grep -q -- '--network host' "$SERVE"
! grep -q -- '--publish' "$SERVE"
! grep -q -- '-p 7770' "$SERVE"
grep -q '^FRANK_ARCHON_PORT=7770$' "$ENV_FILE"
grep -q '^FRANK_ARCHON_SERVED_NAME=frank-archon$' "$ENV_FILE"
grep -q -- '--init' "$SERVE"
grep -q -- '--security-opt seccomp=unconfined' "$SERVE"
grep -q -- '--ulimit memlock=-1' "$SERVE"
grep -q -- '--device /dev/kfd' "$SERVE"
grep -q -- '--device /dev/dri' "$SERVE"
grep -q -- '-v "$FRANK_ARCHON_MODEL_DIR":/models:ro' "$SERVE"
grep -q -- '-v "$FRANK_ARCHON_STATE_DIR/tune":/opt/radiance/share/radiance/tune' "$SERVE"
# No /data mount: the prefix cache lives in pinned host RAM now, so the only
# host state the container writes is the rad-tune cache above.
! grep -q -- ':/data ' "$SERVE"
grep -q -- '-v "$FRANK_ARCHON_TEMPLATE":/templates/template.jinja:ro' "$SERVE"
}
@test "parsers are AUTO — the engine infers them natively, the harness maps the field" {
# radiance --reasoning-format defaults to auto and emits thinking in
# `reasoning_content`; tool calls ride the chat format. NO parser
# override flags exist in this engine. models.yml's
# reasoningContentField is the harness-side contract and lives in the
# operator's home, not this repo.
! grep -q -- '--reasoning-parser' "$SERVE"
! grep -q -- '--tool-call-parser' "$SERVE"
! grep -q -- '--enable-auto-tool-choice' "$SERVE"
! grep -q 'FRANK_ARCHON_REASONING_PARSER' "$ENV_FILE"
! grep -q 'FRANK_ARCHON_TOOL_PARSER' "$ENV_FILE"
}
@test "the serve script gates the image, the .rad payload, the template and the GPU nodes" {
# The image gate is the digest ref itself; the payload gate checks the
# .rad file and its size floor — a truncated convert would otherwise be
# a guaranteed long-timeout boot instead of a fast failure. The ttm
# preflight refuses to start under a pages cap below the pinned ledger.
grep -q 'FRANK_ARCHON_IMAGE' "$SERVE"
grep -q 'podman image exists' "$SERVE"
grep -q 'FRANK_ARCHON_MODEL_FILE' "$SERVE"
grep -q 'FRANK_ARCHON_RAD_MIN_MIB' "$SERVE"
grep -q 'FRANK_ARCHON_TEMPLATE' "$SERVE"
grep -q 'FRANK_ARCHON_TTM_MIN_MIB' "$SERVE"
grep -q 'pages_limit' "$SERVE"
grep -q 'pages_max' "$SERVE"
grep -q '/dev/kfd' "$SERVE"
grep -q -- '--device /dev/dri' "$SERVE"
}
@test "the ttm gate raises a reset pages cap and refuses when the write fails" {
# The cap is a RUNTIME knob: the kernel default is half of RAM, so every
# reboot resets it (measured: the 2026-10-12 23:17 power failure on
# dankserver took it 200 GiB -> 125,988 MiB, and the preflight refusal was
# re-run 6,919 times by Restart=always over ~20 h). Behaviour required of
# the gate, in both directions:
# knob below the floor + writable -> cap ends at the target, start passes
# knob below the floor + unwritable -> cap stays low, start REFUSES by name
local dir="$BATS_TEST_TMPDIR/ttm"
mkdir -p "$dir"
export FRANK_ARCHON_TTM_SYS_DIR="$dir"
export FRANK_ARCHON_TTM_MIN_MIB=150000 FRANK_ARCHON_TTM_TARGET_MIB=204800
echo 32253039 > "$dir/pages_limit" # 125,988 MiB: the kernel default here
run bash -c '. "$1"; ttm_ensure_cap' _ "$TTM_LIB"
[ "$status" -eq 0 ]
[ "$(cat "$dir/pages_limit")" = "52428800" ] # 204,800 MiB in 4 KiB pages
echo 32253039 > "$dir/pages_limit"
chmod 444 "$dir/pages_limit" # the write refused: cap cannot move
# The refusal case must not have `sudo` on PATH: the gate's privileged
# fallback runs `sudo -n`, and on a passwordless host root can write a 444
# file, which would silently test nothing.
run env PATH="$(nosudo_bin)" bash -c '. "$1"; ttm_ensure_cap' _ "$TTM_LIB"
[ "$status" -eq 1 ]
[[ "$output" == *"ERROR: ttm pages cap is 125988 MiB"* ]]
[ "$(cat "$dir/pages_limit")" = "32253039" ]
}
@test "the ttm cap counts only the knobs the kernel exposes" {
# An absent knob is not a zero: a zero-cap reading would refuse a healthy
# host. The effective cap is the MIN over the knobs that exist; pages_max,
# when a kernel exposes one, is the binding one.
local dir="$BATS_TEST_TMPDIR/ttm2"
mkdir -p "$dir"
export FRANK_ARCHON_TTM_SYS_DIR="$dir"
export FRANK_ARCHON_TTM_MIN_MIB=150000 FRANK_ARCHON_TTM_TARGET_MIB=204800
echo 51200000 > "$dir/pages_limit" # 200,000 MiB, no pages_max knob
run bash -c '. "$1"; echo "$(ttm_cap_mib)"' _ "$TTM_LIB"
[ "$status" -eq 0 ]
[ "$output" = "200000" ]
echo 30000000 > "$dir/pages_max" # 117,187 MiB: now the binding knob
run bash -c '. "$1"; echo "$(ttm_cap_mib)"' _ "$TTM_LIB"
[ "$output" = "117187" ]
chmod 444 "$dir/pages_limit" "$dir/pages_max" # both writes refused
run env PATH="$(nosudo_bin)" bash -c '. "$1"; ttm_ensure_cap' _ "$TTM_LIB"
[ "$status" -eq 1 ]
[[ "$output" == *"ERROR: ttm pages cap is 117187 MiB"* ]]
[ "$(cat "$dir/pages_max")" = "30000000" ]
}
@test "the gate runs as a root pre-start hook, where the knob write is legal" {
# The knob is root-owned 0644 and the service runs as User=dank, so the
# gate CANNOT raise it from the service process. systemd's `+` prefix on
# ExecStartPre runs as root even under User= — the canonical privileged
# path — and the gate's own --raise mode is what that line calls.
grep -q '^ExecStartPre=+/home/dank/serverAppConfigs/scripts/ttm-preflight.sh --raise$' "$UNIT"
# Behaviour of that entry point: sourced env values, knob raised, exit 0.
local dir="$BATS_TEST_TMPDIR/ttm3" envf="$BATS_TEST_TMPDIR/ttm3.env"
mkdir -p "$dir"
printf 'FRANK_ARCHON_TTM_MIN_MIB=150000\nFRANK_ARCHON_TTM_TARGET_MIB=204800\n' > "$envf"
echo 32253039 > "$dir/pages_limit"
run env FRANK_ARCHON_ENV_FILE="$envf" FRANK_ARCHON_TTM_SYS_DIR="$dir" \
bash "$TTM_LIB" --raise
[ "$status" -eq 0 ]
[ "$(cat "$dir/pages_limit")" = "52428800" ]
# Usage is a named failure, not a silent no-op.
run bash "$TTM_LIB"
[ "$status" -eq 2 ]
[[ "$output" == *"--raise"* ]]
}
@test "the serve script clears stale containers and self-heals via podman wait" {
# A crashed unit can leave the container name registered forever after;
# the legacy identities hold the same GPUs and die here too. Then block
# on the container so a crash is a nonzero ExecStart.
grep -q 'FRANK_ARCHON_LEGACY_CONTAINERS' "$ENV_FILE"
grep -q 'frank-archon-qwen' "$ENV_FILE"
grep -q 'frank-archon-r9v' "$ENV_FILE"
grep -q 'qwen-fp8' "$ENV_FILE"
grep -q 'exec podman wait "$FRANK_ARCHON_CONTAINER"' "$SERVE"
}
@test "the model id matches the payload's fetch script (and the rollback's build script)" {
# The deployed payload is the PUBLISHER's .rad: the fetch script names
# the repo, the file, and the Hub's LFS sha256; nothing is converted on
# the host for it. The in-house w4dn8 rollback keeps its own trio
# (convert-swift15-w4dn8.sh + download-frank-archon-swift15.sh, and the
# fp8-native era keeps build-frank-archon-rad.sh +
# download-frank-archon.sh).
grep -q '^FRANK_ARCHON_MODEL_DIR=/home/dank/models/rad$' "$ENV_FILE"
grep -q '^FRANK_ARCHON_MODEL_FILE=qwen3.8-next-flash-fp8-iq4r-moe.rad$' "$ENV_FILE"
grep -q 'StillDeadcode/qwen3.8-next-flash-fp8-iq4r-moe' "$DOWNLOAD"
grep -q 'qwen3.8-next-flash-fp8-iq4r-moe.rad' "$DOWNLOAD"
grep -q '0af5e96244e80c21ac8edca1719dae49b8a201b94a2f3994c3e24026ceaa4d20' "$DOWNLOAD"
grep -q 'hfget.sh' "$DOWNLOAD"
# The repo is UNGATED — no token may be required on the fetch path.
! grep -q 'HF_TOKEN' "$DOWNLOAD"
# Rollback provenance: the w4dn8 build script names its payload.
grep -q 'qwen4exp-swift15-w4dn8-i8-hc8m.rad' "$BUILD"
}
@test "no bare HIP-reserved env var is defined" {
# HIP reads bare TEMP for its JIT cache path (2026-08-13: TEMP=1.0
# segfaulted every load), and HOST/GPU_MAX_HEAP_SIZE are the same
# class. Every key here is FRANK_ARCHON_-prefixed.
! grep -qE '^(TEMP|HOST|GPU_MAX_HEAP_SIZE)=' "$ENV_FILE"
! grep -qE '^(export )?(TEMP|HOST|GPU_MAX_HEAP_SIZE)=' "$SERVE"
}
@test "the deployer gates the image, payload and containers, then health-checks" {
# The deployer refuses when the image is absent from dank's rootless
# storage or the .rad is missing/too small — then clears stale
# containers (including the legacy identities), waits out the port,
# relinks/enables/restarts the root unit and health-gates it.
grep -q 'LLM_ARCHON_CONTAINER=frank-archon$' "$REMOTE_DEPLOY"
grep -q 'FRANK_ARCHON_IMAGE' "$REMOTE_DEPLOY"
grep -q 'podman image exists' "$REMOTE_DEPLOY"
grep -q 'FRANK_ARCHON_MODEL_FILE' "$REMOTE_DEPLOY"
grep -q 'FRANK_ARCHON_RAD_MIN_MIB' "$REMOTE_DEPLOY"
grep -q 'frank-archon-qwen' "$REMOTE_DEPLOY"
grep -q 'qwen-fp8' "$REMOTE_DEPLOY"
grep -q 'glm-probe' "$REMOTE_DEPLOY"
grep -q 'wait_port_free 7770' "$REMOTE_DEPLOY"
grep -q 'relink_unit frank-archon' "$REMOTE_DEPLOY"
grep -q 'systemctl enable frank-archon.service' "$REMOTE_DEPLOY"
grep -q 'systemctl restart frank-archon.service' "$REMOTE_DEPLOY"
# The health gate is a bare curl: the HTTP listener binds AFTER the
# full load (main.cpp — engine.start() then engine.serve()), so during
# load the port refuses connections and a bare curl is honest. The
# FreeToken-era body read ('status ok') is gone with that engine.
grep -q 'curl -fsS http://127.0.0.1:7770/health' "$REMOTE_DEPLOY"
! grep -q '"status":"ok"' "$REMOTE_DEPLOY"
}
@test "the deployer will not report green on a stray port-holder" {
# The 2026-09-26 false green: a leftover probe held :7770 and satisfied
# the health curl while the real unit was stopped. The deploy is only
# green when the unit's own MainPID is live behind the health answer.
grep -q 'systemctl show -p MainPID --value frank-archon.service' "$REMOTE_DEPLOY"
grep -q 'stray process holds the port' "$REMOTE_DEPLOY"
}