Files
Your Name 0b5043a9f3
gates / consistency-and-conventions (push) Successful in 26s
fix: llama-server start breakage — version detection, flag-validation race, model dir resolution, user-bus pre-flight
User report after the llamacpp app install: 'installed llama.cpp unknown',
valid flags rejected (randomly per run), 'Model not found' for the HF
downloader's own layout, and a systemd user-bus failure over SSH. Detective
(real b10822 binary, FACT) found four independent causes:

- version: llama-server --version prints to STDERR; detect_llama_version's
  2>/dev/null swallowed it -> always 'unknown'. Now captures 2>&1 + accepts
  semver/build tokens (incl. build 1.2.3 edge)
- validation: printf|grep -q under pipefail -> SIGPIPE rc=141 race randomly
  rejected flags present in the 59 KB --help. Now pipe-less grep (no race);
  20x determinism regression test
- model resolution: resolve_model accepted files only, but the HF downloader
  creates <models>/<repo>/file.gguf dirs. Now expands a dir with exactly one
  *.gguf (never silently picks; multi-gguf lists + errs)
- port: llama.cpp default 8080 vs tool/adapter 8088; validation reliability
  means --port is now always pinned in the unit
- user bus: headless/SSH sessions lack XDG_RUNTIME_DIR -> ensure_user_bus in
  lib/common.sh pre-flights all three systemctl --user tools with remediation
  text; pos ai server --no-unit direct-run escape hatch (pidfile) for boxes
  with no bus
- find_llamacpp narrowed to llama-server/llama-server-cuda (bare 'server'
  fallback hazard); installer post-install sanity (version+help execute,
  symlink targets resolve)

Architect decisions DQ1-DQ6 recorded. Tester: 4 new regression files
(version-from-stderr, 25x flag-validation determinism, model dir expansion,
bus pre-flight + E2E) + 3 fixture updates; suite 16 files / 269 checks.

Verified: make gen idempotent; make check OK; make lint 0 FAIL, 0 WARN;
make test 269/269 (~49s); bash -n clean; git diff --check clean.
2026-09-06 09:25:52 -04:00

158 lines
6.6 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# t-unsupported-flags.sh — unsupported-option handling (D4):
# - CLI-explicit feature the model does not expose → hard error naming the
# unsupported flag and the detected llama version,
# - config-requested / env-requested unsupported flags → same hard error,
# - unsupported *default* flags → silently dropped with a warning (start
# still proceeds), never written into ExecStart,
# - word-boundary matching: `--no-mmap` in help must NOT satisfy a request
# for `--mmap` (the old grep -qF substring bug).
run_test() {
local sandbox stubs cfg models
sandbox="$(mksandbox unsupported-flags)"
stubs="$sandbox/stubs"
cfg="$sandbox/cfg"
models="$sandbox/models"
mkdir -p "$stubs" "$cfg" "$models"
: > "$cfg/ai.env"
touch "$models/my-model.gguf"
local server="$ROOT/bin/pos-ai-server"
local base_env=(PATH="$stubs:/usr/bin:/bin" DRY_RUN=1
CONFIG_FILE="$cfg/ai.env" USER_SYSTEMD_DIR="$sandbox/userunits")
# stub that omits --tensor-split (every default flag supported)
cat > "$stubs/llama-server" <<'STUB'
#!/usr/bin/env bash
case "$1" in
--version) echo "llama.cpp 1.2.3" ;;
--help)
cat <<'HELP'
usage: llama-server [options]
options:
--host <addr> bind address
--port <port> server port
--n-gpu-layers <n> layers to offload
--ctx-size <n> context size
--threads <n> cpu threads
--mmap memory mapping
HELP
;;
esac
STUB
chmod +x "$stubs/llama-server"
# systemctl: succeed — F4's ensure_user_bus pre-flight must pass so these
# flag-validation checks reach their intended outcome (unsupported-flag /
# default-flag behavior). Bus-missing behavior lives in t-ai-server-bus.sh.
printf '#!/usr/bin/env bash\nexit 0\n' > "$stubs/systemctl"
chmod +x "$stubs/systemctl"
# 1. CLI-explicit unsupported flag → rc 1, names flag + model version
test_run_env "${base_env[@]}" -- "$server" start "$models/my-model.gguf" --tensor-split 1:2:3
check_contains "CLI unsupported --tensor-split: rc message" "does not expose" "$TR_OUT"
check_contains "names the unsupported flag" "--tensor-split" "$TR_OUT"
check_contains "names the detected model version" "1.2.3" "$TR_OUT"
[ "$TR_RC" -ne 0 ] && printf ' PASS CLI unsupported flag exits nonzero\n' || printf ' FAIL CLI unsupported flag exits 0\n'
# 2. unsupported flag requested from CONFIG → same hard error (config
# requested flags go through the same validation)
printf 'LLAMACPP_PORT=9999\n' > "$cfg/ai.env"
# stub without --port in help: config-requested --port is unsupported
sed -i '/--port/d' "$stubs/llama-server"
test_run_env "${base_env[@]}" -- "$server" start "$models/my-model.gguf"
check_contains "config-requested unsupported flag: message" "does not expose" "$TR_OUT"
check_contains "config-requested flag named" "--port" "$TR_OUT"
[ "$TR_RC" -ne 0 ] && printf ' PASS config-requested unsupported flag exits nonzero\n' || printf ' FAIL config-requested unsupported flag exits 0\n'
# 3. same via environment export (env feeds config-requested flags too)
local envcfg="$sandbox/envcfg"
mkdir -p "$envcfg"
: > "$envcfg/ai.env"
test_run_env PATH="$stubs:/usr/bin:/bin" DRY_RUN=1 \
CONFIG_FILE="$envcfg/ai.env" USER_SYSTEMD_DIR="$sandbox/userunits" \
LLAMACPP_PORT=9999 -- "$server" start "$models/my-model.gguf"
check_contains "env-requested unsupported flag: message" "does not expose" "$TR_OUT"
check_contains "env-requested flag named" "--port" "$TR_OUT"
[ "$TR_RC" -ne 0 ] && printf ' PASS env-requested unsupported flag exits nonzero\n' || printf ' FAIL env-requested unsupported flag exits 0\n'
# 4. unsupported default flag: start SUCCEEDS, warns, omits the flag
# (--threads absent from help; every other default supported)
cat > "$stubs/llama-server" <<'STUB'
#!/usr/bin/env bash
case "$1" in
--version) echo "llama.cpp 1.2.3" ;;
--help)
cat <<'HELP'
usage: llama-server [options]
options:
--host <addr> bind address
--port <port> server port
--n-gpu-layers <n> layers to offload
--ctx-size <n> context size
--mmap memory mapping
HELP
;;
esac
STUB
chmod +x "$stubs/llama-server"
: > "$cfg/ai.env"
test_run_env "${base_env[@]}" -- "$server" start "$models/my-model.gguf"
check_rc "start succeeds with unsupported default" 0 "$TR_RC"
check_contains "warns about dropped default flag" "--threads" "$TR_OUT"
local exec_line
exec_line="$(printf '%s\n' "$TR_OUT" | grep 'dry-run) ExecStart:' | sed 's/.*ExecStart: //')"
check_not_contains "dropped default never reaches ExecStart" "--threads" "$exec_line"
check_contains "other defaults still emitted" "--n-gpu-layers 0" "$exec_line"
# 5. word-boundary bug (D4): help advertises --no-mmap, NOT --mmap
cat > "$stubs/llama-server" <<'STUB'
#!/usr/bin/env bash
case "$1" in
--version) echo "llama.cpp 1.2.3" ;;
--help)
cat <<'HELP'
usage: llama-server [options]
options:
--no-mmap disable memory mapping
--host <addr> bind address
--port <port> server port
--n-gpu-layers <n> layers to offload
--ctx-size <n> context size
--threads <n> cpu threads
HELP
;;
esac
STUB
chmod +x "$stubs/llama-server"
test_run_env "${base_env[@]}" -- "$server" start "$models/my-model.gguf" --mmap
check_contains "requested --mmap rejected (only --no-mmap in help)" "does not expose" "$TR_OUT"
check_contains "rejection names --mmap" "--mmap" "$TR_OUT"
[ "$TR_RC" -ne 0 ] && printf ' PASS word-boundary: --no-mmap does not satisfy --mmap (rc != 0)\n' || printf ' FAIL word-boundary: --no-mmap wrongly satisfied --mmap (rc 0)\n'
# 6. control: full-support stub → all defaults + requested flags pass
cat > "$stubs/llama-server" <<'STUB'
#!/usr/bin/env bash
case "$1" in
--version) echo "llama.cpp 1.2.3" ;;
--help)
cat <<'HELP'
usage: llama-server [options]
options:
--host <addr> bind address
--port <port> server port
--n-gpu-layers <n> layers to offload
--ctx-size <n> context size
--threads <n> cpu threads
--mmap memory mapping
--tensor-split <n> tensor split
HELP
;;
esac
STUB
chmod +x "$stubs/llama-server"
test_run_env "${base_env[@]}" -- "$server" start "$models/my-model.gguf" --mmap --tensor-split 1:2:3
check_rc "control: full-support stub starts cleanly" 0 "$TR_RC"
check_not_contains "control: no unsupported warning" "does not expose" "$TR_OUT"
}