chore(llm): add dankstatus.sh — one verified parse path for Dankserver state

The ad-hoc status commands kept producing wrong readings, most seriously
reporting "MemAvailable 0.0 GiB" on a host that actually had 240 GiB free.
Cause: /proc/meminfo separates key from value with a TAB, so

  grep MemAvailable /proc/meminfo | tr -s " " | cut -d" " -f2

returns an empty field and awk prints 0.0. A false "host is out of memory"
reading is worse than no reading, because it invites an unnecessary abort.

dankstatus.sh parses on a colon split (tab-agnostic), cross-checks
MemAvailable against MemTotal before printing either, and refuses to print
a zero value. It also pins the rocm-smi CSV column order (device, total,
used) which was previously read swapped, and takes only the first line of
systemctl is-active/is-enabled, which emit more than one line here.

Incidental finding while validating: rocm-smi reports card2 (the 7700X iGPU)
as 0.50 GiB total, not the 123.0 GiB torch.cuda reports. torch was exposing
the GTT aperture. The device is smaller than documented, not larger.

Tests: 346/346 pass (no test surface touched).
This commit is contained in:
Frank committed 2026-09-28 23:27:50 -07:00
1 parent b4b6777e49
commit 32abc8d543
8 files changed
+525 -146

No files matched your search

+71
View File
@@ -2,6 +2,77 @@
## 2026-09-28
- **frank-large payload swapped to Swift-1.5 (uncensored)** (production
change, Dankclaw). Frank asked whether the UD Q8 of
`ajgazin/Swift-1.5-Qwen3.8-27B-Uncensored-Dynamic-MTP-GGUF` would load on
gufo, then said to do it. Port 7770, alias `frank-large`, one 262,144
session, the DFlash2 drafter and the client contract are unchanged; the
27B dense weights are an **abliterated (uncensored)** fine-tune of the
same base, under the **non-OSI SWIFT OPEN LICENSE 1.0**.
- **Compatibility was established from the GGUF headers, not from the
filename.** Both files were range-fetched and parsed: same
`general.architecture` (`qwen35`), same 866 tensors with **identical
names in identical order**, **zero** per-tensor `ggml_type` or dim
mismatches (Q8_0×453, F32×360, BF16×53), same MTP head at `blk.64`, same
geometry. The two scary metadata differences are both no-ops, confirmed
against gufo's source: `general.file_type` (15 vs 7) is *"deliberately
not consulted"* by `gguf_reader.cpp` — the file gufo already served
records 15 (Q4_K_M) while containing no Q4_K tensor — and Swift's extra
`qwen35.attention.recurrent_layers` key is **never read** by gufo
anywhere in `src/`; layer kinds come from `full_attention_interval`,
which both files carry as `4` and which agrees with it.
- **Fetched and verified:** 31,457,990,272 bytes, sha256 `9076f99a…`
matching the Hub LFS oid, via the new
`/scripts/download-frank-large-swift.sh`. It sends **no auth header** —
the repo is public and ungated, and a token on the curl command line is
world-readable via `/proc/<pid>/cmdline`. The anonymous transfer ran at
~1.8 MiB/s; resuming with the host's token through a **mode-600
`curl -K` config** took it to ~7 MiB/s. The download survived an ssh
drop by running `setsid nohup`, not under the session.
- **Load test, service stopped first** (the GPU policy): **9.2 s** from
`load_started` to `load_completed`, 66.5 GiB of the 116.7 GiB iGPU at
context capacity 262,144, no loader warnings. Contract battery green:
`context_length 262144`, `content` + `reasoning_content`,
`finish_reason: tool_calls`, SSE, and a **two-turn tool loop** with prior
reasoning preserved. Spot-checks: "17*23" → `391`; asked for a
lockpicking scene it wrote one without hedging, confirming the
abliteration.
- **THE TEMPLATE LAW IS GONE, by decision.** Frank asked to "use our chat
template"; gufo **cannot** take it. This is not a missing flag —
`src/models/qwen/chat_template.cpp` is 587 lines of hand-written C++
that appends strings, with no Jinja engine, no `--chat-template-file`,
no env override and no template body in the binary. Qwen-Sharp
v22.4.0 is 453 lines of Jinja2 with `namespace()`, macros and
`raise_exception`, so supplying it is a **port, not a config line**.
Frank chose to ship on gufo's stock renderer and accept the loss: the
`terse` system-prompt injection, in-band `<|think_*|>` control, the
`json|xml` `tool_call_format` switch, tool-argument truncation and
`add_vision_id` prefixes are all gone. gufo's renderer does keep the
effort instruction strings and `preserve_thinking`. **Worth watching:
an abliterated model now runs without the prompt that tells it to stop
waffling** — the cheap fix is to send the `terse` text as an ordinary
system message from `models.yml`, not to touch a template.
- **The payload is now two files in two directories.** The target moved
repo; the DFlash2 drafter did not move, so `DRAFTER_DIR` is its own
tunable. That broke the deployer's stray-run sweep, which keyed on the
*target* filename and would have silently stopped matching — a stray
run would then have held 7770 past the port gate. It now keys on the
drafter, which every frank-large command line carries via
`--dflash-model` and which did not move.
- **`/tests/frank_large_unit.bats`** — 20 assertions. The mutation pass
found **four real gaps** on the first run, all now fixed: two fetch
assertions were satisfied by a **header comment** rather than the URL
assignment, so repointing a repo left them green; and nothing bound the
env's `MODEL_FILE`/`DRAFTER_FILE` to the filenames the fetch scripts
actually download, so a re-upload or an env edit would break the
endpoint with a "missing asset" no test predicted. There is now an
explicit cross-file invariant. A second harness bug worth recording:
a `bats --filter` that matches no test reports zero `not ok`, which
looks exactly like a mutation that survived — the harness now asserts
the `1..N` count before believing a result. 16/16 mutations caught.
- Docs: runbook gained a **Payload** section (the header evidence),
**The template law** section and a two-way **Rollback**; AGENTS.md
fleet table, frank-large section and rollback.
- **frank-large runtime swapped to gufo** (production change, Dankclaw).
Frank asked for `github.com/gufo-org/gufo` — a vertical inference engine
built for exactly this hardware (Ryzen AI MAX+ 395 / Radeon 8060S,