refact(deployer): one MainPID gate for all three LLM endpoints

frank-dragoon's health gate never checked WHO answered :7770 — the
false-green the fleet learned about on 2026-09-26 (a stray probe holding
the port satisfies a bare curl while the unit is stopped). The other two
handlers carried that check as inlined copies; all three now call
assert_unit_owns_port, and dragoon gets the protection it was missing.

The seam follows: the endpoints' text-grep deployer tests are deleted,
because tests/llm_deploy_gates.bats EXECUTES the same handlers end to end
(stub systemctl/podman/sudo/ss/curl) and asserts refusals, restarts, unit
links, the pending marker, and the MainPID hardening. GATE_PREFIX is
exported at each handler for the same reason the serve scripts export it.
This commit is contained in:
Frank committed 2026-10-13 22:02:48 -07:00
1 parent c5a816e6a8
commit ab80c1366d
7 files changed
+34 -78

No files matched your search

+5 -2
View File
@@ -64,10 +64,13 @@ sandbox_setup() {
# $HOME/serverAppConfigs is the checkout the handler works in: a git
# WORKTREE of the caller's checkout, so the handler runs the EXACT files
# under test (the suite lives in worktrees, where uncommitted state is
# the norm), and it is a real git repo: the deployer refuses a revision
# under test, and it is a real git repo: the deployer refuses a revision
# it cannot resolve. The handler cds into REMOTE_DIR, so every relative
# path it uses resolves there.
#
# HEAD is committed state, so the seam asserts on what is in history —
# the same rule as the whole suite, and the reason an uncommitted
# deployer change is invisible here until it is committed.
git -C "$repo_root" worktree add -q --detach "$HOME/serverAppConfigs" HEAD
export REMOTE_DIR="$HOME/serverAppConfigs"
-33
View File
@@ -419,36 +419,3 @@ serve_run() {
! 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"
}
-5
View File
@@ -335,11 +335,6 @@ $(cat "$TEST_TMP/serve.log")"
grep -qF "LLM_DRAGOON_CKPT_BYTES=\"\${LLM_DRAGOON_CKPT_BYTES:-$bytes}\"" "$DEPLOYER"
}
@test "the deployer points a missing artifact at the flashnext fetch script" {
grep -qE 'download-frank-dragoon-flashnext\.sh' "$DEPLOYER"
grep -qE 'podman pull' "$DEPLOYER"
}
@test "the stray sweep cannot take frank-zealot or the deployer down" {
# frank-zealot also listens on this host (7771, llama-server) and the
# deployer's own command line contains "frank-dragoon"; only the FULL
-11
View File
@@ -269,17 +269,6 @@ run_stage() {
[[ "$output" == *"build-llama-master-cuda.sh"* ]]
}
@test "the deploy handler gates on binary, GGUF and projector, then MainPID-hardens health" {
grep -q 'deploy_llm_zealot' "$REMOTE_DEPLOY"
grep -q 'LLM_ZEALOT_RUNTIME' "$REMOTE_DEPLOY"
grep -q 'LLM_ZEALOT_MODEL_FILE' "$REMOTE_DEPLOY"
grep -q 'LLM_ZEALOT_MMPROJ' "$REMOTE_DEPLOY"
grep -q 'relink_unit frank-zealot' "$REMOTE_DEPLOY"
grep -q 'systemctl enable frank-zealot.service' "$REMOTE_DEPLOY"
grep -q '127.0.0.1:7771/health' "$REMOTE_DEPLOY"
grep -q 'frank-zealot.service has no MainPID' "$REMOTE_DEPLOY"
}
@test "the two endpoints keep separate ports and the large handler never pkills llama-server" {
# frank-dragoon's sweeps must not take down frank-zealot, whose
# process is llama-server on the SAME host.
+4 -1
View File
@@ -23,11 +23,14 @@ teardown() { sandbox_teardown; }
[ "$(restarts_requested)" = "0" ]
}
@test "the dragoon handler refuses a truncated checkpoint, naming both sizes" {
@test "the dragoon handler refuses a truncated checkpoint, pointing at the fetch" {
truncate -s 9 "$STAGE/models/ck.hgn"
run_handler dankclaw frank-dragoon
[ "$status" -ne 0 ]
[[ "$output" == *"checkpoint: is 9 bytes, expected 10"* ]]
# The refusal is only useful if it names the remedy: the fetch script
# re-verifies byte counts and hashes.
[[ "$output" == *"download-frank-dragoon-flashnext.sh"* ]]
[ "$(restarts_requested)" = "0" ]
}