From ee35d2389e825dc4dc4bb98de14d85d747d74846 Mon Sep 17 00:00:00 2001 From: velixio <270455167+velixio@users.noreply.github.com> Date: Sat, 15 Aug 2026 18:46:36 +0530 Subject: [PATCH] fix: require remote voice render parity --- backend/worker/transport/server.py | 4 ++++ docs/remote-workers.md | 6 ++++-- tests/test_worker_protocol_contract.py | 7 ++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/backend/worker/transport/server.py b/backend/worker/transport/server.py index 4b95cb82..901450b5 100644 --- a/backend/worker/transport/server.py +++ b/backend/worker/transport/server.py @@ -57,6 +57,10 @@ REQUIRED_FEATURES = frozenset({ "task_progress_v1", "task_inputs_v1", "remote_model_download_v1", + # A generic backend.generate() call accepts the same wire shape but drops + # profile conditioning controls. Require the canonical worker render path + # so an older peer cannot successfully return a different voice. + "remote_tts_render_v1", }) diff --git a/docs/remote-workers.md b/docs/remote-workers.md index de198b2a..41795bce 100644 --- a/docs/remote-workers.md +++ b/docs/remote-workers.md @@ -219,10 +219,12 @@ what is genuinely still in flight. **Version or feature mismatch.** The protocol keeps a two-release compatibility window, but release numbers alone do not prove that a worker understands every additive command. Registration therefore also declares named features for task -inputs, progress leases, and remote model downloads. A worker outside the +inputs, progress leases, remote model downloads, and the voice-identity render +pipeline. A worker outside the version window, or one missing a required feature, is refused with `UPGRADE_REQUIRED` and an update instruction before any task runs. It can never -silently render without reference audio or leave a download stuck at 0%. +silently render without reference audio, substitute a different voice, or leave +a download stuck at 0%. Every remote failure includes a concrete next step. Capacity, missing models, expired leases or sessions, authentication, rejected inputs, and result upload diff --git a/tests/test_worker_protocol_contract.py b/tests/test_worker_protocol_contract.py index f418e4c8..841f38df 100644 --- a/tests/test_worker_protocol_contract.py +++ b/tests/test_worker_protocol_contract.py @@ -78,7 +78,7 @@ def test_registration_declares_semantic_features(proto): @pytest.mark.asyncio async def test_old_worker_is_visibly_refused_before_running_wrong_audio(): - """An old peer can share v1's protobuf shape while missing inputs/progress. + """An old peer can share v1's protobuf shape while missing render parity. Registration must fail by name, before authentication or task dispatch, instead of allowing a clone with no reference audio to report SUCCESS. @@ -95,6 +95,11 @@ async def test_old_worker_is_visibly_refused_before_running_wrong_audio(): assert REQUIRED_FEATURES +def test_remote_tts_render_parity_is_a_required_worker_feature(): + """Do not let an old worker silently bypass the canonical TTS pipeline.""" + assert "remote_tts_render_v1" in REQUIRED_FEATURES + + # ── Control / data plane separation ────────────────────────────────────────