docs(frank-tts): worklog entry for the voice-stack audit
Three faults, all of the reporting-success-while-doing-nothing species: the build that could not start (no adduser in the base), the warm pass that could not warm (podman's --env-file keeps the quotes), and the image that would never rebuild (cfhash over the Containerfile alone). Records the proofs, the two behaviour seams, and what stays undone: nothing is deployed, #2 phase 1 stays open until 7772 answers.
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user