Bathroom misroute — 4 hodiny diagnostiky a fix v1.8
Včera večer ve 20:45 systém sám spustil radio do koupelny, kde nikdo nebyl. Žádný explicit příkaz, žádná scéna, žádný cube tap. Jen 46 sekund po tom, co AI coordinator nabídl „relax scénu", začalo z prázdné koupelny hrát Italian Classic Hits Sanremo.
Bug se zaregistroval jako medium severity (one-shot, dá se zastavit ručně), ale klíčové bylo, že se to mohlo opakovat při každém comfort_suggest. Tady je 4 hodiny diagnostiky shrnutých do 8 minut čtení.
Reprodukce
- AI coordinator vyhodnotí situation =
comfort_suggest(level 4, evening + idle scene) - TTS přečte: "Luďku, je večer a nic neběží. Chceš spustit relax scénu?"
- User neodpoví — ani cube, ani button, ani voice
- ~46 s po TTS completed:
sh_audio_brain_v2 audio_dispatchevent - Speaker koupelna začne hrát radio (i když user není v koupelně)
State při bugu
sh_audio_current_mode = 'radio_bathroom'
sh_audio_active_speaker = 'Speaker koupelna'
sh_audio_playing = 'yes'
sh_audio_request = 'idle' ← nikdo nepožádal o radio
sh_audio_final_request = 'idle' ← ditto
sh_scene_active = 'idle' ← scéna se neaktivovala
sh_user_intent = 'idle' ← user neodpověděl
sh_user_intent_request = 'idle' ← ditto
sh_ai_coord_last_action = 'comfort_suggest' ← byla jen NABÍDKA
sh_audio_prev_mode = 'idle' ← žádné prev radio (nebylo co resumovat)
sh_audio_prev_speaker = '' ← ditto
sh_tts_resume_pending = 'idle' ← resume nečeká
sh_tts_resume_target = 'idle' ← ditto
Klíčový červený alarm: sh_audio_current_mode='radio_bathroom'
ale sh_audio_prev_mode='idle'. Mode se zaseknul z ranní rutiny,
nikdo ho neresetoval, a po TTS completion fallback logika ho použila jako
„kam se vrátit".
Hypotézy které neplatily
H1 — audio_brain auto-execute na comfort_suggest.
Hypotéza: brain možná auto-aktivuje "comfort scene" 30–60 s po suggestion bez
čekání na user response. Ověřeno čtením sh_audio_brain_v2 —
žádný takový handler tam není. Falsified.
H3 — sh_audio_active_speaker zaseklý z morning.
Hypotéza: z ranní rutiny zůstal active_speaker='Speaker koupelna',
audio_brain dispatch routes podle něj. Částečně true (active_speaker BYL zaseklý),
ale dispatch nebyl problém — problém byl ve fallback logice resume_exec.
H4 — flow listener na ai_coord_last_action.
Hypotéza: někde flow má trigger „sh_ai_coord_last_action changed +
condition comfort_suggest → action play radio". Grep across všech
flows nenašel žádný takový. Falsified.
Root cause (H2 winner)
sh_tts_resume_exec_v1 (id 009149d9-0e49-4881-b41d-502b52ca652c),
který se volá automaticky po každém TTS completion, měl fallback handler:
// BEFORE v1.8
if (target === 'idle' && currentMode === 'radio_bathroom') {
// Resume bathroom radio
await dispatchAudio({mode: 'radio_bathroom', speaker: 'Speaker koupelna'});
}
Logika byla: „pokud TTS skončil, žádný explicit resume target nebyl, a current mode říká bathroom radio, vrať se k bathroom radio". Záměr: po krátké TTS notifikaci během sprchy resumovat radio, který hrál.
Co fallback nekontroloval: jestli to bathroom radio reálně
hrálo PŘED TTS. sh_audio_current_mode se zaseknulo na
'radio_bathroom' z ranní rutiny v 03:35 a nikdo ho neresetoval.
V 20:45 ten stav stále platil, i když speaker dávno mlčel.
Fix v1.8 — prev_playing guard
// AFTER v1.8
if (target === 'idle' && currentMode === 'radio_bathroom') {
if (sh_audio_prev_playing !== 'yes') {
logEvent({event: 'tts_resume_skip_no_prev_playing'});
return;
}
// Resume bathroom radio
await dispatchAudio({mode: 'radio_bathroom', speaker: 'Speaker koupelna'});
}
Podmínka navíc: sh_audio_prev_playing === 'yes'. Tato proměnná
se nastavuje na 'yes' jen když TTS orchestrator detekuje aktivní
audio playback PŘED svým spuštěním. Pokud nic nehrálo (jen TTS), prev_playing
zůstává 'no' a resume se neprovede.
Test (25.4. noc)
Po deploy v1.8 jsem manuálně reprodukoval podmínky bugu:
- Reset všech audio state vars do clean
- Setup:
sh_audio_current_mode='radio_bathroom',sh_audio_prev_playing='no'(klíčový rozdíl) - Trigger TTS přes
sh_tts_orchestrator_v1 - Wait 60 s
- Check:
sh_audio_requeststále 'idle', žádný dispatch event
Test passed. Resume_exec correctly skipped, no radio fired. Log entry
tts_resume_skip_no_prev_playing zaznamenán.
Long-term watch
Fix v1.8 řeší symptom, ne příčinu zaseklého sh_audio_current_mode.
Pokud znovu uvidíme stuck mode, bude potřeba přidat reset do bathroom radio
stop logic — když speaker stop, set mode='idle'. Aktuálně mode drží svou
poslední hodnotu, což je v zásadě dirty state.
Backlog item: tools/audit_audio_state_consistency.py — periodický
check, jestli current_mode odpovídá reálnému speaker_playing
stavu. Brain Guardian extension.
Co se naučilo
Fallback logika musí explicitně ověřit, že podmínka, na kterou fallback reaguje, je aktuální, ne jen recent. State proměnné se umí zaseknout v ghost hodnotách, které vypadají platně, ale neodpovídají reálnému světu.
Tohle není první ghost state bug. Stuck sh_priority_active='tts'
incident byl podobný — proměnná držela hodnotu z incomplete pipeline a
blokovala následující operations. Brain Guardian validator vrstva (V1 LIVE
od 26.4.) je odpovědí na celou tuhle kategorii bugů — periodický cross-check
state vs. reality.
4 hodiny diagnostiky → 4 řádky kódu. Typický poměr u state-driven bugů. Hypothesis ladder (H1 → H2 → H3 → H4) je nezbytný — bez systematické eliminace bych skončil u quick fix který by jen maskoval symptom.