live: heartbeat sync 2026-10-18T16:47:59-07:00 (Refs #11)

This commit is contained in:
Frank committed 2026-10-18 16:47:59 -07:00
1 parent a550db6027
commit 979e9ef711
173 files changed
+3844 -57

No files matched your search

@@ -1,3 +1,4 @@
- command-center (~/.omp) worktree-clean law, verified 2026-10-09: (1) The secret gate's PEM family regex (BEGIN, a space, a bracketed A-Z star class, PRIVATE KEY) does NOT self-match when the pattern is quoted in brackets form — a SKILL.md documenting the gate verbatim in regex-bracket shape is safe to stage; only the bare quoted literal (e.g. the plain two-word PEM header phrase, the xox[baprs] family prefix followed by 10+ token chars) trips it. Before any commit that stages prose about secrets, pre-scan with the exact gate one-liner: staged=$(git diff --cached --diff-filter=ACM -U0 -- '*.md' '*.yml' '*.yaml' '*.json' '*.toml' '*.sh' '*.ts' '*.js'); printf '%s\n' "$staged" | grep -Eic 'gate patterns' — 0 hits means the gate will pass; redact prose descriptively, never ALLOW_SECRETS. (2) The omp auto-learner mints managed skills under agent/managed-skills/name/ DURING a session — a "clean tree" declared mid-session goes dirty again when capture fires (observed: noctalia-bar-layout-edit appeared between commit and final status). Re-run git status as the LAST command of a cleanup pass. (3) Classification rule that made the tree clean in one pass: agent/memories/*/learned.md and agent/managed-skills/ are config → commit atomically; new top-level runtime dirs (webcache/, reports/, local/, wt/) are state → .gitignore. webcache/ (harness web-fetch cache) is gitignored as of commit 4d2b024.
- Noctalia v5 config layering (verified live on blankpad 2026-10-09): bar widget layout lives in the GUI-override layer ~/.local/state/noctalia/settings.toml under [bar.name] keys start/center/end (defaults: start=["launcher","wallpaper","workspaces"], center=["clock"]); settings.toml loads LAST and wins over ~/.config/noctalia/config.toml. Both layers are watched and hot-reloaded — edit + noctalia config validate + screenshot is the full apply/verify loop (no restart, no reload IPC). Noctalia writes THROUGH symlinks and keeps the link, so keeping settings.toml symlinked from the command-center dotfiles repo is safe. Combine workspace+window indicator: [widget.taskbar] group_by_workspace=true (needs compositor per-window workspace data — niri has it) renders per-workspace capsules with disc labels, workspace_group_content = icons|count|dots (count/dots left-click activates the workspace); the workspaces widget itself has no window-display mode. Applied on blankpad: bar.default start=["launcher","wallpaper","workspaces","taskbar"], center=["cat"] (command-center commits 9255a8b, 0ccdd46). _(context: Blankpad noctalia 5.2.1 bar layout change 2026-10-09: taskbar moved to left section, group_by_workspace identified as the combining widget, verified via config validate + fullscreen screenshot)_
- The command-center (~/.omp) pre-commit secret gate (.githooks/pre-commit, single grep -E of known token regexes) SELF-MATCHES its own documented pattern literals: committing prose that quotes them verbatim (e.g. a quoted PEM private-key header line, the xox[baprs] family prefix, the github-pat prefix, long sk- examples) trips SECRET GATE and blocks the commit (observed twice 2026-10-09 on agent/memories/--home-blank-.omp--/learned.md audit prose). Convention: redact pattern literals descriptively in prose ("PEM private-key headers", "the xox[baprs] family", "ghp 36") instead of setting ALLOW_SECRETS=1; the gate firing on prose is the gate working as designed. Note the gate covers only 10 families — no hf_/npm_/AIza/glpat-short/basic-auth-URL coverage — so clean-gate history is not proof of no leaks.
- niri 26.04 event-stream (niri msg --json event-stream) has NO hotplug event: OutputConfigChanged is only an ACK that an IPC/config output change was Applied (per niri-ipc docs; proven live — mode flips via niri msg output NAME mode WxH@Hz and DPMS off/on emit zero output events). Detect monitor connect/disconnect by diffing the sorted unique set of .WorkspacesChanged.workspaces[].output across events (off/on cycle of eDP-1 emitted exactly WorkspacesChanged 2x). Debounce ~1.5s; OutputConfigChanged events are emitted in bursts on every config reload — ignoring them prevents hotplug-daemon echo loops. Implemented in dank/command-center scripts/desk-autoswitch.sh (blankpad-desk-autoswitch.service). Related trap: niri msg output 26.04 syntax is niri msg output NAME ACTION [args] (mode/off/on), not the old NAME WxH@Hz form.
@@ -1,3 +1,4 @@
- serverAppConfigs LLM deploy law (verified 2026-10-09): (1) A green deploy.sh does NOT prove the running configuration — systemd drop-ins in /etc/systemd/system/unit.service.d/ are invisible to the planner and can override Environment=FRANK_*_ENV_FILE and ExecStart to a staging worktree. dankserver's frank-primary carried bench.conf (2026-10-08 04:09, left from the w4dn8 campaign) pointing both at /home/dank/serverAppConfigs-swift15, so every frank-primary deploy since then restarted the staging tree behind a healthy gate. After an LLM deploy, verify behaviourally: podman inspect container --format {{json .Config.Cmd}} + systemctl cat unit (drop-ins print at the bottom) + /v1/models. Before removing a drop-in: diff staging vs deploy-checkout serve script/unit/env, and git merge-base --is-ancestor stagingHEAD HEAD. (2) Verify a served-model-id rename on /v1/models, never on a chat response: radiance echoes the REQUESTED model name, so a 200 with the new name proves nothing; llama.cpp and halogen ignore the requested id entirely. (3) OpenWebUI keys a model id in three places — model.id+name, access_grant.resource_id (resource_type='model'), openai.api_configs per-backend model_ids — a stale id fails silently (empty picker: it intersects the backend /v1/models with model_ids). Apply with podman exec -i open-webui python3 - (no sqlite3 binary in the image), then systemctl --user restart open-webui.service. GET /api/v1/models/list returning {"items":[],"total":0} for an account whose picker is plainly populated: the API census is not the acceptance test; the browser snapshot is. (4) Playwright probes: locator.isVisible({timeout}) does NOT wait — it returns immediately; use waitFor({state:'visible'}). OpenWebUI 0.6 composer is a contenteditable div, not a textarea; stable anchors are getByRole('button', {name: /selected model/i}) and the text "How can I help you today?". (5) OPEN ITEMS reported to Frank, not approved: Hermes dankserver-small aux routes still send the retir
- serverAppConfigs secret-scan baseline (2026-10-09, gitleaks 8.24 full history + manual sweeps, 559 files/1167 commits): tree and history CLEAN of real secrets. Known gitleaks false positives to expect on re-runs — (1) Bearer PASTE-YOUR-KEY in shared/homepage/docs.html (placeholder in public API guide); (2) curl -u admin:admin in serverAppConfigs-docs/Operations/Fleet Audit and Posture.md (Grafana default-credential RESIDUAL CHECK, not a leak); (3) FRANK_LARGE_*_SHA256 checksums in scripts/frank-*.env; (4) OAUTH_CLIENT_ID / GF_AUTH_GENERIC_OAUTH_CLIENT_ID in open-webui.container, grafana.container, dankclaw/compose.yaml — OAuth2 client IDs are public identifiers; the client SECRETS live only in git-ignored host .env files. Real-value secret file on disk: tests/e2e/.env.local (gitignored, verified). PENDING POSTURE ITEM reported to Frank, not yet approved for action: Grafana on dankclaw answers admin:admin on loopback 127.0.0.1:3002 (probe: ssh dankclaw curl -u admin:admin http://127.0.0.1:3002/api/org → 200) — rotation recommended, awaiting his go.
- serverAppConfigs authentik user provisioning (five accounts kirby/ry/nic/debo/freya pk 17-21 created 2026-10-09; law now in vault commit 65dec6a): (1) After ak shell provisioning with the human-user template (groups Non-Admin Users + jellyfin_users + openwebui_users), OpenWebUI mints each account on FIRST SSO login with role pending (ui.default_user_role=pending in webui.db config table) — they log in fine, then cannot chat / picker is empty until UPDATE "user" SET role='user' WHERE email=…; the row does not exist before first login, so the flip is a first-login-sighting follow-up, not a provisioning step. No per-user model grant needed — the picker model carries the global access_grant row (model, frank-secondary, user, *, read). Written up in serverAppConfigs-docs/Dankclaw/OpenWebUI.md (SSO section). (2) The proxysession claim purge is only due when a group change follows an existing login; users created with their groups already in place have zero rows — verify with a per-user census instead of purging blindly. (3) The authentik-postgres container's DB role/db are authentik/authentik (POSTGRES_USER env), NOT postgres: podman exec authentik-postgres psql -U authentik -d authentik -tAc "select session_data-'claims'-'preferred_username', count(*) from authentik_providers_proxy_proxysession group by 1". (4) User.app_entitlements() requires the Application argument (Application.objects.get(slug="open-webui")) — bare call raises TypeError. Verification bar for a provision: authenticate(request=None, …) accepts the password and rejects a wrong one (the exact PasswordStage path) plus the open-webui entitlement resolves. _(context: serverAppConfigs 2026-10-09: provisioned five authentik human accounts on Dankclaw; vault pages Infrastructure/Authentik.md, Dankclaw/OpenWebUI.md, log.md updated (commit 65dec6a), bats suite 366 green.)_
- radiance arch-capability audit (dankserver, pin 1.0.13, verified 2026-10-08): the container ships 6 arch plugins in /opt/radiance/share/radiance/architectures/ — qwen4exp_fp8, qwen35moe_fp8, qwen35_fp8, qwen35_bf16, llama_fp8, llama_dense_fp8. Capability check per plugin: grep -ac RADIANCE_CALIB / grep -ac gram / grep -ac mtp / grep -ac ngram on the .so (qwen35moe_fp8: calib=2 grams=2 mtp=4 ngram=0; gptq=0 in ALL plugins — GPTQ lives in rad-convert, not the plugin). Introspection pitfalls: the digest-pinned name (stilldeadcode/radiance@sha256:4c3f48…) does NOT resolve locally (podman tries a registry pull and fails); use the local tag docker.io/stilldeadcode/radiance:latest — image lives in ROOTLESS storage as dank (frank-primary runs rootless under the root unit; sudo podman ps is empty). Image has no ls/strings; podman exec frank-primary ls fails; correct form: podman run --rm --entrypoint sh docker.io/stilldeadcode/radiance:latest -c '…'. Verdict recorded: Qwen3.6-35B-A3B (qwen3_5_moe_text, 40 layers, 256 experts top-8, MTP×1) is plugin-supported; w4dn8 recipe maps minus PLE/HC terms; ~20 GiB w4+i8 = fully VRAM-resident on ONE 32 GiB R9700 (no TP=2/host-pool/disk-tier/MNBT-2048 law); convert ~1–1.5 h. Full audit in serverAppConfigs-docs/LLM/Quantization Production.md. _(context: serverAppConfigs 2026-10-08: feasibility audit of porting the frank-archon w4dn8 radiance recipe to Qwen3.6-35B-A3B; KB page Quantization Production.md carries the full write-up.)_
@@ -97,4 +98,3 @@
- Frank's standing working rules for skill management (established 2026-10-05): (1) Do NOT use subagents for reading/analysis/distillation work — he explicitly ordered solo work ("Without subagents… read through each skill", later "NO. SUBAGENTS."); parallel fanout is not authorized by default even when tools suggest it. (2) The knowledgebase (serverAppConfigs-docs) is the system of record for all fleet knowledge — managed skills are NOT durable homes for lessons; when skills duplicate or extend runbooks, distill their content into vault pages and delete the skills, staging deletions in ~/.AGENTTRASH first. His default is "no reason to keep these skills". _(context: 2026-10-05 managed-skills audit session: Frank twice ordered "Without subagents" and then "NO. SUBAGENTS." when the agent planned task-fanout for reading 173 skills and distilling them; and he ruled that none of the fleet skills are worth keeping as skills — their content belongs in serverAppConfigs-docs.)_
- serverAppConfig multi-session triage rule (proven 2026-10-05, three events in one day): when ./tests/run.sh goes red and the failing seams point at files you did not touch, FIRST split the breakage by git state: (a) COMMITTED-but-broken states from a parallel session (swap commits that forgot their seam/deployer twins) → repair them yourself in clearly-labeled follow-up commits; that is what kept the suite green twice. (b) UNCOMMITTED working-tree state (M/untracked files owned by a LIVE concurrent session — check git status --porcelain and untracked ?? entries against HEAD, which may not have moved) → do NOT edit those files; the other session is mid-flight and will reconcile env+seam+docs in its own commit; editing its dirty tree risks colliding with an active writer. Verify your own pushed state instead (rev-list HEAD...origin/main, targeted seams on your files) and report the named dirty files as the red-suite owner. Signature of case (b): HEAD identical across two suite runs yet different failures, because bats seams read the working tree, not the commit. _(context: 2026-10-05 goal session: frankbook e2e split (9c50821/2930431/1c725ea/5573ca1) interleaved with a live parallel session committing Ornith-swap breakage and later holding the tree dirty (M frank-small.env, M frank_small_unit.bats, ?? GGUF Compatibility.md).)_
- Wrapper-script law (serverAppConfigs tests/e2e/run.sh): when converting a pure passthrough exec tool "$@" into "default argument set + passthrough", the default set must be APPENDED to "$@", not substituted for it — exec npx playwright test "${PROJECT_ARGS[@]}" silently DROPS every other arg (--list, --grep, file filters), so run.sh --list ran the entire 34-test suite instead of listing. The regression is only visible by diffing observed behavior against requested semantics; the seam (bash -n) cannot see it. Correct shape: build PROJECT_ARGS only when no user --project is present, then exec npx playwright test "$@" "${PROJECT_ARGS[@]}". Caught 2026-10-05 because the --list proof printed execution checkmarks instead of a listing; verify arg-forwarding wrappers by exercising EVERY documented passthrough arg, not just the happy path. _(context: 2026-10-05 frankbook e2e split; regression introduced by my own default-project-set rewrite and caught during verification (commit 1c725ea).)_
- frank-small seam congruence rule (serverAppConfigs repo): ANY change to scripts/frank-small.env has TWO consumers that must flip in the same change: (1) scripts/deploy-remote.sh's LLM_SMALL_MODEL_FILE/LLM_SMALL_MMPROJ defaults (the payload-existence gate — test 196 greps for the exact env path inside the deployer default), and (2) tests/frank_small_unit.bats (TARGET_STUB/MM_STUB fixture names, the MODEL_FILE env assertion, and the KV-contract test pinning --cache-type-k/--cache-type-v values from the env). On 2026-10-05 two consecutive commits broke it: the Ornith-1.5-9B Q8_0 payload swap (77decb5) left both consumers on MiMo-Q6K paths, and the KV reprice (1862dc3, f16 V→q8_0 V) left the KV test asserting the old unquantized contract — its commit message even claimed the seam was 'already retargeted in 8f53df7' when 8f53df7 covered only payload paths. Symptom each time: full ./tests/run.sh red in frank_small_unit.bats only ('missing /tmp/.../mmproj-...' or argv-value mismatch). Fix = grep the env for the new values and update all three spots. _(context: 2026-10-05 goal session (frankbook e2e split) blocked twice by frank-small seam staleness from concurrent sessions; fixed via 8f53df7-equivalent edits and 5573ca1.)_
@@ -0,0 +1,5 @@
## Thinking Traces Input Model
- The thinking traces' kickoff is not the user's; distinguish between what appears in thinking traces and the user's actual input.
## Code Review Protocol
- Code undergoes adversarial review before acceptance.
@@ -0,0 +1,5 @@
## Information Gathering
- Use the ask tool for information gathering.
## Question Flow
- Skipped questions should be re-asked when the user indicates they were missed.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1791836966806,
"lastResult": {
"at": 1791709075523,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
Whitespace-only changes.
@@ -0,0 +1,13 @@
## Runtime Stability
- System runs must sustain operation beyond the first second; early termination within ~1s is a failure condition.
## Performance Analysis
- Performance gap analysis includes archon's KV quantization as a potential factor.
- Evaluation uses index-lite metric for Qwen3.8-Flash-Next.
- Composite index scores should be calculated and compared alongside individual model scores.
## Reference Baselines
- Agentic fine-tune evaluation: Qwen3-6-27B serves as the primary comparison baseline.
- General capability evaluation: Qwen3.6 27B is to be evaluated against Claude Haiku 5-5.
- Benchmark reference comparison target: qwen-flash-next.
- In fair fight comparison, archon underperforms relative to reference.
@@ -0,0 +1,20 @@
## Design and Testing Phases
- Testing workflow supports pausing and resuming.
- Test frank-archon after design implementation.
- Adversarial reviews are conducted via the frank-archon subagent with extreme thoroughness.
## Dashboard Access
- Dashboard results accessible at http://127.0.0.1:8787
## Benchmarking Workflow
- When harness indicates failure or configuration changes (e.g., KV quantization), discard current benchmarking results and restart the entire benchmarking run.
## Judge Selection
- Use frank-zealot as the default judge for SciCode benchmark evaluation.
- Judge must be performant enough for AA-LCR and HLE tasks.
## Reference Comparison
- Evaluate model performance against alternatives as part of the evaluation scope.
- Reference comparison target is qwen-flash-next.
- In fair fight comparison, archon underperforms relative to reference.
- Score comparison to other models is included in the report.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1791969871087,
"lastResult": {
"at": 1791852389828,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
Whitespace-only changes.
@@ -0,0 +1,17 @@
## Root directory
- `~/.omp` on the dev workstation; the checkout IS the deployment, not a staging copy
## Knowledge base
- `omp-docs/` directory with markdown files using YAML frontmatter (OKF v0.1 format)
## Harness configuration
- `agent/` and `plugins/` directories
## System utilities
- `scripts/` directory
## Runtime state
- Runtime state (dbs, logs, caches, sessions): gitignored on principle
## File organization
- One concept per file; directories must have an `index.md` listing all contents
@@ -0,0 +1,9 @@
## System prompt source
- Read `omp-docs/index.md` before modifying any configuration
- System prompts live in the `omp-docs/` directory in OKF v0.1 format
## Keyboard commands
- `Mod+R`: keyboard command in the system
## Column width presets
- Cycle through 1/2, 1/5, 2/5, 3/5; user simplified from 6 preset options to 4 specific fractions
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792359756091,
"lastResult": {
"at": 1791599865775,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
Whitespace-only changes.
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1792359704968
}
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1792359739617
}
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1791600003219
}
@@ -0,0 +1,39 @@
## LLM Endpoint Inventory
### frank-secondary (Dankclaw, port 7771)
**Current**: Qwen3.5-9B-Uncensored-HauhauCS-Aggressive Q6_K (llama.cpp, RTX 3090, 200,704-token slots, vision via BF16 mmproj).
**Target**: Underdog-Saluki-27B-1.0 (llama.cpp, RTX 3090, 400,000-token slots, vision via BF16 mmproj).
Both run on the same hardware with the same runtime stack. The swap is model-only; container image and systemd unit remain unchanged. Vision support persists via the publisher's BF16 mmproj pass-through (`--mmproj`).
**Context window law**: 400,000 tokens is the per-slot hard ceiling (`--ctx-size 401408 --parallel 2`). The harness clamps to `min(45056, contextWindow − est − 4096)` to keep room for the MTP head. With 400K context, a 45K token request leaves 355K for MTP head + estimation buffer.
### frank-primary (Dankserver, port 7770)
**Radiance 1.0.13**, ukisai/Swift1.5-Qwen3.8-Flash-Next BF16, 49,152 experts GPTQ w4 + 24 bf16-protected experts, 200K max-model-len, 47.7 GiB n-gram pinned to host RAM, 2 R9700s TP=2 (`--tp-wire wht6`), MTP spec decode depth 2, `--max-num-seqs 3`, `--max-model-len 200000`.
### frank-large (Dankclaw, port 7770)
**Halogen-flash-server 0.16.2**, Quat3rnion/halogen-swift1.5-qwen3.8-flash-next-v2-abliterated, 262,144 context per-slot, 2 slots × 262,144 ctx, MTP spec decode ALWAYS-ON (`HALOGEN_SPEC_ADAPT=0`), vision ON (v2 tower loads beside checkpoint), 524,288 positions KV pool.
## Runtime Stack
- **frank-primary**: Radiance 1.0.13 (StillDeadcode, digest-pinned `stilldeadcode/radiance@sha256:4c3f484564…`), plain `podman run`, root systemd unit, 2 R9700s TP=2.
- **frank-large**: Halogen-flash-server 0.16.2 (`ghcr.io/peonist-ai/halogen-flash-server@sha256:0c61bf84…`), digest-pinned, plain `podman run` via `scripts/serve-frank-large.sh`, root systemd unit, Strix Halo iGPU.
- **frank-secondary**: llama.cpp master (0.5.0-dev, commit `5fc4f3c`), CUDA sm_86 build, root systemd unit, RTX 3090.
All LLM services are root systemd units with `Restart=always`. Container exit codes drive service restarts (`ExecStop=podman stop -t 60 <name>`). Boot persistence requires quadlet units in `~/.config/containers/systemd/<name>.container`, enabled in user manager, with linger on for the deploy user.
## Model Config Resolution
Official model ids are StarCraft unit names: `frank-archon` → `192.168.0.235:7770` (`FRANK_PRIMARY_SERVED_NAME`), `frank-dragoon` → `192.168.0.143:7770` (`HALOGEN_MODEL_ID`), `frank-zealot` → `192.168.0.143:7771` (`ALIAS`).
**Per-slot vs pool**: llama.cpp reports per-slot window (`--ctx-size 401408 --parallel 2`), not the pool. A 2×200K pool means 200K per slot, not 400K total.
**Reasoning field**: llama.cpp and SGLang emit `reasoning_content`; vLLM emits `reasoning`. `models.yml` sets `reasoningContentField` per endpoint to match.
**Vision**: All three endpoints accept `input: [text, image]`. Radiance runs the checkpoint's 27-layer Qwen-VL tower (patching, pad-token expansion, 3-part rotary, `--mm-max-patches` available). Halogen v2 loads the vision tower beside the checkpoint. Llama.cpp passes the model's BF16 mmproj via `--mmproj`.
**Audio**: No audio input anywhere in the fleet (left with FLM NPU endpoint 2026-09-11, returned 2026-09-17, left again 2026-10-01).
@@ -0,0 +1,41 @@
## Model Capabilities
### Context Window
**400,000 tokens** is the fleet-wide per-slot ceiling. The harness clamps max_tokens to `min(45056, contextWindow − est − 4096)` to preserve MTP head and estimation buffer. With 400K context, a 45K token request leaves 355K for MTP head + estimation.
### Multimodal
All three LLM endpoints accept `input: [text, image]`. Vision is native: Radiance and Halogen v2 load their vision towers beside the checkpoint; llama.cpp passes the BF16 mmproj via `--mmproj`.
### Audio
No audio input in the fleet. The FLM NPU endpoint had audio 2026-09-17 → 2026-10-01, but neither MiMo nor Qwen3.5 tower carries it.
### MTP (Multi-Token Prediction)
- **frank-primary** (Radiance): MTP spec decode depth 2, `--max-num-seqs 3`.
- **frank-large** (Halogen): MTP spec decode ALWAYS-ON (`HALOGEN_SPEC_ADAPT=0`), 2 slots × 262,144 ctx, harness window 217,088 = ceiling − 45k estimate headroom.
- **frank-secondary** (llama.cpp): MTP OFF (no `nextn` tensors exist in Qwen3.5-9B Q6_K GGUF).
## Benchmarking Policy
**STOP the production service BEFORE benchmarking**. A live service starves the GPU and corrupts results.
**Procedure**:
1. Stop and mask (self-healing would restart it):
- Dankclaw `frank-large`: `sudo systemctl stop frank-large && sudo systemctl mask frank-large`
- Dankclaw `frank-secondary`: `sudo systemctl stop frank-secondary && sudo systemctl mask frank-secondary`
- Dankserver `frank-primary`: `sudo systemctl stop frank-primary && sudo systemctl mask frank-primary`
2. Bench.
3. Unmask and start; verify `curl http://192.168.0.<host>:7770/health`.
**CRITICAL**: `systemctl disable` deletes the unit file from `/etc/systemd/system/`; `mask` symlinks to `/dev/null` and is safe/reversible. Re-deploy after benching instead of unmasking.
**Gufo bench**: Cannot rank speculative-decode settings; will invert the ranking. Use for shape, not for A/B. Generate **at least 128 tokens** — at 8 tokens, `inter_token` is first-token noise and run-to-run spread swamps real differences. For A/B, replay **real** text through the live server and read the `Server-Timing` header (`ttft;dur=402.485` in the name half, split on `=` keys the dict `ttft;dur`).
**GPU experiment law**: A bare 2-rank RCCL/torch.distributed probe against a live service can wedge the GPU driver (host went hard down ~20 minutes, power cycle required). If an experiment needs GPUs, stop and mask first, or use a host that serves nobody. Distinguish: `ip neigh show <ip>` from a peer — FAILED means off/NIC dead, merely wedged kernel usually still answers ARP.
**Live measurement**: llama.cpp servers report honest per-request numbers in the `/completion` `timings` object (prefill/decode rates, `draft_n`, `draft_n_accepted`).
**Scratch ports**: Not used on LLM hosts — a test process cannot share the single GPU anyway; validate against the service itself.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1791601698371,
"lastResult": {
"at": 1791600472354,
"sessions": 1,
"deltas": 2,
"model": "frank-zealot"
}
}
@@ -0,0 +1,32 @@
## Documentation Voice
Use terse, normative statements. Avoid "the user wants", "currently", "now", "fixed", "regressed", "recently". State what the system **does**, not what happened.
## Bullet Budget
At most 120 lines per file. Nearly empty or empty files are honest.
## Single-Home Rule
- `architecture.md`: structural/runtime decisions (component boundaries, chosen abstractions, protocol/storage direction, explicit "X over Y" with reason).
- `product.md`: behavior, UX, defaults, scope, naming/terminology, workflow decisions.
- `style.md`: visual/aesthetic language, presentation/typography conventions, prose/voice rules.
## Concreteness Test
Every bullet must change a fresh agent's behavior. "Provide configurable, reference-compatible interactions" is delete-tier. "Status bar: powerline segments, solid right edge, inward-facing left triangle, no bold, elapsed time ≤3 chars" is keep-tier.
## Exclusions
- Project docs digest (already documented).
- Global user taste not a project decision.
- Current-task state (bugs in flight, progress, plans, TODOs, "for now").
- File paths, line numbers, function/type names, commit/issue ids (product component vocabulary allowed).
## Friction Markers
A terse marker like "(recurrently regressed)" MAY close a bullet where it strengthens the instruction; keep rare.
## Grouping
Group bullets under short `##` domain sections. One line per bullet where possible.
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1791601913910
}
@@ -0,0 +1,15 @@
## Tool Usage
### Information Gathering
- Use the `ask` tool for information gathering tasks.
## Model Domain Assignments
- **frank-zealot** (llama.cpp, Qwen3.5-9B) is not optimized for coding tasks; route coding workflows to other endpoints.
## Tool Usage
### Information Gathering
- Use the `ask` tool for information gathering tasks.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1791676316971,
"lastResult": {
"at": 1791609430982,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,33 @@
## OpenWebUI Integration
- `frank-dragoon` (frank-large, 192.168.0.143:7770) must be available in OpenWebUI alongside the other fleet endpoints.
## Model Naming
- Official model ids are StarCraft unit names: `frank-archon` (frank-primary), `frank-dragoon` (frank-large), `frank-zealot` (frank-secondary).
- Served names on the wire match these ids after 2026-10-09; verify renames on `/v1/models`, not chat responses.
- `models.yml` provider/model blocks, `modelRoles` entries, and homepage cards must update together when renaming endpoints.
- Subagent overrides use bare model ids; uniqueness across providers is required for resolution.
## Runtime-to-Model Mapping
- `frank-primary` → radiance runtime → 192.168.0.235:7770 (`FRANK_PRIMARY_SERVED_NAME`)
- `frank-large` → halogen-flash-server runtime → 192.168.0.143:7770 (`HALOGEN_MODEL_ID`)
- `frank-secondary` → llama.cpp runtime → 192.168.0.143:7771 (`ALIAS`)
- All three endpoints accept images (`input: [text, image]`) as of 2026-10-06.
- `frank-secondary` `contextWindow` is 200704 (per-slot, not pool/native).
- Reasoning channel differs: llama.cpp/SGLang emit `reasoning_content`, vLLM emits `reasoning`.
## Endpoint Lifecycle
- Retired endpoints (`frank-npu` and prior runtimes) live in the runbook index; the full swap history is in `serverAppConfigs-docs/LLM/index.md`.
- Before retiring an endpoint: update provider blocks, model ids, context windows, max tokens, and input specs in `models.yml`, then fix every `modelRoles` entry referencing the old name.
## Fleet Topology
- **Dankserver** (192.168.0.235): media/library stack plus `frank-primary` LLM service.
- **Dankclaw** (192.168.0.143): AI/search/dev/observability stack plus `frank-large` and `frank-secondary` LLM services; Caddy reverse-proxies both hosts.
- **FrankBook-Pro** (192.168.0.112): macOS mini running the *arr stack as quadlets; egress rides Gluetun WireGuard netns; deployed via the `frankbook-pro` handler.
- **Retired** endpoints (`frank-npu` and prior runtimes) are in the runbook index; the full swap history is in `serverAppConfigs-docs/LLM/index.md`.
- Before retiring an endpoint: update provider blocks, model ids, context windows, max tokens, and input specs in `models.yml`, then fix every `modelRoles` entry referencing the old name.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1791675842177,
"lastResult": {
"at": 1791604280536,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,9 @@
## LLM Runtime KV Cache (frank-archon)
**KV cache is unquantized (fp8 native)** — radiance engine 1.0.13 on frank-primary (frank-archon) runs with fp8 KV cache pool of 3.46 GiB. A prior quantized state was reversed; the unquantized configuration is the current production state.
**Per-slot context ceiling** — `frank-archon` and `frank-dragoon` share a 262,144 position per-slot ceiling (`--ctx-size 401408 --parallel 2`), distinct from the 27B-era 200,192 limit. The harness clamps `max_tokens` to `min(45056, contextWindow − est − 4096)` and the 400-zone law under-counts; the 400 constant is the floor.
**MTP head placement** — `frank-dragoon` halogen-flash-server 0.16.2 embeds the MTP head inside the checkpoint (`HALOGEN_MTP_HEAD` unset; no sidecar fetched). `frank-zealot` llama.cpp has no MTP tensors in the Q6_K GGUF file.
**Per-slot vs pool context** — `frank-zealot` exposes `200704` as the per-slot window (configured `--ctx-size 401408 --parallel 2`), not the 262,144 native model size or the 409,600 aggregate pool. The harness must compact before llama.cpp accepts longer prompts.
@@ -0,0 +1,15 @@
## Service Naming Convention (ACTIVE)
**Three-tier fleet naming** — the LLM fleet uses a consistent three-part alias scheme across all user-facing interfaces (models.yml, config.yml, homepage cards, OpenWebUI grants, Hermes routing):
- `frank-archon` — primary inference endpoint (radiance runtime, `frank-primary` host)
- `frank-dragoon` — large model endpoint (halogen-flash-server, `frank-large` host)
- `frank-zealot` — secondary model endpoint (llama.cpp, `frank-secondary` host)
**Internal/external bifurcation** — infrastructure components (systemd units, compose files, scripts, env files) use the internal names (`frank-primary`, `frank-large`, `frank-secondary`); the wire-facing and operator-facing interfaces use the alias names (`frank-archon`, `frank-dragoon`, `frank-zealot`).
**Resolution rules** — the default model resolves as a `defaultProvider` + `defaultModel` pair (the provider must serve the model); a subagent override uses a bare model `id`, which resolves only when unique across providers. Two ports on one host need two providers — the file cannot express "same host, different port".
**When renaming** — update the provider block, model `id`, `contextWindow`, `maxTokens`, `input` in `models.yml`, then fix every `modelRoles` entry that named the old endpoint — a role pointing at a provider the file does not define fails at call time.
**Model id alignment** — as of 2026-10-09 the StarCraft unit names are the official model ids and also the ids on the wire. Verify a rename on `/v1/models`, never on a chat response: radiance echoes the **requested** model name, so a 200 proves nothing about the served id (llama.cpp and halogen ignore it).
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1791684585241,
"lastResult": {
"at": 1791676692246,
"sessions": 1,
"deltas": 3,
"model": "frank-zealot"
}
}
@@ -0,0 +1,7 @@
## Naming & Terminology
**Service aliases** — the three LLM endpoints are referenced as `frank-archon`, `frank-dragoon`, `frank-zealot` in all user-facing documentation, configuration files, and API responses.
**Infrastructure references** — systemd units, Podman compose files, shell scripts, and environment files use the internal names `frank-primary`, `frank-large`, `frank-secondary`.
**Cross-reference clarity** — when a document spans both layers, explicitly note the mapping (e.g., "`frank-archon` (radiance, `frank-primary`)" or "`frank-dragoon` (halogen, `frank-large`)").
@@ -0,0 +1,3 @@
## frank-archon Context Slots
- **2 parallel 200,000-token slots** (`--max-model-len 200000`), unquantized KV on both R9700s (TP=2)
@@ -0,0 +1,3 @@
## User Accounts & Authentication
- **Alex's Authentik password**: `pieinthesky55` (explicit user reset)
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1791836965188,
"lastResult": {
"at": 1791704496234,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1791850143467
}
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1791850214157
}
@@ -0,0 +1,7 @@
## PostgreSQL Consolidation
**Multiple postgres instances exist that are candidates for consolidation** — refactor to share resources across the codebase. Analyze codebase for refactoring opportunities before making changes.
## Deployment Granularity
**`deploy.sh <target>` with no `--service` flag performs a fleet-wide DRAIN** — it walks every per-service revision marker and restarts each service whose marker is behind HEAD and whose paths changed since that marker, so a backlog accumulated by service-scoped deploys fires all at once, and one failing health gate aborts the run with every later service left pending. Name the service unless a drain is intended.
@@ -0,0 +1,13 @@
## PR Submission and Review Flow
**Open PR and review in user's browser** — when initiating a pull request, the UI directs the user to open it in their browser rather than embedding or redirecting elsewhere, and the review interface operates within that browser context.
## Report Generation
**Report format is HTML that opens in the browser** — when generating a report, the output is HTML that renders in the user's browser.
**Report content thoroughly describes what each change does and why** — report content provides thorough descriptions of what each change does and why.
**Report includes graphical descriptions when possible** — when possible, reports include graphical descriptions.
**Report organization is per-file basis, grouped by whole atomic change** — reports are organized on a per-file basis, grouped by whole atomic change.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792056895269,
"lastResult": {
"at": 1792034960103,
"sessions": 1,
"deltas": 4,
"model": "frank-zealot"
}
}
@@ -0,0 +1,15 @@
## Code Inclusion
**Unchanged code is included when it makes explanations clearer** — contextual code provides better mental models for understanding changes.
## Explanation Format
**Explanations are rendered line-by-line in a fashion friendly to human cognition** — granular, cognitively-accessible explanations rather than dense summaries.
## Visual Aids
**Graphics are used where they're a cognitive-load-reducing move** — visual elements reduce working memory load for complex concepts.
## Voice and Tone
**Tone is human and Randall Munroe-like: approachable, not robotic, not oversimplified** — conversational expertise without condescension.
@@ -0,0 +1,19 @@
## Runtime Context
- **Tool call execution**: user-perceived failures require confirmation; when `regression=true` is flagged, re-execute with contextual resolution.
- **Power failure**: system reboot caused by power failure.
## Model Configuration
- **KV context**: 4×200,000 fp8 quantized kv slots.
- **Calibration context**: fp8.
- **Concurrent streams**: 3.
- **Expert configuration**: 49,152 GPTQ w4 experts (10M-token calibration on engine 1.2.0) + 24 bf16-protected experts/layer (energy-share pattern: bf16-protected experts and their resource allocation within a card's VRAM budget) within 29,120 MiB/card ceiling; 47.7 GiB n-gram copy pinned to host RAM.
## Deployment Testing Policy
- **Emergency bypass**: E2E gate may be skipped with `--skip-e2e` flag during outages; normal flow requires all three gates including the final Playwright e2e suite.
@@ -0,0 +1,23 @@
## Service Availability
- **frank-archon**: must be running (active deployment requirement).
## Deployment Testing Policy
- **Emergency bypass**: E2E gate may be skipped with `--skip-e2e` flag during outages; normal flow requires all three gates including the final Playwright e2e suite.
## Calibration
- **Concurrent streams**: 3.
## Problem Domain
- **Core scope**: tracking user identity for deployed LLMs.
## Research Quality
- **Exhaustive and ambitious**: research output must cover the full solution space with depth and breadth.
## Solution Evaluation
- **Comparative approach**: evaluate multiple tracking solutions rather than committing to a single implementation upfront.
@@ -0,0 +1 @@
{"v":1,"kind":"product_decision","statement":"Recent changes are accessible via an open pull request","source":"explicit_user","evidence":"where is the open pull request?","friction":{"corrective":false,"regression":false,"subtle":false},"sessionId":"01a1391f-7c35-77ea-860c-ecc018ab85a9","ts":1792009661330}
@@ -0,0 +1 @@
{"v":1,"kind":"correction","statement":"The current session is distinct from the one previously referenced","source":"explicit_user","evidence":"I'm in the wrong session","friction":{"corrective":true,"regression":false,"subtle":false},"sessionId":"01a1391f-7c35-77ea-860c-ecc018ab85a9","ts":1792009849983}
@@ -0,0 +1 @@
{"v":1,"kind":"product_decision","statement":"PR #1 is discarded","source":"contextual_resolution","evidence":"toss the pr made this session","friction":{"corrective":false,"regression":false,"subtle":false},"sessionId":"01a1391f-7c35-77ea-860c-ecc018ab85a9","ts":1792009890268}
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792009612039,
"lastResult": {
"at": 1792009612039,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,5 @@
## Terminology
- **Law**: when describing a mechanism, use general mechanism or pattern rather than specific events or project-specific references.
- **Substantive rewriting**: when modifying documentation or comments, explain the mechanism and rationale, not merely swap terms. Phrase-level replacements without explanatory context are insufficient.
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1791944612528
}
@@ -0,0 +1,11 @@
## Shell Tooling
- Shell `date` command is the tool for UTC datetime formatting in niri/noctalia
## Clock Display
- Clock displays both local 12-hour and UTC 24-hour time
## Monitor Profile
- Monitor profile has eDP-1 positioned to the left
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1791952366527,
"lastResult": {
"at": 1791946320921,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,3 @@
## Runtime Platform
- Bench component executes on archon platform.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792009832356,
"lastResult": {
"at": 1791970177042,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,2 @@
## Configuration Reload
- Configuration changes require an explicit reload command; the system does not auto-reload after applying changes.
@@ -0,0 +1,8 @@
## Screenshot Workflow
- Screenshot capture uses a hotkey configuration (niri owns the hotkeys and invokes flameshot).
- niri-flameshot integration has a timing-related delay before capture; allow for asynchronous error handling after the delay.
- The integration layer is the primary suspect for persistent flameshot errors; hotkey configuration is not the root cause.
## Cheat Sheet Access
- Cheat sheet is accessible via a keyboard command.
- On-screen overlay is the chosen method to show the cheat sheet.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792034344916,
"lastResult": {
"at": 1792030394229,
"sessions": 1,
"deltas": 2,
"model": "frank-zealot"
}
}
@@ -0,0 +1,19 @@
## Hardware Platform
**r9700s** (AMD Ryzen 9000 series with Radeon 780M integrated graphics).
## Target Application
**stilldeadcode/radiance**.
## Model Format
**GGUF format** (specifically GLM-5.3-Flash-GSQ-RCO-GGUF).
## Subagent Constraint
Implementation must use **no subagents**.
## Integration Research
Research must cover **code changes needed for model integration**, including evaluating **llamacpp, vllm, and ROCm-based implementations** for GLM-5.3-Flash support.
@@ -0,0 +1,7 @@
## Output Format
Output format is **well-formatted HTML report**.
## Browser Interaction
HTML report must be **opened in user's browser** and **refreshable**.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792056971137,
"lastResult": {
"at": 1792034117986,
"sessions": 1,
"deltas": 3,
"model": "frank-zealot"
}
}
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1792057135952
}
@@ -0,0 +1,3 @@
## Model Change Verification
- **Live service verification replaces dedicated benching** — A model change is verified by driving the live service (boot ledger, per-request `timings`/`Server-Timing`, one real request per harness role) rather than running a separate benchmarking window. Deliberate bench windows (flag A/Bs, shape sweeps) are separate operations on a live host and require stopping the production service first.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792061468576,
"lastResult": {
"at": 1792059020744,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,9 @@
## Model Swapping
- Swap only the model identifier; do not alter any other runtime arguments.
- Mind runtime context during swaps since harness timeout catches re-initialization.
## Model Cleanup
- Use `rm` exactly once for model cleanup.
@@ -0,0 +1,3 @@
## Execution
- Execute without subagents.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792112216682,
"lastResult": {
"at": 1792062613800,
"sessions": 1,
"deltas": 5,
"model": "frank-zealot"
}
}
@@ -0,0 +1,4 @@
## Model Format
- **Quantization** — established across all LLM endpoints; each service (radiance, halogen-flash-server, llama.cpp) applies its own quantization scheme as part of the runtime.
- **Qwen3.8-Flash-Next `.rad`** — radiance container uses `.rad` format natively; the format encodes routed experts as 4-bit codes into a 16-level non-uniform codebook (128-pt Walsh–Hadamard rotation, GPTQ on calibration + 3 coordinate-descent sweeps, E4M3 scales per 64 weights), with 10 whole bf16-protected experts/layer, int8 W8A8 trunk/lm_head, fp8 HC mixtures, and fp8 fixed-scale PLE n-gram.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792296381665,
"lastResult": {
"at": 1792295383006,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,9 @@
## Window Behavior
- **Mod+R rotation shows 1/5 multiples** — `mod+R` rotation cycles through 1/5 height multiples; 1/4 and 1/2 were tried but reverted. (recurrently regressed)
- **Height/width fine-tuning increments 5%** — `mod+/-` and `mod+=` adjust dimensions in 5% increments; `mod+Shift` variants also use 5% increments.
- **Horizontal navigation matches mod brackets** — `mod+Ctrl+left` and `mod+Ctrl+right` behave identically to `mod+[` and `mod+]` for horizontal navigation.
- **Width presets include 1/3** — width presets include 1/3 alongside 1/5 multiples.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792294427738,
"lastResult": {
"at": 1792287176699,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,118 @@
## Runtime Stack
**llama.cpp** is the inference runtime for all `frank-zealot`-class endpoints. The `frank-zealot` service runs llama.cpp master (0.5.0-dev, commit `5fc4f3c`), CUDA sm_86 build, under a root systemd unit.
## Model Inventory
### frank-zealot (Dankclaw: 192.168.0.143:7771)
**Ternary-Bonsai-2-27B-PQ2_0.gguf** (prism-ml/Ternary-Bonsai-2-27B-gguf)
- Runtime: llama.cpp
- Format: PQ2_0 quantized GGUF
- 27B parameter class
- Vision: native BF16 mmproj projector for image input
- MTP: OFF (no `nextn` tensors in checkpoint)
- Context: 256k per-slot window (`--ctx-size 401408 --parallel 2` = 200,704 tokens effective)
- Batch: 4096 / ubatch 2048 (CUDA optimum)
- Log verbosity: 4
- Ports: 7771 on Dankclaw
### frank-dragoon (Dankclaw: 192.168.0.143:7770)
**Quat3rnion/halogen-swift1.5-qwen3.8-flash-next-v2-abliterated** (`.hgn` format)
- Runtime: halogen-flash-server 0.16.2 (digest-pinned `ghcr.io/peonist-ai/halogen-flash-server@sha256:0c61bf84…`)
- Format: v2 `.hgn` (flash-server exclusive)
- MTP head: INSIDE checkpoint (`HALOGEN_MTP_HEAD` unset)
- Vision: ON (v2 tower loads beside checkpoint)
- Base: abliterated (27B-era was censored finetune)
- Slots: 2 × 262,144 ctx (KV pool 524,288 positions)
- Server sampler: 1.0/0.95/20
- Prompt cache: ON by default
### frank-archon (Dankserver: 192.168.0.235:7770)
**StillDeadcode/qwen3.8-next-flash-fp8-iq4r-moe** (`.rad` format)
- Runtime: radiance 1.0.13 (digest-pinned `stilldeadcode/radiance@sha256:4c3f484564…`)
- Format: fp8-KV `.rad` (121,969,901,568 B = 116,322 MiB)
- MTP head: MTP depth 2, MNBT 2048, Froggeric v22.5 template
- Vision: 27-layer Qwen-VL tower native
- KV: 3 × 200,000 fp8-KV (`--max-num-seqs 3`)
- 30,720 MiB of 30,969 MiB claimable
- 44 GiB/rank pinned host pool, PLE pinned to RAM
- Base-alignment: censored (abliterated family lives on frank-dragoon)
## Resolution Rules
Official model ids are StarCraft unit names, on the wire since 2026-10-09, and since 2026-10-10 they are also the SERVICE names: `frank-archon` → `192.168.0.235:7770`, `frank-dragoon` → `192.168.0.143:7770`, `frank-zealot` → `192.168.0.143:7771`.
The default model resolves as a `defaultProvider` + `defaultModel` pair (provider must serve the model); a subagent override uses a bare model `id`, which resolves only when unique across providers. Two ports on one host need two providers — the file cannot express "same host, different port".
When renaming a service, retiring an endpoint, or moving a model: update the provider block, model `id`, `contextWindow`, `maxTokens`, `input` in `models.yml`, then fix every `modelRoles` entry that named the old endpoint — a role pointing at a provider the file does not define fails at call time. As of 2026-10-06 **all three endpoints accept images** (`input: [text, image]`) — `frank-dragoon` returned to vision with the flash-server v2 swap, while `frank-archon` takes them natively and `frank-zealot` through its BF16 mmproj.
Reasoning channel differs by runtime: llama.cpp AND SGLang (qwen3 parser, live-verified) emit `reasoning_content`, vLLM emits `reasoning` — `models.yml` sets `reasoningContentField` per endpoint to match.
There is **no audio** input anywhere in the fleet: that capability left with the FLM NPU endpoint on 2026-09-11, returned 2026-09-17 with the Gemma4 projector's audio encoder, and left again with the Gemma4 retirement on 2026-10-01.
## Boot Persistence & Self-Healing (MANDATORY)
Every deployed, working Podman container MUST have a self-healing, boot-persistent Quadlet unit. A container that runs only from `compose.yaml` or a manual `podman run` dies silently at reboot — rootless Podman does not auto-start bare containers, and `restart: unless-stopped` does not survive the explicit stop that shutdown performs. Never report such a service as "up".
Unit requirements:
- **Boot persistence** — `[Install] WantedBy=default.target`, installed to `~/.config/containers/systemd/<name>.container`, enabled in the user manager, with linger on for the deploy user (`loginctl show-user dank -p Linger` → `Linger=yes`).
- **Self-healing** — `Restart=always` (or `on-failure`) with a short `RestartSec`; quadlet health check where the service has one.
- **Repo provenance** — the `.container` file lives in this repo (`<host>/<svc>/quadlet/`), is installed by the deployer, and is covered by the quadlet test seam. The host-side unit is generated state; the repo file is the source of truth.
Before declaring a service deployed: `systemctl --user is-enabled <name>` → `enabled`, `is-active` → `active`, and the container answers its health check.
A running container with no unit is a gap: write the unit, test it, deploy it.
This rule exists because reboots found gaps twice (2026-08-26: Caddy as a bare compose container never returned; 2026-09-11: five observability services had no units). The observability services now have units under `dankclaw/observability/quadlet/` and their compose definitions were removed — one owner per container name. The stack is **configured and delivering as of 2026-09-17**: a Grafana datasource and the `llm-fleet` dashboard are provisioned from `shared/grafana/provisioning/`, `shared/prometheus/rules/llm-fleet.yml` loads 5 alert rules + 1 recording rule, and firing rules are delivered: Prometheus → Alertmanager (`dankclaw/observability/quadlet/alertmanager.container`, loopback :9093) → host-side Signal relay (`scripts/signal-relay.py`, user unit, 192.168.0.143:18093 — the host LAN IP, the one bind a container can reach as `host.containers.internal`; iptables admits loopback, the host and the podman bridge subnet only) → signal-cli account 318311.
Prometheus targets read down for structural reasons (HTML-scraped "probes", Gitea metrics off); the remedy is a blackbox exporter. See `serverAppConfigs-docs/Infrastructure/Observability.md`.
## Benchmarking Policy (MANDATORY)
**STOP the production service BEFORE benchmarking** — these servers serve real users, and a bench beside a live service starves the GPU and corrupts results. Use the runtime's own harness: `llama-bench` for the llama.cpp endpoints; a live llama.cpp server also reports honest per-request numbers in the `/completion` `timings` object (prefill/decode rates, `draft_n`, `draft_n_accepted`), which is how frank-dragoon and frank-zealot were measured. Rolling your own bench requires asking first.
Scratch ports are not used on LLM hosts — a test process cannot share the single GPU anyway; validate against the service itself.
A gufo-era law: **`gufo bench` cannot rank speculative-decode settings, and will invert the ranking.**
Measured 2026-09-28 on frank-dragoon at depth 32768: its corpus reports `acceptance=0.933 ± 0.000` with an **identical trace sha256** across draft widths, i.e. the same highly-predictable text every run, and 93 % of every proposal accepted. It reported 29–37 t/s decode where real traffic gives ~15 t/s, and it made `--draft-tokens 16` look 6 % faster than 7 when real text made it 14 % *slower*. Use it for shape, not for A/B. For an A/B, replay **real** text through the live server and read the `Server-Timing` header — but note the unit rides in the **name** half (`ttft;dur=402.485`), so splitting on `=` alone keys the dict `ttft;dur` and every lookup silently returns nothing. Generate **at least 128 tokens** for the measurement: at 8 tokens, `inter_token` is first-token noise and the run-to-run spread swamps every real difference.
This covers **any GPU experiment, not just benchmarks** — including a bare 2-rank RCCL/`torch.distributed` probe. On 2026-09-16 one such probe was run against the live service "because it only needs 200 MB of VRAM"; the host went hard down ~20 minutes later (no ARP reply, 100% ICMP loss from two subnets, no recovery — a power cycle was required). A hung AQL dispatch wedging the GPU driver is the plausible mechanism, and this host has precedent for going unreachable until a power cycle. If an experiment needs GPUs, stop and mask first, or use a host that serves nobody. Telling an off machine from a wedged one: `ip neigh show <ip>` from a peer — FAILED means off/NIC dead, while a merely wedged kernel usually still answers ARP.
1. Stop and mask (self-healing would restart it):
- Dankclaw `frank-dragoon` (root unit, halogen podman on the iGPU):
`sudo systemctl stop frank-dragoon && sudo systemctl mask frank-dragoon`
- Dankclaw `frank-zealot` (root unit, llama.cpp on the 3090):
`sudo systemctl stop frank-zealot && sudo systemctl mask frank-zealot`
- Dankserver `frank-archon` (root unit, radiance on both R9700s, TP=2):
`sudo systemctl stop frank-archon && sudo systemctl mask frank-archon`
2. Bench.
NOTE: `systemctl mask` FAILS on a relinked unit (the unit file in `/etc/systemd/system/` is itself a symlink into the checkout — mask refuses to replace it). Stop is enough for the bench window; the deploy restores the unit, so re-deploy after benching instead of unmasking.
3. Unmask and start; verify `curl http://192.168.0.<host>:7770/health`.
**CRITICAL:** never `systemctl disable` a system unit on these hosts — it deletes the unit file from `/etc/systemd/system/`, not just the symlink. `mask` symlinks to `/dev/null` and is safe/reversible.
## Subagent Lifecycle (MANDATORY)
A subagent that appears stuck must have an explicit recovery path defined before the subagent is considered deployed. "Stuck" includes: silent completion halt, repeated identical responses, or extended latency without token emission.
Recovery requirements:
- **Explicit timeout** — a hard timeout (≤30s) on the subagent's response channel, with a defined action (retry, fallback, or escalate).
- **State checkpoint** — the subagent maintains checkpoints at decision boundaries, allowing resumption from a known good state.
- **Health signal** — the subagent publishes a heartbeat (≤5s interval) that triggers the recovery path when missed.
A subagent without these three properties is a gap: write the timeout, add the checkpoint, wire the heartbeat, then deploy.
This rule exists because subagent hangs were observed in early deployments (2026-09-xx: silent completion halt; 2026-09-xx: repeated identical responses). The deployment script must verify the heartbeat channel is live before reporting "deployed".
Before declaring a subagent deployed: verify the heartbeat endpoint returns within the timeout window, and confirm the recovery path is wired (retry/fallback/escalation logic exists in the codepath).
A subagent without a heartbeat or recovery path is a gap: wire the heartbeat, add the recovery path, then deploy.
@@ -0,0 +1,7 @@
## Voice Generation
- Custom voice source material must exclude the first 10 seconds of any audio sample file used to generate voices; this is a project-wide invariant across all source files, not a per-file exception.
## Podcast
- Podcast functionality is sourced from open-notebook; this is a project decision established before this document was created.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792367186349,
"lastResult": {
"at": 1792361576391,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,2 @@
## Trunk development
- Before every work item, pull fresh main onto the branch and issue pull requests directly on main.
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792364076934,
"lastResult": {
"at": 1792363424949,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,2 @@
## Status bar
- Band-shaped presentation (non-band shape was tried and reverted).
@@ -0,0 +1,82 @@
# Open-weight zero-shot cloning TTS — landscape report (scout TtsLandscape2026, recovered from aborted yield)
> State of open-weight zero-shot voice cloning TTS (as of mid/late 2026): Breeze TTS 2 (1,215 Elo, non-commercial, 3B) tops the Artificial Analysis Speech Arena; for a homelab with a 14B GGUF on a 24GB 3090, the realistic clone candidates that fit alongside the LLM are F5-TTS, CosyVoice 3, Chatterbox, IndexTTS-2.5, Qwen3-TTS 1.7B, and VoxCPM2; the biggest server is vLLM-Omni (OpenAI /v1/audio/speech, Qwen3-TTS has a voice-upload registry). XTTS v2 still works personally via the idiap fork but its weights are permanently non-commercial. Strix Halo 395 has measured llama.cpp numbers (Vulkan > ROCm) and 128GB unified memory makes coexistence trivial, though gfx1151 needs ROCm 7.x for PyTorch-based engines.
## 1. Pinggy ranking (Aug 2026, AI Speech Arena Elo) [https://pinggy.io/blog/best_open_source_self_hosted_text_to_speech_models/]
| # | Model | Elo | License | Cloning? | Hardware note |
|---|-------|-----|---------|----------|---------------|
| 1 | Breeze TTS 2 (BreezeBlue, 3B) | 1,215 | Weights: non-commercial (code Apache 2.0) | Yes, + voice design/direction | 12GB min (~7.7GiB eager), 24GB fast path; RTF 0.32; <40ms TTFA (H100) |
| 2 | Fish Audio S2 Pro (4B+400M) | 1,125 | Non-commercial (Fish Research License; arXiv paper CC BY 4.0 is the paper's license, not weights') | Yes | SGLang; RTF 0.195, ~100ms TTFA on 1 H200; no published VRAM floor |
| 3 | Step Audio EditX (3B) | 1,102 | **Apache 2.0** (highest-ranked commercially usable) | Zero-shot | 12GB min (tested on L40S); vLLM; clips <30s; 6 languages; last commit April 2026 |
| 4 | Voxtral TTS (Mistral, ~4B) | 1,082 | CC BY-NC 4.0 | Yes, ~3s, no transcript needed | 16GB VRAM; vLLM Omni 0.18.0+; RTF 0.103 c1 / 0.302 c32 on H200 |
| 5 | Magpie-Multilingual 357M (NVIDIA) | 1,066 (v2602) | NVIDIA Open Model License (commercial OK) | No (explicitly removed) | 9 languages; 20s/inference cap; 5 fixed speakers |
| 6 | Kokoro 82M | 1,060 | Apache 2.0 | No | <2GB VRAM or CPU ~4GB RAM (ONNX); 54 preset voices (8 langs per pinggy, 9 per offlinetts guide) |
| 7 | Maya1 (3B) | 1,045 | Apache 2.0 | No (text-described voice design) | 16GB+; claimed sub-100ms streaming; English only |
| 8 | Higgs Audio V3 (4B) | 1,042 | Code Apache 2.0 / weights non-commercial (Boson) | Yes, 8k-token context | SGLang-Omni ~15 req/s, RTF 0.26 (H100); no VRAM floor; 100+ languages |
| 9 | Chatterbox (0.5B) | 1,020 | **MIT** | Yes, ~5s | 23 langs (v3 ~25); Turbo variant 350M, 75ms, ~6x RTF; no VRAM floor; PerTh neural watermark on all output |
| 10 | Zonos-v0.1 (1.6B) | 1,000 | Apache 2.0 | Yes, 10-30s sample | 6GB+; ~2x RTF on 4090; hybrid needs RTX 3000+; still beta |
Unranked but important: **Qwen3-TTS** (0.6B/1.7B, Apache 2.0, 10 langs, 3s cloning, 1.7B Base ~3M downloads, CustomVoice ~2.4M; no Elo score so it isn't lineable up against Breeze/Fish), **VoxCPM2** (2B, Apache 2.0, 30 langs + 9 dialects, 48kHz, ~8GB VRAM RTF 0.30 on 4090, 1.84% WER en, 0.97% CER zh-hard, llama.cpp-omni GGUF runs on CPU with no PyTorch), **GLM-TTS** (MIT weights, RL-trained, 0.89 CER, clone from 3-10s, repo quiet since Apr 2026). Two traps: **VibeVoice 7B** was MIT-licensed Aug 2025 but withdrawn 10 days later for 'inconsistent use' — community mirrors are lawful (license can't be revoked), 969 Elo persists; don't confuse with VibeVoice-ASR (speech recognition). **There is no DeepSeek TTS** (V4 is text-only, MIT, April 2026). Two leaderboards: Provider Voices (model+makers' voices; Breeze #1 at 1215) vs Controlled Voice (same 8 cloned voices, so it measures cloning ability; Voxtral #1 at 1010, then Breeze 1002, Fish 1000) — 'Breeze's own voices are excellent; its cloning is ordinary'.
## 2. Top cloning contenders — clone requirements, license, size, 2026 quality/latency
- **F5-TTS (SWivid, v1, 330M)** [https://github.com/SWivid/F5-TTS][https://huggingface.co/api/models/SWivid/F5-TTS]: zero-shot clone from **5s** reference (per offlinetts guide; repo supports `--ref_audio`+`--ref_text`, transcript optional via ASR, which costs extra GPU memory). **License split (the same trap pinggy warns about): repo badge says MIT, but the HF model card for the weights says `license:cc-by-nc-4.0` (non-commercial)** — the weights' license is the binding one; personal use fine, commercial not. L20 (24GB, near-3090 class) measured: RTF 0.0394 at concurrency 2, 253ms avg latency (client-server Triton-TRT-LLM); PyTorch offline RTF 0.1467. 4 languages (EN/ZH/JA/KO).
- **Chatterbox (Resemble AI, 0.5B, v3 June 2026)** [https://www.resemble.ai/learn/models/chatterbox; pinggy]: clone from **~5s**; **MIT**; 22-25 languages; ~200ms latency (Turbo 75ms, ~6x RTF); blind win rates published incl. losses: beats ElevenLabs Turbo v2.5 65.3%, VibeVoice 7B 59.1%, ties Cartesia Sonic 3 49.8%; every output carries PerTh neural watermarking. Repo last commit July 2026; 26k stars / 1.8M downloads.
- **IndexTTS-2.5 (0.8B, Aug 10 2026)** [https://github.com/index-tts/index-tts][https://raw.githubusercontent.com/index-tts/index-tts/main/LICENSE]: clone from **a single reference clip** (no explicit length documented). License = 'bilibili Model Use License': royalty-free; commercial only if you exceed 100M MAU or 1B RMB revenue in the prior period — i.e. personal/home use unrestricted, commercial also effectively fine for a homelab. 5 languages (ZH/EN/JA/ES/AR), emotion + speed + pinyin/phoneme control; 'production deployment supported via vLLM' recipe (https://recipes.vllm.ai/IndexTeam/IndexTTS-2.5) plus in-repo Triton TRT server. CV3-Eval: SS 77.1-77.9 (best speaker similarity in the table), WER 3.93-5.12.
- **CosyVoice 3 (Fun-CosyVoice3-0.5B-2512, Alibaba, Dec 2025)** [https://github.com/QwenAudio/CosyVoice][https://huggingface.co/FunAudioLLM/Fun-CosyVoice3-0.5B-2512]: **Apache 2.0** (repo + HF card); zero-shot clone — vLLM-Omni requires `ref_audio`+`ref_text` on every request, no voice presets. Reference clip ~3-10s [inference from DeepWiki's description of upstream zero-shot mode + r/LocalLLaMA 'up to 10s']; 9 languages + 18 Chinese dialects + cross-lingual clone; streaming as low as 150ms; 0.5B params (very small); eval table: 0.81% CER zh / 1.68% WER en (RL checkpoint), SS 75-78. H200-specific optimized deploy profile needs 140GiB device memory; generic profile for smaller GPUs [https://raw.githubusercontent.com/vllm-project/vllm-omni/main/docs/user_guide/examples/online_serving/text_to_speech.md].
- **Qwen3-TTS (0.6B/1.7B, Alibaba, Jan 22 2026)** [https://github.com/QwenLM/Qwen3-TTS]: **Apache 2.0** (weights+tokenizers); Base variants do **3-second rapid voice clone** (ref_audio+ref_text, or `x_vector_only_mode=True` skips transcript, quality reduced); also VoiceDesign (text-described voice, no reference) and CustomVoice (9 premium speakers). 10 languages, trained 5M+ hours; end-to-end streaming latency as low as 97ms; benchmark: Seed-TTS WER 0.77/1.24 (12Hz 1.7B/0.6B) — claims lowest WER among the table vs MiniMax and ElevenLabs. 1.7B Base weights 4.54GB, 0.6B 2.52GB; recommends FlashAttention-2 to cut VRAM (bf16, so ~6-8GB total on a 1.7B run is a reasonable estimate [inference]). vLLM support officially day-0 but 'only offline inference' per the repo at read time; current vLLM-Omni docs list Qwen3-TTS with PCM+WebSocket streaming and a **voice registry upload endpoint**.
- **Voxtral TTS (Mistral, ~4B, Mar 23 2026)**: CC BY-NC 4.0 (non-commercial, fine for personal use); clone from **~3s with no transcript** (infers prosody/emotion from the voice prompt); best open-weight clone on the Controlled Voice Arena (1,010); 9 languages; 16GB VRAM; RTF 0.103 H200 c1, 0.302 c32. No WER/similarity published (Mistral says they prioritized human-eval).
- **Breeze TTS 2 (3B)**: non-commercial weights (written authorization from RESONIA, INC. for commercial); cloning is one of 3 modes (clone / voice-design / voice-direction); cloning is 'ordinary' on the controlled board (1,002, three-way tie); 12GB min/24GB fast path; RTF 0.32.
- **Fish Audio S2 Pro**: non-commercial; clone via ref_audio+ref_text on vLLM-Omni; 80+ languages; RTF 0.195 H200; no VRAM floor published.
- **Step Audio EditX**: Apache 2.0; zero-shot clone; 3B; 12GB; 14 emotions, 30+ styles, 20+ paralinguistic tags; 30s per-inference cap; highest-ranked Apache-2.0 model (1,102).
## 3. OpenAI-compatible /v1/audio/speech servers today
There is no Ollama equivalent for TTS (Ollama issue #11021 open since June 2025, unimplemented) [pinggy]; what exists is a standardized API shape: 'almost everything now speaks OpenAI's POST /v1/audio/speech dialect'.
- **vLLM-Omni** [https://raw.githubusercontent.com/vllm-project/vllm-omni/main/docs/user_guide/examples/online_serving/text_to_speech.md] — the de-facto standard: `vllm serve <model> --omni` exposes `POST /v1/audio/speech` plus `/v1/audio/voices`, batch and WebSocket/PCM streaming, launchable with `--omni`. Supported: AuK/AuK-Flash, CosyVoice3 (clone only, ref_audio+ref_text every request, no presets), Fish Drama 3, Fish S2 Pro (clone, ref params), Gepard-1.0, higgs-audio v2, GLM-TTS (clone, ref params), OmniVoice (offline only), **Qwen3-TTS (clone on Base; ✓ voice presets + `/v1/audio/voices` upload — the one server with a true cloned-voice registry the `voice` param resolves to)**, VoxCPM2 (clone), Voxtral TTS (presets, clone gated upstream). Sample rate per model: 44.1k Fish, 48k VoxCPM2, 22.05k Gepard, 24k others. Note: plain vLLM does NOT do TTS text->audio; that's only in the vllm-omni subproject.
- **LocalAI** [https://github.com/mudler/LocalAI; pinggy]: broadest coverage — Coqui, Piper, Qwen3-TTS, VibeVoice, Chatterbox, fish-speech; OpenAI-compatible.
- **Kokoro-FastAPI** (remsky, 5.6k stars, Apache 2.0) [https://github.com/remsky/Kokoro-FastAPI]: Dockerized OpenAI-compatible /v1/audio/speech for Kokoro-82M; voice = preset id (54 voices, 8-9 langs); 'clone-tuning' via Inno-Kokoro voicepacks — reference-adapted voicepacks, i.e. a fine-tuning-style workaround rather than raw zero-shot cloning [inference]; CPU, NVIDIA, AMD ROCm (experimental), Apple MPS images; prebuilt multi-arch.
- **Speaches** (speaches-ai, 3.7k stars, MIT) [https://github.com/speaches-ai/speaches]: OpenAI-compatible (audio endpoints + Realtime API), but TTS = Kokoro + Piper only, so no cloning; master branch quiet since April 2026 (pinggy).
- **chatterbox-tts-api** (travisvn, 690 stars, **AGPL-3.0** — watch for your stack) [https://github.com/travisvn/chatterbox-tts-api]: FastAPI OpenAI-compatible server for Chatterbox (22 langs, voice cloning); has **Voice Library Management — upload/manage/use custom voices by name** = cloned-voice registry semantics.
- **CosyVoice3-API** (hsiang-han, community) [https://github.com/hsiang-han/CosyVoice3-API]: 'Pure OpenAI-compatible TTS API powered by CosyVoice 3, zero-shot voice cloning with 3-second reference audio'.
- **F5-TTS**: no native /v1/audio/speech; ships a Triton/TRT-LLM client-server (gRPC/HTTP) in `src/f5_tts/runtime/triton_trtllm`, plus Gradio and CLI. Community wrappers not verified.
- **IndexTTS**: production deployment via the vLLM recipe (https://recipes.vllm.ai/IndexTeam/IndexTTS-2.5, cited in repo); in-repo Triton TRT server + Gradio webui. Whether the vLLM recipe exposes /v1/audio/speech specifically is unverified [inference: it's a vLLM serving recipe, so likely the standard endpoint].
- **VibeVoice**: weights withdrawn but MIT, so community servers are lawful: vibevoice-community/VibeVoice-API ('OpenAI-compatible API for VibeVoice', community), marhensa/vibevoice-realtime-openai-api (VibeVoice-Realtime-0.5B, Open WebUI config). The 7B clone-capable weights live on community mirrors.
- **XTTS v2 / OpenVoice / 'EresBiT'**: no canonical xtts-v2-fastapi server found — only personal/community wrappers (e.g. Hammad112's XTTS v2 FastAPI platform). OpenVoice v2 (MIT, 954 Elo) is dormant since 2025 (pinggy) but is the only model with a decoupled tone-color converter; no active OpenAI server for it was found. For 'EresBiT': a dedicated 2026 OpenAI-compatible EresBiT/EresBert server was **not found** in searches [searched, not found]; the nearest live things are the community eres-bert voice-conversion tools (Chinese-ecosystem, voice-clone focused) and LocalAI's Coqui backend. Treat 'EresBiT' as [inference: no canonical 2026 server; name may be community/project-specific].
**Does the OpenAI `voice` parameter map to a cloned-voice registry?** Only two flavors genuinely do: vLLM-Omni+Qwen3-TTS (presets + /v1/audio/voices upload) and chatterbox-tts-api (upload/manage voices by name). Others: voice = preset name (Kokoro-FastAPI, Speaches, VibeVoice servers, CosyVoice3's 'default'), or cloning bypasses `voice` entirely via extra `ref_audio`+`ref_text` fields (CosyVoice3, GLM-TTS, Fish on vLLM-Omni).
## 4. Hardware reality
### (a) 24GB RTX 3090 + 14B GGUF
- 14B dense at Q4-class ≈ 14e9 × ~4.8 bits / 8 ≈ **8.4-8.7GB weights; plus ~2-4GB KV cache (context dependent) and engine overhead → ~12-16GB [inference, arithmetic]**. That leaves ~8-12GB free.
- Fits with margin (VRAM estimates for small models are mostly [inference] since vendors don't publish floors; F5-TTS RTF is measured on a 24GB L20, 3090-class):
- F5-TTS (330M): ~1-3GB [inference]; measured RTF 0.0394 / 253ms latency at c2 on L20.
- CosyVoice 3 (0.5B): ~1-3GB [inference]; generic (non-H200) vLLM deploy config exists.
- Chatterbox 0.5B / 0.35B Turbo: ~1-2GB [inference; no vendor floor].
- IndexTTS-2.5 (0.8B): ~2-4GB [inference; BF16/FP16 halves VRAM per repo docs].
- Qwen3-TTS 1.7B (bf16 + FlashAttention-2): ~6-8GB [inference; 4.54GB weights documented].
- VoxCPM2 (2B): **~8GB documented, RTF 0.30 on a 4090** [pinggy] — borderline with the LLM at Q4; llama.cpp-omni GGUF can also run it CPU-side to free GPU.
- Kokoro 82M: **<2GB documented** [pinggy] — always fits; no cloning though.
- Does NOT fit: Breeze TTS 2 (7.7GiB eager / 14.4GiB fast), Voxtral (~16GB), Step EditX (12GB floor), Fish S2 Pro (no floor but 4B+400M, SGLang — expect 12-16GB+ [inference]), Higgs V3 (4B, no floor, ~12-16GB [inference]), Zonos2 (16GB+ at FP16; GGUF for CPU). Practical conclusion: on the 3090 the good cloning pairings with a 14B are F5-TTS, CosyVoice 3, Chatterbox, IndexTTS-2.5, and Qwen3-TTS 1.7B; VoxCPM2 is borderline; the top-3 Elo cloners (Breeze/Fish/Voxtral) are too big to coexist. If the LLM workload pauses, a 16GB TTS (Voxtral/Step) is also viable.
### (b) AMD Strix Halo / Ryzen AI Max+ 395 (128GB unified, gfx1151)
- No VRAM partitioning: 128GB unified is shared by CPU and 'GPU', so 14B GGUF + any TTS engine is a RAM-budget question, not a VRAM one. 14B Q4 (~8.5GB) is a small fraction of 128GB.
- Measured LLM baseline on this exact class of box (Beelink GTR9 Pro, 128GB) [https://github.com/botAGI/strix-halo-llm-benchmarks]: Qwen3.6-35B-A3B Q4_K_M, llama.cpp server — **Vulkan 15.9 ms/token (~63 tok/s) c1 vs ROCm 18.7 ms/token (~53 tok/s) c1**; TTFA 210ms (c1) → 338ms (c4) → 865ms (c8) on Vulkan; 3-hour sustained c4: 100% completion, 1.6% drift. **Vulkan beats ROCm in this config** — supports the 'ROCm spotty' premise; the ROCm backend works but isn't the fast path here.
- TTS-specific: **F5-TTS README documents exactly this gotcha**: 'RDNA 3.5 and RDNA 4 GPUs (gfx1151/gfx1201) require ROCm 7.x; ROCm 6.x causes `HIP error: invalid device function`' (issue #1236) — so PyTorch/CUDA-style engines on this APU need the ROCm 7.x torch build. Kokoro-FastAPI ships an AMD ROCm (experimental, x86_64 only) image and a CPU image; a 3090-class card note: F5-TTS's ROCm 7.x pytorch wheels (torch==2.9.1+rocm7.2) are the documented path. CosyVoice/Chatterbox/XTTS ROCm-on-gfx1151 numbers: not found in sources [searched, not found] — treat as [inference: use ROCm 7.x torch + PyTorch, or fall back to CPU/Vulkan where supported (llama.cpp-omni supports Vulkan)].
- CPU-only paths (all fine on a 395 — 128GB, 32 cores of memory bandwidth): **Kokoro: ~2.4x real-time on CPU, <4GB RAM (ONNX) [pinggy, third-party benchmark]**; **VoxCPM2: runs as GGUF on CPU/Metal/CUDA/Vulkan via llama.cpp-omni with no PyTorch at all** — 'Kokoro has been the only real CPU option in this space for a year; this is the first serious model to join it' (pinggy); F5-TTS/Chatterbox/CosyVoice CPU speed: no published numbers [inference: expect slower than GPU; F5-TTS's PyTorch RTF 0.1467 is GPU-L20, CPU would be worse].
## 5. XTTS v2 in 2026
- **Status:** Coqui the company **shut down January 2024** without relicensing anything [pinggy, cites the shutdown post]. The original coqui-ai/TTS repo is dead (~45k stars, unmaintained); the **idiap/coqui-ai-TTS fork is actively maintained** — 2.3k stars, 5,314 commits on dev, released as PyPI `coqui-tts`, v0.27.0 adds a caching mechanism for cloned voices, v0.20.0 shipped XTTSv2 with 17 languages and <200ms streaming [https://github.com/idiap/coqui-ai-TTS]. Code is MPL-2.0.
- **License (the part that matters):** code is MPL-2.0, but the **weights are under the Coqui Public Model License, 'only non-commercial use of a machine learning model and its outputs'** [coqui-ai/TTS discussions 4145/4304; pinggy; coqui/XTTS-v2 HF LICENSE]. The CPML is non-negotiable and **nobody who can sell a commercial license exists anymore** — Coqui is gone and the cpml URL is a dead 404 [pinggy]. So: fine for Frank's personal homelab agent; not a viable commercial building block (unlike e.g. Chatterbox's MIT or IndexTTS's near-unrestricted Bilibili license).
- **Capability today:** 17 languages; clone from a short clip (~3s per pinggy, ~6s per HF coqui/XTTS-v2 card and localaimaster — sources disagree on the exact figure); <200ms streaming. Quality: **920 Elo**, dead-last on the 2026 leaderboard (vs Step EditX 1,102 Apache-2.0 and Chatterbox 1,020 MIT, both newer and permissively licensed). It's a graveyard-tier model (pinggy: 'the bottom of the leaderboard is a graveyard' — XTTS is listed among four models to skip, alongside dead OpenVoice, StyleTTS2 and MetaVoice). Still, it's the most battle-tested for personal use and LocalAI ships a Coqui backend. Recommendation: pick it only for its 17-language breadth or existing integrations; otherwise Chatterbox (MIT, ~5s clone, 2026 quality) or F5-TTS (5s clone, RTF 0.04, non-commercial OK for personal use) dominate it in 2026.
## Bottom line for a /v1/audio/speech server on a 3090 next to a 14B GGUF
- Personal/cloning quality tier, fits the budget: **Qwen3-TTS 1.7B via vLLM-Omni** (Apache 2.0, 3s clone, native /v1/audio/speech + /v1/audio/voices registry, 97ms streaming, ~6-8GB [inference]) — the best match for an omp harness wanting a `voice` parameter that resolves to a saved clone; or **Chatterbox v3 via travisvn/chatterbox-tts-api** (MIT model, AGPL server, voice library upload, ~1-2GB [inference]). For minimum footprint: **F5-TTS** (5s clone, measured RTF 0.04, ~1-2GB [inference], weights CC-BY-NC = personal-use only) or **CosyVoice 3** (Apache 2.0, 0.5B, 3-10s reference). The top Elo cloners (Breeze/Fish/Voxtral) are non-commercial (OK for personal) but 12-16GB+ and won't coexist with the 14B on a 24GB card.
- On the Strix Halo 395: anything above coexists trivially with 128GB unified; use llama.cpp/Vulkan paths where available (VoxCPM2 GGUF via llama.cpp-omni), ROCm 7.x for PyTorch engines (F5-TTS explicitly requires it on gfx1151; ROCm 6.x crashes), CPU-only fallback = Kokoro (~2.4x RT) or VoxCPM2 GGUF. [inference on CPU speeds for the PyTorch models, no vendor numbers.]
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1792309892756
}
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1792311618707
}
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1792354753052
}
@@ -0,0 +1,2 @@
## File formats
- No .pi extension; use .md or .json for project artifacts
@@ -0,0 +1,2 @@
## Documentation approach
- Never open subagents to look up documentation; read documentation directly
@@ -0,0 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792367076712,
"lastResult": {
"at": 1792366721197,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
Loaded 100 of 173 files, more files were not shown because too many files have changed in this diff. Show more