#!/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 KV pool backs seqs x max-model-len at the declared cache dtype (an # engine HARD limit, not a ratio), the per-card VRAM pair stays inside the # 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: vram_weights + the pinned host pool serve the # per-rank resident set TOGETHER, so cutting the VRAM slab to pay for the # KV pool moves the pool with it. 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. grep -q '^FRANK_ARCHON_PLACEMENT=expert_tiered$' "$ENV_FILE" grep -q '^FRANK_ARCHON_HOST_POOL_MIB=44928$' "$ENV_FILE" grep -q '^FRANK_ARCHON_VRAM_WEIGHTS_MIB=[1-9]' "$ENV_FILE" grep -q '^FRANK_ARCHON_VRAM_KV_MIB=[1-9]' "$ENV_FILE" grep -q '^FRANK_ARCHON_KV_DTYPE=fp8$' "$ENV_FILE" grep -q '^FRANK_ARCHON_NGRAM_PLACEMENT=ram$' "$ENV_FILE" # Prefix cache tiers (publisher pattern; disk tier sized for 2 slots). grep -q '^FRANK_ARCHON_PREFIX_HOST_MIB=8192$' "$ENV_FILE" grep -q '^FRANK_ARCHON_PREFIX_DISK_MIB=65536$' "$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_HOST_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 KV pool backs seqs x max-model-len at the declared cache dtype" { # The engine's own hard limit: a request longer than the pool's token # count is "accepted by the API and then refused as too large to ever fit # -- this is a hard limit, not a backing ratio". Rates are measured, not # derived: per rank, kv_attn (13 layers) pages 26.00 KiB per 4 tokens at # fp8 and 52.00 KiB at bf16, while kv_qsa_bkey (13 layers) pages 3.25 KiB # at BOTH — the QSA block key is an index, not attention state, and does # not follow --kv-cache-dtype — so the rate is 7,488 B/token at fp8 and # 14,144 at bf16 (1.89x, not 2x). The dtype-independent terms also live in # the pool: ~110 MiB a sequence of recurrent state (kv_gdn_state 54 MiB x 2 # copies + conv/tail/ple) and 448 MiB of prefix checkpoints (8 x 55.99). # Verified against BOTH live boots: fp8 3543-330-448 = 2765 MiB -> 387,240 # tokens (ledger 386,904); bf16 6912-220-448 = 6244 MiB -> 462,946 # (ledger 462,752). Both within 0.1%. kv=$(awk -F= '/^FRANK_ARCHON_VRAM_KV_MIB=/{print $2}' "$ENV_FILE") seqs=$(awk -F= '/^FRANK_ARCHON_MAX_SEQS=/{print $2}' "$ENV_FILE") maxlen=$(awk -F= '/^FRANK_ARCHON_MAX_MODEL_LEN=/{print $2}' "$ENV_FILE") case "$(awk -F= '/^FRANK_ARCHON_KV_DTYPE=/{print $2}' "$ENV_FILE")" in fp8) bpt=7488 ;; bf16) bpt=14144 ;; *) fail "unknown FRANK_ARCHON_KV_DTYPE" ;; esac tokens=$(( (kv - 110 * seqs - 448) * 1048576 / bpt )) [ "$tokens" -ge $(( seqs * maxlen )) ] } @test "the per-card VRAM pair stays inside the measured card ceiling" { # The engine's OWN budget table, not a hand-picked reserve. Boot of # 2026-10-13 at the pair 24000/5120: card 32,624 MiB (31.86 GiB), budgets # claimed 29,120, `3.42 GiB is neither budgeted nor accounted — the # driver's context, the activation arena and --gpu-headroom-mib come out of # it; anything past those is throughput on the floor`, and `card 0 settled # at 2.01 GiB free against 209.19 MiB (the 96.00 MiB --gpu-headroom-mib and # 113.19 MiB kept for the static region)`. Claimable reserve above the # engine's floor: 2,058 - 209 = 1,849 MiB, so the ceiling is 30,969 MiB. # The 2026-10-11 bound of 29,120 ("keep >= 1 GiB free", card read as 31,856 # MiB) was MY reserve, which the engine never asked for; the shipped pair # spends the difference and lands ~458 MiB above the floor. w=$(awk -F= '/^FRANK_ARCHON_VRAM_WEIGHTS_MIB=/{print $2}' "$ENV_FILE") kv=$(awk -F= '/^FRANK_ARCHON_VRAM_KV_MIB=/{print $2}' "$ENV_FILE") [ $(( w + kv )) -le 30969 ] } @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 -- '--vram-weights-mib "$FRANK_ARCHON_VRAM_WEIGHTS_MIB"' "$SERVE" grep -q -- '--vram-kv-mib "$FRANK_ARCHON_VRAM_KV_MIB"' "$SERVE" grep -q -- '--kv-cache-dtype "$FRANK_ARCHON_KV_DTYPE"' "$SERVE" grep -q -- '--prefix-cache-host-mib "$FRANK_ARCHON_PREFIX_HOST_MIB"' "$SERVE" grep -q -- '--prefix-cache-disk-mib "$FRANK_ARCHON_PREFIX_DISK_MIB"' "$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":/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" }