Motivation
When testing streaming we rarely want to hear the audio. Every client app should have a mute option that defaults to muted, lives in Settings → Advanced, and is settable from every way we configure the app (Settings UI, cold-launch flags, Appium capabilities / config-on-connect, harness per-play app-config, and the characterization matrix knobs).
In scope: iOS/tvOS (apple/) + Android (android/), plus the dashboard/proxy/harness config plumbing.
Out of scope: Roku.
Current state (verified 2026-06-23)
- iOS — mostly built.
isMuted + a "Mute audio" toggle in Settings/Advanced, the is.flag.muted launch flag, UserDefaults persistence, and always-muted preview tiles all already exist. Pointers: apple/InfiniteStreamPlayer/InfiniteStreamPlayer/PlayerViewModel.swift:103,485,732,1337,2274,2308,2372,2445; SettingsOverlay.swift:375. Gaps below.
- Android — not built.
android/ exists but has no app-level mute (no FLAG_MUTED / setVolume / muted). Tiles silence via a no-audio renderer factory (LivePreviewTile.kt). Full feature needed.
- Harness — no
muted. tools/harness-cli/cmd/harness/appconfig.go covers segment/protocol/live-offset/peak-bitrate only.
Requirements
- Mute toggle in Settings → Advanced on iOS and Android.
- Default = muted (true) on both platforms.
- Live toggle (no relaunch) updates the player immediately.
- Settable from all config paths, all converging on the same state:
- Settings UI (persisted)
- Cold-launch flag
-is.flag.muted true|false (NSArgumentDomain / intent extra)
- Appium capability / config-on-connect (
isAudioMuted / app.muted)
- Harness per-play
harness app-config <target> --muted true|false
- Characterization matrix per-arm + sweep knobs
Scope checklist
iOS (apple/InfiniteStreamPlayer/InfiniteStreamPlayer/)
Android (android/InfiniteStreamPlayer/) — build the whole thing, copy the allow4K toggle pattern
Harness + characterization plumbing (tools/harness-cli/, tests/characterization/)
Acceptance criteria
- iOS and Android both launch muted by default, with a working Settings → Advanced toggle that takes effect live.
-is.flag.muted false at launch, harness app-config --muted false, and a char-matrix arm muted: false each produce audible playback on a real run; the inverse mutes.
- Verify from data, not just the UI: confirm on a real play that audio is/ isn't flowing (per the project's "don't trust a screenshot" rule).
Motivation
When testing streaming we rarely want to hear the audio. Every client app should have a mute option that defaults to muted, lives in Settings → Advanced, and is settable from every way we configure the app (Settings UI, cold-launch flags, Appium capabilities / config-on-connect, harness per-play app-config, and the characterization matrix knobs).
In scope: iOS/tvOS (
apple/) + Android (android/), plus the dashboard/proxy/harness config plumbing.Out of scope: Roku.
Current state (verified 2026-06-23)
isMuted+ a "Mute audio" toggle in Settings/Advanced, theis.flag.mutedlaunch flag, UserDefaults persistence, and always-muted preview tiles all already exist. Pointers:apple/InfiniteStreamPlayer/InfiniteStreamPlayer/PlayerViewModel.swift:103,485,732,1337,2274,2308,2372,2445;SettingsOverlay.swift:375. Gaps below.android/exists but has no app-level mute (noFLAG_MUTED/setVolume/muted). Tiles silence via a no-audio renderer factory (LivePreviewTile.kt). Full feature needed.muted.tools/harness-cli/cmd/harness/appconfig.gocovers segment/protocol/live-offset/peak-bitrate only.Requirements
-is.flag.muted true|false(NSArgumentDomain / intent extra)isAudioMuted/app.muted)harness app-config <target> --muted true|falseScope checklist
iOS (
apple/InfiniteStreamPlayer/InfiniteStreamPlayer/)PlayerViewModel.swift:103isMuted = false→true.muted: Bool?toServerAppConfig(struct ~:1073); parse it infetchServerAppConfig(); overlay inapplyServerAppConfig()so harness/config-on-connect per-play push works.isAudioMuted→is.flag.mutedcapability mapping (PlayerViewModel.swift:3912) is complete end to end.Android (
android/InfiniteStreamPlayer/) — build the whole thing, copy theallow4Ktoggle patternUiState.muted: Boolean = true(state/PlayerState.kt).FLAG_MUTED = "advanced_muted"+setMuted()setter that writes SharedPreferences and appliesplayer.volume = if (muted) 0f else 1f(state/PlayerViewModel.kt).LaunchConfig.muted ?: prefs.getBoolean(FLAG_MUTED, true)inloadAdvancedFlags().LaunchConfig.mutedholder + intent-extra capture foris.flag.muted(MainActivity.kt).PickerItemin the Advanced section (ui/screen/SettingsOverlay.kt).muted: Boolean?to AndroidServerAppConfig; parse from/api/sessions; overlay inapplyServerAppConfig().Harness + characterization plumbing (
tools/harness-cli/,tests/characterization/)appconfig.go: add--mutedflag →ac["muted"]inbuildAppConfigBody(+ usage text +appconfig_test.gocase).runner/probe.go:ProbeConfig.Muted+ emit-is.flag.mutedinProbeLaunchArgs(+probe_test.gocase).Mutedon the Arm spec (internal/charmatrix/spec.go);CHAR_ARM_<i>_MUTED+CHAR_SWEEP_MUTEDincmd/harness/char.go; read them inmodes/char_matrix_fleet_test.go.runner/appium.gobaselineTestFlags/modes/runconfig.go) — default-muted may make a forced baseline unnecessary; document the choice.go-proxy/cmd/server/configconnect.go) is generic (app.muted=trueroutes automatically) — no change expected; verify.Acceptance criteria
-is.flag.muted falseat launch,harness app-config --muted false, and a char-matrix armmuted: falseeach produce audible playback on a real run; the inverse mutes.