diff --git a/serverAppConfigs-docs/log.md b/serverAppConfigs-docs/log.md index 7fb0b18..ca326b5 100644 --- a/serverAppConfigs-docs/log.md +++ b/serverAppConfigs-docs/log.md @@ -2589,3 +2589,57 @@ every restart since the cutover. three sources still live on the workstation and the engine GGUFs have not been fetched to `/home/dank/models/Qwen3-TTS-GGUF` yet, so the first real run is a host step. + +## 2026-10-18 — voice stack audit: three faults that no gate could see + +- The voice stack had never been run end to end — no image built, nothing on + 7772 — so the whole path was audited as static code against the tools it + calls, and three faults came out of it. All three are the same species: a + step that **reports success while doing nothing**. +- **The build would not have happened.** `Containerfile.engine` created its + unprivileged user with `adduser`; the pinned ubuntu:24.04 base has no + `adduser` at all. Proven in the base on Dankclaw rather than argued from + memory: `command -v adduser` prints nothing, and the replacement line + `useradd --system --uid 10000 --home-dir /app --shell /usr/sbin/nologin tts` + answers `uid=10000(tts) gid=999(tts)`. Its `SYS_UID_MAX` warning is cosmetic + and is the price of matching the façade's uid, so the comment says that + before someone "fixes" it by dropping `--system`. +- **The warm pass would not have warmed.** The unit hands its env file to podman + via `--env-file`, which performs no shell parsing and takes the literal + remainder of the line, while `source` strips one quoting layer. `WARM_TEXT` + therefore reached the container with its quotes, the hook interpolated them + into the synthesis body, the JSON was invalid, and every voice would log + "registered, but the warm synthesis failed" beside an `active` unit. Fixed in + the hook — strip exactly one matched pair, only when the inner content also + carries a quote or backslash, then JSON-escape — and the seam now reads + `scripts/frank-tts-engine.env` **both** ways and asserts the engine gets the + same sentence in both shapes. The bug survived the first deploy precisely + because every existing test used `source`. +- **A rebuilt image would not have been rebuilt.** `build_compose_image` skips + `podman build` on a matching `cfhash` label, and that hash covered the + `Containerfile` alone — so editing `app.py` or `tts-engine-warm.sh` (both + `COPY`'d) skipped the build, the deploy reported success, `/health` answered, + and the container served old code. Each case arm now names the files its + `Containerfile` copies and hashes them with it; comfyui/loki/alloy have no + extras, so their labels are byte-unchanged and they do not rebuild here. +- Tests as behaviour, not as greps: the new seam extracts the real + `build_compose_image` from the deployer and drives it against a stub `podman` + that replays per-image label state, asserting the observable question — did + the deploy ask podman to build, or did it skip? — through build, skip, + engine-hook-edit-rebuilds-engine-only, and `app.py`-rebuilds-the-façade. + Reverting `scripts/deploy-remote.sh` turns it red at the warm-hook step, which + is the fault being prevented. Suite green: **443/443** (`./tests/run.sh`, + wall 110 s); each fix commit was left green on its own so the branch bisects. +- Docs carry the lessons where an operator will hit them: a **Locally built + images** section in [[deployment]], two new troubleshooting lines in [[Frank + TTS Engine]] (all-voices-cold = quoted `WARM_TEXT`; façade 502 beside engine + 200 = model loaded ≠ voices registered), and `Voice Packs.md` step 4 + corrected — the warm hook re-registers **every** pack on every run, because + the engine replaces a name it already holds (`tools/tts-server.cpp:148` at + the pinned commit: *"Re-registering a name replaces the previous entry."*), + so a restart is what an engine bump needs and not what a rebuilt pack needs. +- Opened as PR **#14** against `main`. Deliberately NOT done: no deploy (the + first `--service frank-tts-engine` run is a ~10-minute recursive source clone + and a new unit on a host that serves people — Frank's call), no voice pack + built on the host, no Playwright run (this branch publishes no host port and + touches no vhost), and Gitea **#2** phase 1 stays open until 7772 answers.