Hands-free voice mode had no language signal at all, so every spoken reply was
synthesized with the English voice no matter what the user actually said. The
multilingual Piper work (PR #26) added server-side routing for a "language"
field but nothing ever sent one.
Carry the language the private Jetson Whisper service already detects through
to the TTS request for the reply that speech produced, and only for that reply.
hermes-stt returns {text, model, language}, accepted only as a bare ISO-639
token; hermes_stt_client.py writes a <stem>.language sidecar next to the .txt
transcript Hermes reads, leaving the local-command contract intact; the
patched local-command envelope and /api/transcribe re-validate it and surface
it; atlas-voice.js binds it to the voice-mode generation token and chat
session, consumes it exactly once, and clears it on cancellation, restart,
session change, empty transcript or transcription error; /api/tts honours it
only from the fixed en/ru/es allow-list and otherwise sends English.
A client "voice" field is never read at any hop, and typed messages, the manual
read-aloud button, and any reply not produced by a spoken turn carry no trusted
signal and stay on the English voice.
The two WebUI-side and one agent-side edits are fail-closed replace_exact
patches; both patch roots are now env-overridable so the contract can be
verified offline without a GPU or an image build.
Repairs the blockers from the independent review of the previous head. The
role-aware verdict contract was correct but reachable only through one call
site and only on cards written with real newlines, so most real review cards
never used it.
* The role-blind call is no longer a weaker classifier that can reject before
the role-aware judge runs. Without a card there is no defensible
role-dependent judgement, so `unfinished_result_reason()` applies only the
card-independent checks. That closes the short-circuit at every call site,
including the one PR15 moves to `cli_lane_execution`, and it makes a
journalled terminal record accepted under one version of these semantics
re-validate under any other instead of being quarantined into a re-dispatch
of an already-accepted task.
* The lane resolves the role from the card and passes it, and records it in
the Kanban metadata. The verdict contract binds only where the lane can buy
another turn: in single-shot mode a rejection discards the worker's real
result, so review cards keep the relaxation without gaining any rejection
single-shot mode did not already have.
* Card scope expands the literal \n escapes the board stores in one-line
bodies, so explicit `Hermes-Task-Role` / `Hermes-Expected-Output` directives
are honoured on the 6 of 78 live cards that carry no real newline, and the
read-only, verdict and mutation heuristics stop being cut apart by them.
* Card scope now ends at the first non-card H2 and at the runner's controller
evidence, which is emitted under its own heading. Goal-controller rejection
history can no longer sit inside the card, and an upstream heading rename
fails closed instead of admitting history into role resolution.
* The inference recognises the SHIP/BLOCK-shaped deliverables real cards
actually use: 21 of 78 live cards resolve to review, up from 10, with no
implementation card misclassified. Cards asking for a findings list rather
than a verdict deliberately stay on the model judge, since the verdict is
the review contract's only gate.
* A declared review role no longer outranks mutation evidence: a report that
changed files falls back to the implementation regime.
* Judge reasons go through the agent runtime's canonical redactor, extended
for the two shapes it deliberately passes through and this lane handles -
`scheme://user:secret@host` and a credential named in prose - while a 40-hex
commit SHA survives as evidence.
Regressions cover the recovered t_dbdcd739 incident, a verbatim snapshot of
every live card on three boards with a hand-labelled expected role, the
upgrade and single-shot properties against the previous gate, the upstream
context-heading contract, and the end-to-end `execute_claim` shape that used
to burn every goal turn.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>