#!/usr/bin/env bats # Seam: frank-primary — the Dankserver LLM endpoint on :7770. # # This file asserts the tcclaviger RDNA4 vLLM ROOT-UNIT shape (the 2026-09-30 # cutover to GLM-5.3-Flash W4A16 int4, which replaced the qwen FP8 + MTP4 # shape): the serve script drives `podman run` DIRECTLY (the image entrypoint # execs `vllm serve`), with every engine flag qualified by boot probe on # 2026-09-30 and carried in the repo env file. The seam pins what the flags # alone do not say: the qualification law (131,072 x 2 seqs at bf16 KV, util # 0.98 is what boots; the window is forced up by the fork's KV reserve model, # not by capacity), the offload law (170 GiB of experts cannot fit 64 GiB of # VRAM — the engine MUST run with --enable-expert-offload), the self-healing # law (the run returns before the engine is up, so the serve script must wait # on the container for Restart=on-failure to mean anything), the memlock law # that makes the 73.64 GiB/rank expert pin legal, the deployer's image/payload # gates with the false-green MainPID check, and the three measured-absent # flags: expert prefetch (36% precision, costs ~40-50% decode), MTP spec # decode (buys zero decode and collapses prefill on an offload-bound shape), # and fp8 KV (disables the only MLA decode path gfx12 has). load helpers UNIT="$REPO_ROOT/scripts/frank-primary.service" SERVE="$REPO_ROOT/scripts/serve-frank-primary.sh" ENV_FILE="$REPO_ROOT/scripts/frank-primary.env" REMOTE_DEPLOY="$REPO_ROOT/scripts/deploy-remote.sh" DOWNLOAD="$REPO_ROOT/scripts/download-frank-primary.sh" @test "frank-primary unit, serve script and env exist in scripts/" { [ -f "$UNIT" ] [ -x "$SERVE" ] [ -f "$ENV_FILE" ] } @test "frank-primary is boot-persistent, self-healing and unprivileged" { # Root unit parity with frank-large/frank-small: enable is the [Install] # section, the restart policy survives a crash, and the tiered-expert # pin needs an unbounded memlock (a root unit grants it via PID1; the # dank session's own hard limit is 8 MiB and cannot be raised without # restarting user@1000.service). grep -q '^WantedBy=multi-user.target$' "$UNIT" grep -q '^Restart=on-failure$' "$UNIT" grep -q '^RestartSec=' "$UNIT" grep -q '^User=dank$' "$UNIT" grep -q '^LimitMEMLOCK=infinity$' "$UNIT" } @test "frank-primary runs the repo's own serve script with the repo's env" { grep -q '^ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-primary.sh$' "$UNIT" grep -q 'FRANK_PRIMARY_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-primary.env' "$UNIT" } @test "the unit stops the container child explicitly" { # 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 both GPUs. The container name must match # the env file's identity. grep -q '^ExecStop=/usr/bin/podman stop' "$UNIT" grep -q 'frank-primary-glm' "$UNIT" grep -q '^FRANK_PRIMARY_CONTAINER=frank-primary-glm$' "$ENV_FILE" } @test "the serve shape is direct podman run, not a launcher or shim" { # The tcclaviger stack is fully expressed by flags; the R9V launcher, # docker->podman shim and setup-state layering are the RETIRED shape. grep -q 'podman run' "$SERVE" ! grep -q 'launch.sh' "$SERVE" ! grep -q 'podman-shim' "$SERVE" ! grep -q 'R9V_STATE_FILE' "$SERVE" ! grep -q -- '--model-draft' "$SERVE" ! grep -q -- '--spec-type' "$SERVE" } @test "the qualified engine shape is in the env file" { # 2026-09-30 boot probe: 131,072 x 2 seqs at bf16 KV, util 0.98 boots and # serves (measured "GPU KV cache size: 138,561 tokens"). 0.99 fails the # free-memory wall. The window is not a capacity choice: the engine's own # reserve model (one max_model_len request) needs a large L for this # model's per-sequence KDA state, and below ~92k the boot REFUSES with # "needed ... larger than the available KV cache memory". NBATCH pins the # prefill chunk that was measured. grep -q '^FRANK_PRIMARY_MAXLEN=131072$' "$ENV_FILE" grep -q '^FRANK_PRIMARY_SEQS=2$' "$ENV_FILE" grep -q '^FRANK_PRIMARY_UTIL=0.98$' "$ENV_FILE" grep -q '^FRANK_PRIMARY_NBATCH=2048$' "$ENV_FILE" grep -q '^FRANK_PRIMARY_MODEL_SHARDS=34$' "$ENV_FILE" } @test "the serve script wires the engine flags from the env" { grep -q -- '--max-num-seqs "$FRANK_PRIMARY_SEQS"' "$SERVE" grep -q -- '--max-model-len "$FRANK_PRIMARY_MAXLEN"' "$SERVE" grep -q -- '--max-num-batched-tokens "$FRANK_PRIMARY_NBATCH"' "$SERVE" grep -q -- '--tensor-parallel-size 2' "$SERVE" grep -q -- '--enable-prefix-caching' "$SERVE" grep -q -- '--trust-remote-code' "$SERVE" } @test "the endpoint advertises the GLM tool dialect" { # Without --enable-auto-tool-choice the engine rejects tool_choice "auto" # with a 400 ("requires --enable-auto-tool-choice and --tool-call-parser"), # which breaks every tool-using agent on the endpoint. grep -q -- '--enable-auto-tool-choice' "$SERVE" grep -q -- '--tool-call-parser "$FRANK_PRIMARY_TOOLPARSER"' "$SERVE" grep -q -- '--reasoning-parser "$FRANK_PRIMARY_REASONINGPARSER"' "$SERVE" grep -q '^FRANK_PRIMARY_TOOLPARSER=glm47$' "$ENV_FILE" grep -q '^FRANK_PRIMARY_REASONINGPARSER=glm47$' "$ENV_FILE" } @test "expandable segments is ON — the cache arena leaves no slack" { # The expert-offload plan fills VRAM to the modelled headroom, so the # profile/graph-capture phase has ~0.2 GiB to work with and the default # allocator's fragmentation fails it: measured 2026-09-30 on the deployed # unit, "Tried to allocate 662.00 MiB ... 240.00 MiB is free" from the # engine core while the identical shape booted with this env set. grep -q 'PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True' "$SERVE" } @test "expert offload is ON — the payload cannot fit VRAM without it" { # 170 GiB of int4 experts against 64 GiB of VRAM. Without the offload flag # the engine has no home for the expert tensors at all; the arena # (14.51 GiB = 2,383 slots) plus 73.64 GiB/rank of pinned host rows is the # only shape that boots. --expert-precise-memory is the qualified sizing. grep -q -- '--enable-expert-offload' "$SERVE" grep -q -- '--expert-precise-memory' "$SERVE" } @test "MTP spec decode is absent BY MEASUREMENT, not by omission" { # 2026-09-30: the MTP head loads and accepts well (mean accepted length # 3.76-4.90), but decode was unchanged (9.6-11.2 vs 10.0 tok/s) while # prefill collapsed 918 -> 255 tok/s — the verification forward gathers the # union of the batch's experts, so spec decode does not amortize a # residency-bound gather. It also costs 2.6 GiB of arena. ! grep -q -- '--speculative-config' "$SERVE" ! grep -q 'speculative' "$ENV_FILE" } @test "fp8 KV is absent — it would disable the only MLA decode path on gfx12" { # _use_rocm_sparse_triton() in the ROCM_AITER_MLA_SPARSE backend requires a # non-fp8 KV cache, and that rope-free Triton path is the only MLA decode # available on gfx12: aiter's MLA decode op is registered under # @if_aiter_supported, gated on CDNA>2. With fp8 KV the MTP draft step dies # with "_OpNamespace 'vllm' object has no attribute # 'rocm_aiter_mla_decode_fwd'". KV dtype therefore stays auto/bf16. ! grep -q -- '--kv-cache-dtype' "$SERVE" ! grep -q '^FRANK_PRIMARY_KV_DTYPE=' "$ENV_FILE" } @test "expert prefetch is absent BY LAW, not by omission" { # Telemetry (2026-09-29): the prefetcher served 13,369 of 36,720 # prefetched slabs (36% precision) — roughly half the gather traffic was # waste, and the "savings" would have been installs anyway. Deleting the # flag moved decode 26.8 -> 40.6 tok/s. Any future flag must come with a # fresh A/B, so the serve script must NOT wire it unconditionally. ! grep -q -- '--expert-prefetch' "$SERVE" ! grep -q 'expert_prefetch' "$ENV_FILE" grep -q 'PREFETCH IS ABSENT BY LAW' "$ENV_FILE" } @test "TunableOp stays off" { # CLAV_TUNABLEOP_SWEEP silently skips under expert offload unless # CLAV_TUNABLEOP_WITH_OFFLOAD=1, and forcing it corrupts numerics # (repetition loops, early halts, invalid measurements). ! grep -q 'TUNABLEOP' "$ENV_FILE" ! grep -q 'TUNABLEOP' "$SERVE" } @test "the serve script gates the digest-pinned image and the payload" { # The image is pinned by OCI manifest digest (the floating :latest moved # once already). The payload gate checks config.json and the exact shard # count from the env — a missing shard means a guaranteed long-timeout # boot instead of a fast failure. grep -q 'FRANK_PRIMARY_IMAGE' "$SERVE" grep -q 'RepoDigests' "$SERVE" grep -q 'FRANK_PRIMARY_MODEL_SHARDS' "$SERVE" grep -q 'config.json' "$SERVE" grep -q '/dev/kfd' "$SERVE" grep -q -- '--device /dev/dri' "$SERVE" ! grep -q 'tcclaviger/vllm:latest' "$ENV_FILE" } @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 (the retired R9V container and the ad hoc # experiment container) hold the same GPUs and die here too. Then block # on the container so a crash is a nonzero ExecStart. grep -q 'FRANK_PRIMARY_LEGACY_CONTAINERS' "$ENV_FILE" grep -q 'frank-primary-r9v' "$ENV_FILE" grep -q 'qwen-fp8' "$ENV_FILE" grep -q 'exec podman wait "$FRANK_PRIMARY_CONTAINER"' "$SERVE" } @test "frank-primary holds the single fleet port under its own alias" { # The scratch identity (qwen3.8-flash-next on :7773) must never leak # into production: LAN publish of :7770, the harness's model id. grep -q '^FRANK_PRIMARY_PUBLISH=7770:7770$' "$ENV_FILE" grep -q '^FRANK_PRIMARY_SERVED_NAME=frank-primary$' "$ENV_FILE" } @test "the model id matches the payload's served alias wiring" { # The payload dir and its shard count are the fetch script's output # layout; the fetch script must write where the env points. grep -q '^FRANK_PRIMARY_MODEL_DIR=/home/dank/models/GLM-5.3-Flash-W4A16-AutoRound$' "$ENV_FILE" grep -q '^FRANK_PRIMARY_MODEL_SHARDS=34$' "$ENV_FILE" grep -q 'GLM-5.3-Flash-W4A16-AutoRound' "$DOWNLOAD" grep -q 'Intel/GLM-5.3-Flash-W4A16-AutoRound' "$DOWNLOAD" } @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_PRIMARY_-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 digest and payload, then health-checks" { # The deployer refuses when the pinned image is absent from rootless # storage or the payload is missing/wrong-sharded — 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_PRIMARY_CONTAINER=frank-primary-glm' "$REMOTE_DEPLOY" grep -q 'FRANK_PRIMARY_IMAGE' "$REMOTE_DEPLOY" grep -q "podman images --format '{{.RepoDigests}}'" "$REMOTE_DEPLOY" grep -q 'FRANK_PRIMARY_MODEL_SHARDS' "$REMOTE_DEPLOY" grep -q 'config.json' "$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-primary' "$REMOTE_DEPLOY" grep -q 'systemctl enable frank-primary.service' "$REMOTE_DEPLOY" grep -q 'systemctl restart frank-primary.service' "$REMOTE_DEPLOY" grep -q 'wait_for_health frank-primary http://127.0.0.1:7770/health 1800' "$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-primary.service' "$REMOTE_DEPLOY" grep -q 'stray process holds the port' "$REMOTE_DEPLOY" }