refact(frank-dragoon): serve gates run on the shared gate library

The hand-written image/ck/tokenizer gates are require_image, require_bytes and
require_dir_files from scripts/lib/llm-gates.sh. The serve seam is unchanged
and still green (its podman stub now models `podman image exists`, the
primitive the gate uses). Behaviour preserved: every refusal names the
artifact, the endpoint and the fetch script.
This commit is contained in:
Frank
2026-10-13 21:40:02 -07:00
parent acf2a5cee5
commit 028afdeb58
4 changed files with 50 additions and 42 deletions
+4 -2
View File
@@ -69,12 +69,14 @@ require_min_mib() {
# require_dir_files — DIR must be a directory and every named member a
# regular file (the flat-tokenizer law: snapshot symlinks dangle in the
# container mount even when the directory itself is real).
# container mount even when the directory itself is real. A symlinked
# DIRECTORY is refused too: a HF cache snapshot can link the directory
# itself, and a link dangles inside the container mount.
require_dir_files() {
local dir="$1" label="$2" hint="$3"
shift 3
local f
if [[ ! -d "$dir" ]]; then
if [[ -L "$dir" || ! -d "$dir" ]]; then
_gate_fail "$label: missing at $dir" "$hint"
fi
for f in "$@"; do
+30 -40
View File
@@ -29,7 +29,14 @@
# exactly like frank-archon's.
set -euo pipefail
# The artifact gates are the shared library — the serve script and the
# deployer refuse the same missing/truncated payload with the same words
# (the 2026-10-01 crash-loop was the two hand-written twins disagreeing).
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export GATE_PREFIX=frank-dragoon
# shellcheck disable=SC1091
source "$SCRIPT_DIR/lib/llm-gates.sh"
ENV_FILE="${FRANK_DRAGOON_ENV_FILE:-$SCRIPT_DIR/frank-dragoon.env}"
if [ ! -f "$ENV_FILE" ]; then
echo "frank-dragoon: env file not found: $ENV_FILE" >&2
@@ -57,55 +64,37 @@ set +a
: "${HALOGEN_KV_SLOTS:?HALOGEN_KV_SLOTS must be set}"
: "${HALOGEN_SPEC_ADAPT:?HALOGEN_SPEC_ADAPT must be set (0 = head always on)}"
# Capture podman's output BEFORE grepping: under `set -o pipefail` the
# `podman images | grep -q` pattern races (grep exits on first match,
# podman dies SIGPIPE 141, pipefail turns the 141 into "no match" — the
# 2026-09-30 deploy refusal).
DIGESTS="$(podman images --format '{{.RepoDigests}}')"
if ! grep -qF "${FRANK_DRAGOON_IMAGE#*@}" <<<"$DIGESTS"; then
echo "frank-dragoon: image $FRANK_DRAGOON_IMAGE not in podman storage;" >&2
echo " pull it: podman pull ${FRANK_DRAGOON_IMAGE%@*}" >&2
exit 1
fi
# The image gate is the digest-pinned ref itself: `podman image exists` takes
# a name@sha256 ref in one command, so a retagged look-alike or a newer
# latest cannot satisfy it and there is no pipefail/SIGPIPE race with grep
# (the 2026-09-30 deploy refusal). Runs as dank: the unit's process uses
# dank's rootless storage.
require_image "$FRANK_DRAGOON_IMAGE" \
"pull it: podman pull docker://$FRANK_DRAGOON_IMAGE" || exit 1
# Size-exact gates on all three payload files: a truncated curl exits 0 on
# some failures and a partial .hgn loads far enough to look alive, so the
# byte counts from the Hub tree API are the contract (fleet fetch law).
ck() {
# ck <env-name> <path> <expected-bytes>
if [ ! -f "$2" ]; then
echo "frank-dragoon: $1 $2 missing;" >&2
echo " fetch it: bash scripts/download-frank-dragoon-flashnext.sh" >&2
exit 1
fi
local sz
sz="$(stat -c %s "$2")"
if [ "$sz" != "$3" ]; then
echo "frank-dragoon: $1 is $sz bytes, expected $3 (partial download?)" >&2
exit 1
fi
}
ck checkpoint "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_CHECKPOINT" "$FRANK_DRAGOON_CHECKPOINT_BYTES"
ck n-gram-table "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_NGRAM_TABLE" "$FRANK_DRAGOON_NGRAM_BYTES"
ck vision-tower "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_VISION_TOWER" "$FRANK_DRAGOON_VISION_BYTES"
require_bytes "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_CHECKPOINT" \
checkpoint "$FRANK_DRAGOON_CHECKPOINT_BYTES" \
"fetch it: bash scripts/download-frank-dragoon-flashnext.sh" || exit 1
require_bytes "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_NGRAM_TABLE" \
n-gram-table "$FRANK_DRAGOON_NGRAM_BYTES" \
"fetch it: bash scripts/download-frank-dragoon-flashnext.sh" || exit 1
require_bytes "$FRANK_DRAGOON_MODEL_DIR/$FRANK_DRAGOON_VISION_TOWER" \
vision-tower "$FRANK_DRAGOON_VISION_BYTES" \
"fetch it: bash scripts/download-frank-dragoon-flashnext.sh" || exit 1
# The payload ships the tokenizer as a FLAT DIRECTORY. A HuggingFace cache
# snapshot symlinks each file into a sibling blobs/, and those symlinks
# dangle inside the container mount — so the directory must be real and
# every named file must be a regular file, not a link.
TOK_DIR="$FRANK_DRAGOON_MODEL_DIR/tokenizer"
if [ -L "$TOK_DIR" ] || [ ! -d "$TOK_DIR" ]; then
echo "frank-dragoon: tokenizer dir $TOK_DIR missing or a symlink (HF cache snapshot?);" >&2
echo " fetch it: bash scripts/download-frank-dragoon-flashnext.sh" >&2
exit 1
fi
for TF in $FRANK_DRAGOON_TOKENIZER_FILES; do
if [ ! -f "$TOK_DIR/$TF" ] || [ -L "$TOK_DIR/$TF" ]; then
echo "frank-dragoon: tokenizer file $TOK_DIR/$TF missing or a symlink;" >&2
echo " fetch it: bash scripts/download-frank-dragoon-flashnext.sh" >&2
exit 1
fi
done
# The tokenizer file list is word-separated ON PURPOSE (one argument per
# file), so the unquoted expansion below is the interface, not a slip.
# shellcheck disable=SC2086
require_dir_files "$FRANK_DRAGOON_MODEL_DIR/tokenizer" tokenizer \
"fetch it: bash scripts/download-frank-dragoon-flashnext.sh" \
$FRANK_DRAGOON_TOKENIZER_FILES || exit 1
# GPU access is mandatory but the node paths are variables so the seam can
# drive the gate on a box without an AMD GPU.
@@ -115,6 +104,7 @@ if [ ! -e "$DEV_KFD" ] || [ ! -d "$DEV_DRI" ]; then
echo "frank-dragoon: $DEV_KFD or $DEV_DRI missing — no gfx1151 to serve on" >&2
exit 1
fi
# Host-RAM preflight — the engine's pin gate, checked BEFORE podman run.
# The engine refuses to pin when MemAvailable is below the checkpoint plus
# a 16 GiB floor: pinned pages cannot be swapped, and under the floor the
+7
View File
@@ -45,6 +45,13 @@ podman_stub() {
#!/usr/bin/env bash
case "$1" in
images) printf '%s\n' "${STUB_DIGESTS:-}" ;;
image)
# `podman image exists REF` — exit 1 when storage lacks the ref,
# which is exactly the primitive the shared gate library calls.
case " ${STUB_DIGESTS:-} " in
*" $3 "*) exit 0 ;;
*) exit 1 ;;
esac ;;
rm) exit 0 ;;
run|wait) echo "PODMAN $*"; exit "${STUB_WAIT_RC:-0}" ;;
*) exit 0 ;;
+9
View File
@@ -130,6 +130,15 @@ call() {
[[ "$output" == *"tokenizer: missing"* ]]
}
@test "the flat-directory gate refuses a symlinked directory" {
# The snapshot directory itself can be the link (a HF cache blobs/
# sibling), and a link into the container mount dangles there too.
ln -s "$TMP/flat" "$TMP/linked-dir"
call "require_dir_files '$TMP/linked-dir' tokenizer 'fetch the tokenizer' tokenizer.json"
[ "$status" -ne 0 ]
[[ "$output" == *"tokenizer: missing"* ]]
}
@test "the image gate refuses a ref the storage does not hold" {
# A name@sha256 ref exists only if THAT manifest was pulled, so the
# gate is the ref itself — one command, no grep race.