frank-large: cutover to flash-server v2 (abliterated Swift1.5)

Engine: halogen-flash-server 0.16.2, digest-pinned (pulled 0c61bf84).
Payload: Quat3rnion qwen38-flash-next-v2-swift15-abliterated.hgn
(66,687,678,464 B, sha256-verified) + REQUIRED n-gram table
(51,200,246,144 B @peonist 5cc17cea) + vision tower (897,916,416 B
@peonist e053f488) + shared tokenizer (6 files). Shape: 2 slots x
200192 (KV pool 400384 positions, ~10.7 GiB), MTP always-on
(HALOGEN_SPEC_ADAPT=0, card-mandated), server-side sampler at the
card's 1.0/0.95/20, MAX_TOK 8192 with the 65536/7200 coupled pair,
vision ON. Rollback = revert this commit (27B payload stays on disk).

- serve script: byte-exact gates on all three .hgn files + flat
  tokenizer, foreground podman run (ledger to the log file)
- deploy-remote: twin gates for n-gram/vision, flashnext fetch pointer
- seam: 19 tests rewritten (spec-adapt law, pool product, compaction
  math, render shape); full suite 362/362 green
This commit is contained in:
Frank committed 2026-10-06 03:07:40 -07:00
1 parent 7fef3cb907
commit 31b1ec397b
6 files changed
+495 -266

No files matched your search

+126 -68
View File
@@ -1,18 +1,20 @@
#!/usr/bin/env bats
# Seam: frank-large serves peonist-ai/halogen-qwen3.8-27b (a .hgn halogen
# checkpoint) on the Strix Halo iGPU through the halogen CONTAINER
# (ghcr.io/peonist-ai/halogen, digest-pinned), driven by the root unit's
# serve script — one conversation of 200,700 tokens with the DFlash2
# drafter ON. 2026-10-05 cutover from the ROCmFPX llama.cpp shape.
# Seam: frank-large serves the Quat3rnion ABLITERATED Swift1.5 finetune of
# peonist's Qwen3.8-Flash-Next v2 checkpoint (a .hgn flash-server payload)
# on the Strix Halo iGPU through the halogen-flash-server CONTAINER (0.16.2,
# digest-pinned), driven by the root unit's serve script — two 200,192-token
# slots with the MTP head always on (HALOGEN_SPEC_ADAPT=0) and the vision
# tower mounted. 2026-10-06 cutover from the halogen 27B-dense shape.
#
# This seam exists because the artifacts are a triangle that must agree on
# exact strings the engine reads: the image DIGEST, the checkpoint file
# name AND byte count inside the /models mount, and the tokenizer's
# flat-file shape (a HF cache snapshot's symlinks dangle inside the
# container). A serve gate and a deployer gate that disagree is the
# exact strings the engine reads: the image DIGEST, the checkpoint/n-gram/
# vision file names AND byte counts inside the /models mount, and the
# tokenizer's flat-file shape (a HF cache snapshot's symlinks dangle inside
# the container). A serve gate and a deployer gate that disagree is the
# 2026-10-01 crash-loop; both sides are pinned here, plus the one engine
# law the fleet must never "optimize" away: HALOGEN_KV_SLOTS=1 is what
# keeps speculative decoding on.
# law the card is emphatic about: HALOGEN_SPEC_ADAPT=0 — the default
# adaptive policy silently disables the MTP head per request and HALVES
# decode.
#
# Everything is tested from what an operator or supervisor can observe —
# the command line podman would run, the exit code, the deployer's error
@@ -36,7 +38,7 @@ teardown() {
# podman_stub — a fake podman on PATH that prints the argv of `run` and
# `wait` and answers `images` with whatever digests the test names, so
# "what would the supervisor run" is observable output and the digest
# gate is testable without a 3.5 GB image.
# gate is testable without a 3.6 GB image.
podman_stub() {
mkdir -p "$TEST_TMP/bin"
cat > "$TEST_TMP/bin/podman" <<'STUB'
@@ -52,14 +54,14 @@ STUB
}
# good_env — the minimum env the serve script demands, with the digest
# stub agrees on and zero-byte artifacts passing the size gate.
# stub agrees on and zero-byte artifacts passing the size gates.
good_env() {
: > "$TEST_TMP/empty.env"
touch "$TEST_TMP/qwen.hgn"
touch "$TEST_TMP/qwen.hgn" "$TEST_TMP/ngram.hgn" "$TEST_TMP/vision.hgn"
mkdir -p "$TEST_TMP/tokenizer" && touch "$TEST_TMP/tokenizer/tok.json"
GOOD_ENV=(
"FRANK_LARGE_ENV_FILE=$TEST_TMP/empty.env"
"FRANK_LARGE_IMAGE=ghcr.io/peonist-ai/halogen@sha256:cafe"
"FRANK_LARGE_IMAGE=ghcr.io/peonist-ai/halogen-flash-server@sha256:cafe"
"FRANK_LARGE_CONTAINER=frank-large-halogen"
"FRANK_LARGE_LEGACY_CONTAINERS=frank-large"
"FRANK_LARGE_DEV_KFD=$TEST_TMP/qwen.hgn"
@@ -69,42 +71,56 @@ good_env() {
"FRANK_LARGE_MODEL_DIR=$TEST_TMP"
"FRANK_LARGE_CHECKPOINT=qwen.hgn"
"FRANK_LARGE_CHECKPOINT_BYTES=0"
"FRANK_LARGE_NGRAM_TABLE=ngram.hgn"
"FRANK_LARGE_NGRAM_BYTES=0"
"FRANK_LARGE_VISION_TOWER=vision.hgn"
"FRANK_LARGE_VISION_BYTES=0"
"FRANK_LARGE_TOKENIZER_FILES=tok.json"
"HALOGEN_MODEL_ID=frank-large"
"HALOGEN_SLOT_CTX=200700"
"HALOGEN_KV_SLOTS=1"
"HALOGEN_DRAFTER=2"
"HALOGEN_CACHE_MB=16384"
"STUB_DIGESTS=localhost/x ghcr.io/peonist-ai/halogen@sha256:cafe"
"HALOGEN_CTX=200192"
"HALOGEN_KV_POOL_POSITIONS=400384"
"HALOGEN_KV_SLOTS=2"
"HALOGEN_SPEC_ADAPT=0"
"HALOGEN_MAX_TOK=8192"
"STUB_DIGESTS=localhost/x ghcr.io/peonist-ai/halogen-flash-server@sha256:cafe"
)
}
@test "the env file defines the halogen deployment values" {
grep -qE '^FRANK_LARGE_IMAGE=ghcr\.io/peonist-ai/halogen@sha256:[0-9a-f]{64}$' "$ENV_FILE"
@test "the env file defines the flash-server deployment values" {
grep -qE '^FRANK_LARGE_IMAGE=ghcr\.io/peonist-ai/halogen-flash-server@sha256:[0-9a-f]{64}$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_CONTAINER=frank-large-halogen$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_PUBLISH=7770:8731$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_CHECKPOINT=.*\.hgn$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_CHECKPOINT_BYTES=[0-9]+$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_CHECKPOINT_SHA256=[0-9a-f]{64}$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_NGRAM_TABLE=.*\.hgn$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_NGRAM_BYTES=[0-9]+$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_NGRAM_SHA256=[0-9a-f]{64}$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_VISION_TOWER=.*\.hgn$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_VISION_BYTES=[0-9]+$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_VISION_SHA256=[0-9a-f]{64}$' "$ENV_FILE"
grep -qE '^FRANK_LARGE_TOKENIZER_FILES="tokenizer\.json( [a-z_.]+)+"$' "$ENV_FILE"
grep -qE '^HALOGEN_MODEL_ID=frank-large$' "$ENV_FILE"
grep -qE '^HALOGEN_SLOT_CTX=[0-9]+$' "$ENV_FILE"
grep -qE '^HALOGEN_CTX=[0-9]+$' "$ENV_FILE"
}
@test "the drafter stays on: slots must stay 1" {
# halogen makes batching and speculation mutually exclusive — with
# more than one slot the drafter is DISABLED and per-stream decode
# falls to serial speed (the sibling engine measured 41 -> 14 t/s
# when slots went up). frank-large was a one-session endpoint before
# this swap too (PARALLEL=1), so slots=1 preserves the contract;
# raising it is a product decision, never a default change.
grep -qE '^HALOGEN_KV_SLOTS=1$' "$ENV_FILE"
grep -qE '^HALOGEN_DRAFTER=2$' "$ENV_FILE"
@test "the head stays on: adaptive spec must stay 0 and the window shape pinned" {
# The card is emphatic: the default adaptive policy turns the MTP head
# off for whole requests after low acceptance and HALVES decode. The
# two-slot law does NOT carry over from the 27B engine — on this line
# batching and speculation coexist (the card's quickstart ships 2
# slots WITH the head), so 2 is concurrent capacity, not a tax.
grep -qE '^HALOGEN_SPEC_ADAPT=0$' "$ENV_FILE"
grep -qE '^HALOGEN_KV_SLOTS=2$' "$ENV_FILE"
# The cap/timeout pair is coupled (FLAGS.md table): a request at the
# cap must not outlast the queue timeout, or everyone queued behind
# it eats a 503.
# it eats a 503. The per-request default must stay under the cap.
grep -qE '^HALOGEN_MAX_TOK=[0-9]+$' "$ENV_FILE"
grep -qE '^HALOGEN_MAX_TOKENS_CAP=[0-9]+$' "$ENV_FILE"
grep -qE '^HALOGEN_QUEUE_TIMEOUT=[0-9]+$' "$ENV_FILE"
mt="$(sed -nE 's/^HALOGEN_MAX_TOK=([0-9]+)$/\1/p' "$ENV_FILE")"
cap="$(sed -nE 's/^HALOGEN_MAX_TOKENS_CAP=([0-9]+)$/\1/p' "$ENV_FILE")"
[ "$mt" -le "$cap" ]
}
@test "the serve script refuses when there is no GPU to serve on" {
@@ -116,20 +132,25 @@ good_env() {
[[ "$output" == *"no gfx1151 to serve on"* ]]
}
@test "the KV pool plus pinned cache fit unified memory with margin" {
# Pool arithmetic is the engine's documented product: slots x
# slot_ctx x 64 KiB. The prompt cache is PINNED (HALOGEN_CACHE_MB,
# ~70 KB/token per entry — 16384 MB >= one full-slot entry). Against
# both: 33.4 GiB of mapped weights, ~20 GiB engine working set, host
# baseline and frank-small co-tenancy. The box has 121 GiB unified;
# the 2026-09-16 halogen era ran the same engine class here on a
# LARGER pool with less co-tenancy slack.
ctx="$(sed -nE 's/^HALOGEN_SLOT_CTX=([0-9]+)$/\1/p' "$ENV_FILE")"
@test "the pool product and memory ledger fit unified memory with margin" {
# Pool arithmetic is the engine's documented product: the explicit
# HALOGEN_KV_POOL_POSITIONS must equal slots x ctx, and KV costs
# ~28 KiB/position (FLAGS.md). Against it: ~62 GiB resident weights,
# the arena scratch 3.8 + 8.4*(MAX_TOK/16384) GiB, the vision tower
# ~2 GiB pool charge, the 20 GiB host reserve and the host baseline
# + co-tenancy (frank-small, ComfyUI, Caddy/observability) ~15. The
# box has 121 GiB unified. The n-gram table rides as mmap'd page
# cache beside the checkpoint and is not a pinned charge (the card's
# own quickstart arithmetic does not bill it).
ctx="$(sed -nE 's/^HALOGEN_CTX=([0-9]+)$/\1/p' "$ENV_FILE")"
slots="$(sed -nE 's/^HALOGEN_KV_SLOTS=([0-9]+)$/\1/p' "$ENV_FILE")"
cache_mb="$(sed -nE 's/^HALOGEN_CACHE_MB=([0-9]+)$/\1/p' "$ENV_FILE")"
pool_gib=$(( slots * ctx * 65536 / 1073741824 ))
cache_gib=$(( ${cache_mb:-0} / 1024 ))
total_gib=$(( 34 + pool_gib + cache_gib + 20 + 15 ))
pool="$(sed -nE 's/^HALOGEN_KV_POOL_POSITIONS=([0-9]+)$/\1/p' "$ENV_FILE")"
mt="$(sed -nE 's/^HALOGEN_MAX_TOK=([0-9]+)$/\1/p' "$ENV_FILE")"
reserve="$(sed -nE 's/^HALOGEN_HOST_RESERVE_GIB=([0-9]+)$/\1/p' "$ENV_FILE")"
[ "$pool" -eq $(( slots * ctx )) ]
kv_gib=$(( pool * 28 * 1024 / 1073741824 ))
arena_tenths=$(( 38 + 84 * mt / 16384 ))
total_gib=$(( 62 + kv_gib + arena_tenths / 10 + 2 + reserve + 15 ))
[ "$total_gib" -le 121 ]
}
@@ -149,12 +170,12 @@ good_env() {
FRANK_LARGE_CHECKPOINT=absent.hgn "$SERVE"
[ "$status" -ne 0 ]
[[ "$output" == *"checkpoint"*"missing"* ]]
[[ "$output" == *"download-frank-large-halogen.sh"* ]]
[[ "$output" == *"download-frank-large-flashnext.sh"* ]]
}
@test "the serve script refuses a checkpoint of the wrong byte count" {
# A truncated curl exit-0 artifact must not boot; the model card's
# byte count is the contract (fleet fetch law).
# A truncated curl exit-0 artifact must not boot; the Hub's byte
# count is the contract (fleet fetch law).
podman_stub
good_env
run env PATH="$TEST_TMP/bin:$PATH" "${GOOD_ENV[@]}" \
@@ -163,6 +184,38 @@ good_env() {
[[ "$output" == *"expected 42"* ]]
}
@test "the serve script refuses a missing or wrong-size n-gram table" {
# The lookup table is REQUIRED: the engine reads it beside the
# checkpoint (the card names it explicitly), and prompt-lookup
# drafting dies without it.
podman_stub
good_env
run env PATH="$TEST_TMP/bin:$PATH" "${GOOD_ENV[@]}" \
FRANK_LARGE_NGRAM_TABLE=absent.hgn "$SERVE"
[ "$status" -ne 0 ]
[[ "$output" == *"n-gram-table"*"missing"* ]]
run env PATH="$TEST_TMP/bin:$PATH" "${GOOD_ENV[@]}" \
FRANK_LARGE_NGRAM_BYTES=42 "$SERVE"
[ "$status" -ne 0 ]
[[ "$output" == *"expected 42"* ]]
}
@test "the serve script refuses a missing or wrong-size vision tower" {
# Optional on the engine, ENABLED here (image input is a fleet
# contract) — a silent absence would degrade the endpoint to
# text-only with no error anywhere.
podman_stub
good_env
run env PATH="$TEST_TMP/bin:$PATH" "${GOOD_ENV[@]}" \
FRANK_LARGE_VISION_TOWER=absent.hgn "$SERVE"
[ "$status" -ne 0 ]
[[ "$output" == *"vision-tower"*"missing"* ]]
run env PATH="$TEST_TMP/bin:$PATH" "${GOOD_ENV[@]}" \
FRANK_LARGE_VISION_BYTES=42 "$SERVE"
[ "$status" -ne 0 ]
[[ "$output" == *"expected 42"* ]]
}
@test "the serve script refuses a symlinked tokenizer file" {
# A HuggingFace cache snapshot's entries are symlinks into blobs/ and
# dangle inside the container mount — even when the directory itself
@@ -189,28 +242,32 @@ $(cat "$TEST_TMP/serve.log")"
[[ "$output" == *"--name frank-large-halogen"* ]]
[[ "$output" == *"--publish 7770:8731"* ]]
[[ "$output" == *"-e HALOGEN_MODEL_ID=frank-large"* ]]
[[ "$output" == *"-e HALOGEN_SLOT_CTX=200700"* ]]
[[ "$output" == *"-e HALOGEN_KV_SLOTS=1"* ]]
[[ "$output" == *"-e HALOGEN_DRAFTER=2"* ]]
[[ "$output" == *"-e HALOGEN_CTX=200192"* ]]
[[ "$output" == *"-e HALOGEN_KV_POOL_POSITIONS=400384"* ]]
[[ "$output" == *"-e HALOGEN_KV_SLOTS=2"* ]]
[[ "$output" == *"-e HALOGEN_SPEC_ADAPT=0"* ]]
[[ "$output" == *"-e HALOGEN_CHECKPOINT=/models/qwen.hgn"* ]]
[[ "$output" == *"-e HALOGEN_NGRAM_TABLE=/models/ngram.hgn"* ]]
[[ "$output" == *"-e HALOGEN_VISION_TOWER=/models/vision.hgn"* ]]
[[ "$output" == *"--device $TEST_TMP/qwen.hgn"* ]]
[[ "$output" == *"$TEST_TMP/tokenizer:/tokenizer:ro"* ]]
[[ "$output" == *"ghcr.io/peonist-ai/halogen@sha256:cafe"* ]]
[[ "$output" == *"ghcr.io/peonist-ai/halogen-flash-server@sha256:cafe"* ]]
# Foreground run, never -d: the engine ledger must land in the log
# file (`podman run -d` captured only the container ID and the
# runbook's ledger grep read a one-line hex file, found 2026-10-06).
! grep -qE '(^|[[:space:]])-d([[:space:]]|$)' "$TEST_TMP/serve.log"
! grep -q 'PODMAN wait' "$TEST_TMP/serve.log"
[[ "$output" == *"-e HALOGEN_CACHE_MB=16384"* ]]
}
@test "the shipped env pins the fleet's window and shape" {
# 200700 is the per-conversation hard ceiling (a longer prompt is a
# 400 naming the limit, never a silent truncation), and the harness
# 200192 is the per-slot hard ceiling (a longer prompt is a 400
# naming the limit, never a silent truncation), and the harness
# compaction math must fit prompt + budget under it: OMP compacts at
# 70% = 140490, and 140490 + 57344 = 197834 <= 200700.
grep -qE '^HALOGEN_SLOT_CTX=200700$' "$ENV_FILE"
ctx=200700
# 70% = 140134, and 140134 + 57344 = 197478 <= 200192. models.yml
# contextWindow must quote the same number or the harness discovers
# the wall as a 400.
grep -qE '^HALOGEN_CTX=200192$' "$ENV_FILE"
ctx=200192
[ $(( ctx * 70 / 100 + 57344 )) -le "$ctx" ]
}
@@ -230,22 +287,23 @@ $(cat "$TEST_TMP/serve.log")"
grep -qF "LLM_LARGE_IMAGE=\"\${LLM_LARGE_IMAGE:-$img}\"" "$DEPLOYER"
dir="$(sed -nE 's/^FRANK_LARGE_MODEL_DIR=(.*)$/\1/p' "$ENV_FILE")"
ckpt="$(sed -nE 's/^FRANK_LARGE_CHECKPOINT=(.*)$/\1/p' "$ENV_FILE")"
tokf="$(sed -nE 's/^FRANK_LARGE_TOKENIZER_FILES="(.*)"$/\1/p' "$ENV_FILE")"
tokf="$(sed -nE 's/^FRANK_LARGE_TOKENIZER_FILES=\"(.*)\"$/\1/p' "$ENV_FILE")"
grep -qF "LLM_LARGE_TOK_DIR=\"\${LLM_LARGE_TOK_DIR:-$dir/tokenizer}\"" "$DEPLOYER"
grep -qF "LLM_LARGE_TOK_FILES=\"\${LLM_LARGE_TOK_FILES:-$tokf}\"" "$DEPLOYER"
bytes="$(sed -nE 's/^FRANK_LARGE_CHECKPOINT_BYTES=([0-9]+)$/\1/p' "$ENV_FILE")"
grep -qF "LLM_LARGE_CKPT_BYTES=\"\${LLM_LARGE_CKPT_BYTES:-$bytes}\"" "$DEPLOYER"
}
@test "the deployer points a missing artifact at the halogen fetch script" {
grep -qE 'download-frank-large-halogen\.sh' "$DEPLOYER"
@test "the deployer points a missing artifact at the flashnext fetch script" {
grep -qE 'download-frank-large-flashnext\.sh' "$DEPLOYER"
grep -qE 'podman pull' "$DEPLOYER"
}
@test "the stray sweep cannot take frank-small or the deployer down" {
# frank-small also listens on this host (7771, llama-server) and the
# deployer's own command line contains "frank-large"; only the FULL
# container name matches a stray manual halogen run and nothing else.
# container name matches a stray manual flash-server run and nothing
# else.
grep -q "pkill -f 'frank-large-halogen'" "$DEPLOYER"
! grep -qE "pkill -f 'llama-server'" "$DEPLOYER"
! grep -qE "pkill -f 'frank-large'" "$DEPLOYER"
@@ -262,8 +320,8 @@ $(cat "$TEST_TMP/serve.log")"
grep -qE '^ExecStop=.*podman stop .* frank-large-halogen$' "$UNIT"
}
@test "the unit runs the halogen runtime and no retired path" {
grep -q 'halogen' "$UNIT"
@test "the unit runs the flash-server runtime and no retired path" {
grep -q 'halogen-flash-server' "$UNIT"
# Every retired payload/build stays on the host, so a stale path in
# a unit still resolves — to a runtime nobody has measured in weeks.
! grep -qE 'GitPackages/(ROCmFPX-kairic|llama.cpp-ik_llama|llama-builds|ciru-orca)' "$UNIT"
@@ -271,9 +329,9 @@ $(cat "$TEST_TMP/serve.log")"
}
@test "the rollback door stays qualified: the ROCmFPX build pin survives" {
# Rolling back this swap means rebuilding the fork the old payload
# needs; the pin (real commit, gfx1151, no webui fetch) is what makes
# that door trustworthy months later.
# Rolling back to the pre-halogen shape means rebuilding the fork the
# old payload needs; the pin (real commit, gfx1151, no webui fetch)
# is what makes that door trustworthy months later.
grep -qE 'ROCMFPX_COMMIT=.*[0-9a-f]{40}' "$BUILD"
grep -qE 'GPU_TARGET=.*gfx1151' "$BUILD"
grep -qE 'DLLAMA_BUILD_WEBUI=OFF' "$BUILD"