fix(fleet): every LLM root unit restarts on a voluntary exit; parity seam gates it
frank-zealot shipped Restart=on-failure while its two siblings shipped always. exit 0 is a CLEAN stop to systemd: on-failure leaves the endpoint dark and the unit GREEN until the next boot — and frank-zealot is the compaction hub for all three endpoints, so its quiet death degrades every conversation in the fleet. tests/llm_units.bats gains the fleet parity seam (Restart=always, RestartSec 1..20, WantedBy=multi-user.target across scripts/frank-*.service); the per-unit seams drop their weaker Restart greps so the policy lives in one place. Runbook unit line updated.
This commit is contained in:
@@ -12,7 +12,11 @@ Group=dank
|
||||
LimitMEMLOCK=infinity
|
||||
Environment="FRANK_ZEALOT_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-zealot.env"
|
||||
ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-zealot.sh
|
||||
Restart=on-failure
|
||||
# Restart=always, the fleet law for LLM units: exit 0 is a CLEAN stop to
|
||||
# systemd, so on-failure leaves the endpoint dark (and the unit reporting
|
||||
# success) until the next boot. frank-zealot is the fleet's compaction hub —
|
||||
# every endpoint's compaction dies when it does.
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
|
||||
@@ -27,7 +27,7 @@ conversation slot and unquantized KV back.
|
||||
| Host / device | Dankclaw (192.168.0.143), RTX 3090, 24,576 MiB |
|
||||
| Endpoint | `http://192.168.0.143:7771` — served model id **`frank-zealot`** (`ALIAS` in the env; the SERVICE keeps the `frank-zealot` name), `/v1` OpenAI surface |
|
||||
| Runtime | llama.cpp master (0.5.0-dev, commit `5fc4f3c`), CUDA sm_86 build `…/llama-master/build-cuda/bin/llama-server` (`GGML_CUDA_FA_ALL_QUANTS=ON`); `qwen35` is registered in the build's own `src/llama-arch.cpp` and `src/models/qwen35.cpp` exists at that commit |
|
||||
| Unit | root systemd `frank-zealot.service` ([`scripts/frank-zealot.service`](../../scripts/frank-zealot.service)), `User=dank`, `Restart=on-failure`, `LimitMEMLOCK=infinity` |
|
||||
| Unit | root systemd `frank-zealot.service` ([`scripts/frank-zealot.service`](../../scripts/frank-zealot.service)), `User=dank`, `Restart=always` (raised from `on-failure` 2026-10-13 for fleet parity — exit 0 is a CLEAN stop to systemd, so the weaker policy leaves the compaction hub dark while the unit reports success; `tests/llm_units.bats` gates it), `LimitMEMLOCK=infinity` |
|
||||
| Serve script | [`scripts/serve-frank-zealot.sh`](../../scripts/serve-frank-zealot.sh), tunables [`scripts/frank-zealot.env`](../../scripts/frank-zealot.env) |
|
||||
| Weights | `/home/dank/models/Qwen35-9B-HauhauCS-Q6K/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-Q6_K.gguf` — HauhauCS **Q6_K**, 7,359,259,008 B, sha256 `c0ba7beb68fd3fe47891bd549486d38dcf62d00817296ea314ad37017f5a4986` |
|
||||
| Projector | `mmproj-Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-BF16.gguf`, 921,704,448 B, sha256 `05f662501f8bd45607b079723a3e238a4e888fd085a10a53f4057a0e250f6934` — the model is natively multimodal and the Q6_K file carries no vision tensors, so this file IS the vision capability |
|
||||
|
||||
@@ -346,8 +346,9 @@ $(cat "$TEST_TMP/serve.log")"
|
||||
}
|
||||
|
||||
@test "the deployed unit keeps the fleet's restart and boot-persistence shape" {
|
||||
grep -qE '^Restart=(always|on-failure)$' "$UNIT"
|
||||
grep -qE '^WantedBy=' "$UNIT"
|
||||
# Restart=always and the WantedBy value are the FLEET parity seam
|
||||
# (tests/llm_units.bats); what is endpoint-specific is which script the
|
||||
# unit runs, under whom, and what a stop must reap.
|
||||
grep -qE '^ExecStart=.*/serve-frank-dragoon\.sh$' "$UNIT"
|
||||
grep -qE '^User=dank$' "$UNIT"
|
||||
grep -qE '^LimitMEMLOCK=infinity$' "$UNIT"
|
||||
|
||||
@@ -85,10 +85,12 @@ run_stage() {
|
||||
[ -f "$ENV_FILE" ]
|
||||
}
|
||||
|
||||
@test "frank-zealot is boot-persistent, self-healing and unprivileged" {
|
||||
grep -q '^WantedBy=multi-user.target$' "$UNIT"
|
||||
grep -q '^Restart=' "$UNIT"
|
||||
grep -q '^RestartSec=' "$UNIT"
|
||||
@test "frank-zealot runs unprivileged with the memlock budget and its env file" {
|
||||
# Restart policy, RestartSec and WantedBy are the FLEET parity seam
|
||||
# (tests/llm_units.bats). What is endpoint-specific: CUDA pins its
|
||||
# staging buffers, so the memlock budget is load-bearing even though the
|
||||
# weights are copied into VRAM, and the unit must name the serve script
|
||||
# and the env file that sizes it.
|
||||
grep -q '^User=dank$' "$UNIT"
|
||||
grep -q '^LimitMEMLOCK=infinity$' "$UNIT"
|
||||
grep -q '^ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-zealot.sh$' "$UNIT"
|
||||
|
||||
@@ -80,3 +80,38 @@ unit_repo_path() {
|
||||
}
|
||||
done < <(find "$REPO_ROOT/scripts" -name "frank-*.service" -type f | grep -v AGENTTRASH | sort)
|
||||
}
|
||||
|
||||
@test "every LLM root unit is self-healing against a voluntary exit" {
|
||||
# Restart=always, not on-failure. Every endpoint here is a container
|
||||
# runner (podman run) or a server whose front end can exit 0 on a
|
||||
# control-plane event; systemd treats exit 0 as a CLEAN stop and
|
||||
# on-failure leaves the endpoint dark until the next boot, with the unit
|
||||
# reporting success. frank-archon learned this on 2026-10-04; the fleet
|
||||
# law is that no LLM unit may ship the weaker policy.
|
||||
local unit
|
||||
while IFS= read -r unit; do
|
||||
grep -qx 'Restart=always' "$unit" || {
|
||||
echo "$(basename "$unit"): $(grep '^Restart=' "$unit") (must be always)"
|
||||
return 1
|
||||
}
|
||||
done < <(find "$REPO_ROOT/scripts" -name "frank-*.service" -type f | grep -v AGENTTRASH | sort)
|
||||
}
|
||||
|
||||
@test "every LLM root unit restarts promptly and starts at boot" {
|
||||
# RestartSec=10 is the fleet's cadence: fast enough that a transient
|
||||
# fault is invisible to traffic, slow enough that a hard fault does not
|
||||
# spin the engine (each radiance boot re-pins ~147 GiB).
|
||||
# WantedBy=multi-user.target is boot persistence for a ROOT unit —
|
||||
# default.target is the USER-manager's target and means nothing here.
|
||||
local unit
|
||||
while IFS= read -r unit; do
|
||||
grep -qE '^RestartSec=([1-9]|1[0-9]|20)$' "$unit" || {
|
||||
echo "$(basename "$unit"): no RestartSec in 1..20"
|
||||
return 1
|
||||
}
|
||||
grep -qx 'WantedBy=multi-user.target' "$unit" || {
|
||||
echo "$(basename "$unit"): $(grep '^WantedBy=' "$unit")"
|
||||
return 1
|
||||
}
|
||||
done < <(find "$REPO_ROOT/scripts" -name "frank-*.service" -type f | grep -v AGENTTRASH | sort)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user