diff --git a/backend/charts/backend-listen/prod_omi_backend_listen_values.yaml b/backend/charts/backend-listen/prod_omi_backend_listen_values.yaml index fc19a7c5bdd..f4efc4de6ba 100644 --- a/backend/charts/backend-listen/prod_omi_backend_listen_values.yaml +++ b/backend/charts/backend-listen/prod_omi_backend_listen_values.yaml @@ -130,6 +130,8 @@ env: value: "0" - name: BATCH_PIPELINE_PERCENT value: "0" + - name: SERVER_BATCH_ONLY_MOBILE_PERCENT + value: "0" - name: BATCH_PIPELINE_SERVING_ENABLED value: "false" - name: BATCH_PIPELINE_WORKER_ENABLED diff --git a/backend/docs/feature-flag-registry.md b/backend/docs/feature-flag-registry.md index ee36e129b45..fc607059969 100644 --- a/backend/docs/feature-flag-registry.md +++ b/backend/docs/feature-flag-registry.md @@ -277,7 +277,7 @@ and an explicit empty literal renders as `''`. | `PLAYBACK_ON_DEMAND_ENABLED` | Build playback only on open/play; stable generation/checksum source IDs; existing task/run-lock coalescing | backend | env | closed | {value: 'false', category: rollout} | false (backend-listen (chart), pusher (chart)); {value: 'false', category: rollout} (cloud_run/backend, cloud_run/backend-integration, cloud_run/backend-sync, cloud_run/backend-sync-backfill, gke/backend-listen, gke/pusher) | false (backend-listen (chart), pusher (chart)); {value: 'false', category: rollout} (cloud_run/backend, cloud_run/backend-integration, cloud_run/backend-sync, cloud_run/backend-sync-backfill, gke/backend-listen, gke/pusher) | — | graduate | on | 2026-11-15 | dazheng | | `REPEAT_WORK_REVISION_ENABLED` | Reuse existing sync outbox with 60min ceiling and exact successful snapshot-based notes/L1 dedupe; content-free call/final joins | backend | env | closed | {value: 'false', category: rollout} | false (backend-listen (chart), pusher (chart)); {value: 'false', category: rollout} (cloud_run/backend, cloud_run/backend-integration, cloud_run/backend-sync, cloud_run/backend-sync-backfill, gke/backend-listen, gke/pusher) | false (backend-listen (chart), cloud_run/backend, cloud_run/backend-integration, cloud_run/backend-sync, cloud_run/backend-sync-backfill, pusher (chart)); {value: 'false', category: rollout} (gke/backend-listen, gke/pusher) | — | pending | undecided | 2026-11-04 | dazheng | | `REVIEW_SURFACE_MODE` | Review queue, reversible agent changes and entity pages; off by default, on enables authenticated routes | backend | env | closed | on | on (backend-listen (chart), cloud_run/backend, gke/backend-listen) | on (backend-listen (chart), cloud_run/backend, cloud_run/backend-sync, gke/backend-listen) | — | pending | undecided | 2026-11-07 | dazheng | -| `SERVER_BATCH_ONLY_MOBILE_PERCENT` | Default 0; iOS/Android unsalted owner-hash cohort nested in existing batch admission, with the same custody and language gates | backend | env | closed | — | — | — | — | pending | on | 2026-10-31 | dazheng | +| `SERVER_BATCH_ONLY_MOBILE_PERCENT` | Default 0; iOS/Android unsalted owner-hash cohort nested in existing batch admission, with the same custody and language gates | backend | env | closed | — | — | 0 (backend-listen (chart)) | — | pending | on | 2026-10-31 | dazheng | | `SONIOX_CAPTURE_AXIS_DIAGNOSTICS` | Measure raw Soniox token clocks against wire PCM and the managed send ledger; default off, no placement changes | backend | env | closed | declared (gke/backend-listen, gke/pusher); false (cloud_run/backend, cloud_run/backend-integration, cloud_run/backend-sync, cloud_run/backend-sync-backfill) | false (backend-listen (chart), cloud_run/backend, cloud_run/backend-integration, cloud_run/backend-sync, cloud_run/backend-sync-backfill, gke/backend-listen, gke/pusher, pusher (chart)) | false (cloud_run/backend, cloud_run/backend-integration, cloud_run/backend-sync, cloud_run/backend-sync-backfill); true (backend-listen (chart), gke/backend-listen, gke/pusher, pusher (chart)) | — | pending | undecided | 2026-11-04 | dazheng | | `SONIOX_ELAPSED_AXIS` | Measure Soniox elapsed timestamps before enabling speaker windows | backend | env | closed | — | — | — | — | kill | off | 2026-11-04 | dazheng | | `SONIOX_IDLE_COHORT_CLOSE_SECONDS` | Treatment-arm close seconds for the uid-hash idle cohort; unset/off keeps the global value; floored at 20 s | backend | env | open | — | — | — | — | pending | undecided | 2026-11-21 | backend | @@ -942,7 +942,6 @@ their code default (`fail` tells you which way a missing value resolves). - `SCREEN_TASK_STOP` — Stop screen-task gate and flagged extraction admission; default false; clients poll every 30 seconds with a 55-second lease (fail: inverted) - `SELFHEAL_MODE` — Conversation self-heal sweeper mode: off/detect-only/nudge/heal (fail: closed) - `SERVER_BATCH_ONLY_ENABLED` — Default on; consented supported-language platform sessions reserve durable batch ownership and open no managed live STT (fail: open) -- `SERVER_BATCH_ONLY_MOBILE_PERCENT` — Default 0; iOS/Android unsalted owner-hash cohort nested in existing batch admission, with the same custody and language gates (fail: closed) - `SERVER_BATCH_ONLY_PLATFORMS` — Default macos,windows; comma-separated desktop platforms admitted to server batch-only capture; linux supported explicitly (fail: closed) - `SONIOX_ELAPSED_AXIS` — Measure Soniox elapsed timestamps before enabling speaker windows (fail: closed) - `SONIOX_IDLE_CLOSE_SECONDS` — Default on at 45 s; close paid Soniox transports after continuous active-VAD silence or 60 s with no client frames; 0 or off is the kill switch (fail: open) diff --git a/backend/scripts/verify_pusher_cohost_env_diff.py b/backend/scripts/verify_pusher_cohost_env_diff.py index 70e2b07b38d..c6b00caab6c 100644 --- a/backend/scripts/verify_pusher_cohost_env_diff.py +++ b/backend/scripts/verify_pusher_cohost_env_diff.py @@ -220,6 +220,10 @@ "MOBILE_STANDARD_BATCH_ENABLED", "MOBILE_STANDARD_BATCH_PERCENT", "MOBILE_STANDARD_BATCH_UIDS", + # Only listen reads the mobile server-batch-only cohort: server_batch.selected via + # request_eligible and via eligible <- create_in_progress_conversation, both called only + # from routers/listen. Pusher's finalizer uses owned/has_audio and never admits capture. + "SERVER_BATCH_ONLY_MOBILE_PERCENT", # Managed listen-only STT rollout; pusher is not a live audio receiver. "PARAKEET_BATCH_PRESSURE_MIN_REPLICAS", "PARAKEET_BATCH_PRESSURE_POOL_HOST", diff --git a/backend/tests/unit/test_backend_listen_helm_defaults.py b/backend/tests/unit/test_backend_listen_helm_defaults.py index 7146f3c439d..28112de42dd 100644 --- a/backend/tests/unit/test_backend_listen_helm_defaults.py +++ b/backend/tests/unit/test_backend_listen_helm_defaults.py @@ -97,6 +97,37 @@ def test_backend_listen_helm_template_uses_runtime_project_for_google_cloud_proj assert 'name: TRANSLATION_NEGATIVE_CACHE_TTL\n value: "21600"' in rendered +def test_prod_listen_carries_an_explicit_zero_mobile_batch_only_percent(): + """Ruling 73b217: the mobile batch-only cohort is a reviewed listen value, explicit 0 until a ramp PR changes it. + + The value must reach the rendered container env (not only the values file), so a listen roll applies it. + """ + values = _load_values(ENV_IDENTITY_DEFAULTS["prod"]["values_file"]) + assert _env_value(values, "SERVER_BATCH_ONLY_MOBILE_PERCENT") == "0" + names = [entry.get("name") for entry in values.get("env", []) or [] if isinstance(entry, dict)] + assert names.count("SERVER_BATCH_ONLY_MOBILE_PERCENT") == 1 + + helm = shutil.which("helm") + if helm is None: + pytest.skip("helm is not installed") + rendered = subprocess.run( + [ + helm, + "template", + "backend-listen", + str(CHART_DIR), + "-f", + str(ENV_IDENTITY_DEFAULTS["prod"]["values_file"]), + "--set-string", + "image.tag=abc1234", + ], + check=True, + capture_output=True, + text=True, + ).stdout + assert 'name: SERVER_BATCH_ONLY_MOBILE_PERCENT\n value: "0"' in rendered + + def test_backend_listen_helm_template_requires_image_tag(): helm = shutil.which("helm") if helm is None: diff --git a/config/deployment-setting-classification.json b/config/deployment-setting-classification.json index a2406af3c57..13c001e0b1f 100644 --- a/config/deployment-setting-classification.json +++ b/config/deployment-setting-classification.json @@ -240,6 +240,7 @@ "STT_LIVE_DEMAND_MIN_DWELL_SECONDS", "MOBILE_STANDARD_BATCH_ENABLED", "MOBILE_STANDARD_BATCH_PERCENT", + "SERVER_BATCH_ONLY_MOBILE_PERCENT", "MOBILE_STANDARD_BATCH_UIDS", "OWNER_IDENTITY_RETRY_COALESCE_ENABLED", "SPEAKER_GROUPING_CROSS_SCOPE_THRESHOLD",