Supersedes draft PR #24 (hermes/tts-voice-hfc-female): Brad changed the
decision after that task landed, so this starts fresh from origin/main
instead of building on it.
Bakes three checksum-pinned Piper voices (en_US-amy-medium,
ru_RU-irina-medium, es_MX-claude-high) alongside the existing lessac set,
and adds deterministic, allow-listed language routing to
hermes-jetson-tts-server.py: an explicit request "language" field maps
through a fixed dict to one of the three baked voices, with unknown,
missing, or malformed input always falling back to English amy. A
client-supplied "voice" field is never read, so no client input can reach
a filesystem path. All three voices are eagerly preloaded at process
start (measured ~243MB RSS for three vs. ~88MB for one).
The WebUI has no signal for the language of the text it is about to
speak (verified: hermes-webui-atlas-voice.js sends only text and engine),
so no client- or server-side language detection is added; this gap is
documented in NOTES.md and the PR description rather than papered over.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Document what an operator needs to know about the repairs: why the ordinal
workspace claim is ReadWriteMany and why colocation is only a preference, that
`lease_failed` is retryable rather than terminal and what a persistent deferred
park means, why a retry publishes an attempt-scoped ref and what to expect from
the extra drafts, and why the best-effort pool is deliberately absent from the
hermes Kustomization health checks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three fenced worker Pods claim Hermes Kanban runs through a coordinator that
owns every state transition, with per-ordinal HMAC authority, a mediated
broker-only SCM path, and durable per-ordinal workspaces.
Content is the reviewed head of PR #18 (689bcb6e) with PR 16's and PR 19's
contributions removed: they were merged in only to validate co-existence and are
not prerequisites, so this branch no longer carries them as ancestors. Only PR 14
and PR 15 remain, because the broker boundary and the cli_lane_* decomposition
are load-bearing for two of the fixed P0 boundaries.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>