From 96b7687b347e8286300f672df86df2b98e0de15b Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 19:32:03 -0700 Subject: [PATCH] docs(frank-tts): the contract table gains its Default voice row #18 shipped DEFAULT_VOICE and #17 explained why the harness cannot name a voice from a setting, but the endpoint page still described voice resolution as if every request carried a name. One contract row, one paragraph in Naming a voice (a call that names nothing is the harness default shape, not a client bug), the env-file row now lists the default voice among its knobs, and the worklog records the rollout: both merges, the one-dispatch deploy with the 33/33 gate, the card flipping to up on the next checker tick, and the previously failing call returning 430124 bytes with the journal reading "speech voice=narrator1 (default)". --- serverAppConfigs-docs/Dankclaw/Frank TTS.md | 7 +++++-- serverAppConfigs-docs/log.md | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/serverAppConfigs-docs/Dankclaw/Frank TTS.md b/serverAppConfigs-docs/Dankclaw/Frank TTS.md index 9bba640..83416e8 100644 --- a/serverAppConfigs-docs/Dankclaw/Frank TTS.md +++ b/serverAppConfigs-docs/Dankclaw/Frank TTS.md @@ -35,6 +35,7 @@ render needs the app installed, not this endpoint. | Networks | `dankclaw_default` first (it owns the default route and the consumers), then `frank-tts-engine.network` (10.89.5.0/24, the bridge to the engine) | | Voice packs | `/home/dank/voices` mounted read-only at `/voices` — host state, never repo state | | Upstream | `http://frank-tts-engine:8880` (`scripts/frank-tts.env`, `TTS_UPSTREAM`) | +| Default voice | `DEFAULT_VOICE` (`scripts/frank-tts.env`) answers a request that names none; the journal marks that line `(default)`, and an **unknown** name is still a 400 | | Rollback | `TTS_BACKEND=llama-tts-cli` runs a CLI binary per request and needs no upstream at all | ## Naming a voice @@ -42,7 +43,9 @@ render needs the app installed, not this endpoint. A voice **is** a directory name. `voice=narrator1` resolves to `/voices/narrator1/reference.wav`, and that file — not a manifest, not a config entry — is what makes the name real. omp's TTS device may send `voice_id`; the -shim accepts either. +shim accepts either. A call that sends **neither** — the harness's normal shape, +because no harness setting carries a voice for this transport — speaks +`$DEFAULT_VOICE`; empty default makes it a 400 again. The usable shape is `^[a-z0-9][a-z0-9._-]*$`: lowercase, starting with a letter or digit, then letters, digits, `.`, `_`, `-`, at most 64 characters. No slash, @@ -114,7 +117,7 @@ shows up as a failed synthesis, never as a down probe. | `dankclaw/voiceservice/Containerfile` | python 3.12-alpine pinned by digest + ffmpeg, uid 10000 | | `dankclaw/voiceservice/quadlet/frank-tts.container` | the unit: two networks, `Restart=always`, `HealthCmd`, `[Install]` | | `dankclaw/voiceservice/quadlet/frank-tts-engine.network` | the shared bridge, named after the engine unit and attached by both | -| `scripts/frank-tts.env` | backend, upstream, port, limits — paths and numbers, no credentials | +| `scripts/frank-tts.env` | backend, upstream, port, limits, and the default voice — paths and numbers, no credentials | | `tests/frank_tts_unit.bats` | the shim against stub engines over real sockets | | `tests/frank_tts_engine.bats` | the pair's wiring: generator output, planner, homepage, deployer | diff --git a/serverAppConfigs-docs/log.md b/serverAppConfigs-docs/log.md index a7ff446..8220363 100644 --- a/serverAppConfigs-docs/log.md +++ b/serverAppConfigs-docs/log.md @@ -2734,3 +2734,23 @@ every restart since the cutover. to Kokoro is one key, not two, and `speech.voice: narrator1` — briefly recorded above as wiring — went back to `am_fenrir`: that key is the vocalizer's Kokoro enum, so a pack name there breaks speak-aloud and does nothing for synthesis. + +## 2026-10-18 — the default voice shipped: a harness voiceover needs no voice + +- Merged **#17** (`b590695`) and **#18** (`7889a68`), then deployed both affected + services in one dispatch — `--service frank-tts --service homepage`, 159 s, + Playwright gate 33/33. The shim image rebuilt with the new handler; the + homepage checker took the card on its next timer tick: `frank-tts → up`, + probed for real rather than gated. +- The call that had failed is the one that proves the fix. Same device, same + restarted session, **no** `voice_id`: `Saved 430124 bytes (model=frank-tts + (dankclaw)/frank-tts, voice=default, codec=wav)`. The façade journal says what + actually spoke — `speech voice=narrator1 (default) format=wav chars=148 -> + 430124 bytes` — with the engine's ledger behind it (`audio 8.96 s, RTF 0.551`). + The harness prints `voice=default` because it never had a name to print; the + voice that answered is the operator's line in `scripts/frank-tts.env`, and the + journal keeps them distinguishable forever after. +- The page gained a **Default voice** row in its contract table and + [[#Naming a voice]] now states that a call naming nothing is the harness's + normal shape, not a client bug — the misconception that produced the 400 in + the first place.