Memory pressure — 6 live patchů a bathroom audio reset
Druhá půle dne 14. května. Ráno proběhl úklid RPi/HAOS infrastruktury (sandbox 24/7, kiosk Phase E, dashboard cache retry — samostatný post). Odpoledne se ale začalo vrtat do jednoho konkrétního symptomu: koupelna občas zůstala svítit a hrát rádio dlouho po tom, co jsme z ní odešli. Reálně viděný problém, opakující se, ne edge case.
Tady je jak se z toho stalo 6 živých patchů do Homey v jednom dni. Bez chaosu. Sandbox-first, drift check, rollback ready, SHA bit-perfect verify na každém PUT.
Phase R0 — co reálně bolí
Read-only preflight přes sandbox runner + Homey REST + watchdog log + EventLog. Tři číselné nálezy hned dali kontext:
- HomeyScript app stav
running, ale mem oscilovala 99-154 MB přes 24h, threshold 125 MB - 38 restart_triggered ve watchdog logu za 24h. Průměrně jeden restart každých 38 min, ale clusterově každých 5-10 min při high-mem fázích
- HomeyScript cumulative crashed counter 1906
Sandbox runner gate ale ukázal DEPLOY_GATE_OPEN, testy 20/20 PASS.
Sandbox infra OK. Problém byl jinde — v reálné runtime stabilitě Homey.
Phase R1 — root cause classification, ne hádání
R1 prompt měl explicit enum 11 možných root cause tagů
(FLOW_MISSING, WRONG_FLOW_CARD,
STATE_NOT_WRITTEN, SCRIPT_CRASH_INTERRUPTED,
VACANCY_GUARDIAN_NOT_ENFORCING, atd.). Cíl: zařadit do
jedné nebo více kategorií, ne mít fluffy diagnózu.
Diagnostika ukázala:
sh_vacancy_guardian_last_run_ts='2026-05-14T06:25:02Z'— guardian naposledy doběhl před 67 minutamish_audio_current_modestuck na'radio_bathroom'od ranní rutiny (~03:30Z)sh_audio_active_speakerstuck na'Speaker koupelna'- 5 vacancy decisions / 24h v EventLog (očekáváno ~288 = 5min × 24h)
- Motion ages ve vacancy decisions: 13 686 s, 9 456 s, 4 685 s, 4 056 s, 2 286 s — světlo/audio běželo až 3.8 hodiny po odchodu z koupelny
Klasifikace: VACANCY_GUARDIAN_NOT_ENFORCING primary
+ STATE_NOT_RESET secondary (audio mode neresetuje sám sebe).
Phase R1 FLOW VERIFY — eliminace špatné hypotézy
Hypotéza #1 byla: cron Flow je rozbitý. Klasická intuice, často správná. Sandbox-first ale chce empirický důkaz, ne intuici. Pull všech 233 advanced flows přes REST, najít flow co volá guardian skript, inspekce karet:
Flow a996ffa1 — "SH – Vacancy – Guardian Cron 5min"
enabled: true
broken: false
trigger card:
id: homey:manager:cron:every_nth ✅ správný cron format
args: { n: 5, type: "minute" } ✅ správné argumenty (T6+T15 valid)
action card:
id: homey:app:com.athom.homeyscript:run
args.script.id: 718292bb-... ✅ matches sh_room_vacancy_guardian_v1
Flow je strukturálně 100% správně. Eliminované hypotézy:
FLOW_MISSING, FLOW_DISABLED,
FLOW_BROKEN_CARD, FLOW_WRONG_SCRIPT_TARGET.
Ne flow je rozbitý, ale runtime kill mid-flight. Watchdog log to potvrdil: cluster restartů přesně okolo posledního guardian run-u (06:20, 06:25, 06:30 — guardian completed at 06:25, killed cluster at 06:30).
Heartbeat patch — důkaz CRASH_MID_RUN bez hádání
Klíčový problém pre-deploy: měli jsme jen jeden timestamp
(sh_vacancy_guardian_last_run_ts psaný na konci run()).
Když byl stale, nebylo možné rozlišit:
- "cron Flow je rozbitý a vůbec nefire-uje guardian"
- "cron Flow fire-uje, ale guardian se zabije mid-flight"
Patch: přidat druhý timestamp na začátku run() —
sh_vacancy_guardian_heartbeat_ts. Když cron fire-uje a script
alespoň jednou volá setValue, heartbeat se zapíše. Pokud pak
script doběhne, zapíše se i last_run. Pair logika:
both fresh → OK_completing
heartbeat fresh,
last_run stale → CRASH_MID_RUN ← runtime kill během exekuce
both stale → CRON_NOT_FIRING ← cron flow / app dead
mixed → WARN
Sandbox test T26 5/5 PASS. Replay scénář guardian_heartbeat_crash_mid_run.json
simuloval přesnou produkční situaci.
Dvě malé live změny — var a guardian v1.1
Před guardian v1.1 deploye potřeba nová Logic proměnná:
POST /api/manager/logic/variable
body: { "name": "sh_vacancy_guardian_heartbeat_ts",
"type": "string", "value": "" }
→ HTTP 200
→ id: 1514bec1-7a87-4d22-b3c7-16ce185a2e79
Idempotent guard pre-check potvrdil, že var neexistuje. POST. Verify post-GET.
Audit JSON do rollback/var_create_sh_vacancy_guardian_heartbeat_ts.json.
Žádný script ji ještě nepoužíval — bezpečné izolované přidání.
Potom guardian v1.0 → v1.1: 273 řádků → 309 řádků, dvě malé editace —
heartbeat write at top of run() + bathroom-specific stuck-state reset
when vacancy confirmed. Backup pre-deploy live source pulled, SHA porovnáno
s pre-staging backup-em (drift check), PUT, re-GET, SHA bit-perfect verify.
První cron po deployi — sandbox-first vindication
PUT proběhl v 08:01:13Z. Next 5-min cron at 08:05:00Z. Sample at 08:05:29Z:
sh_vacancy_guardian_heartbeat_ts = '2026-05-14T08:05:00.138Z' ← ✅ fresh
sh_vacancy_guardian_last_run_ts = '2026-05-14T07:55:02.351Z' ← ❌ stuck 10 min
CRASH_MID_RUN diagnóza okamžitě prokázána. Heartbeat fire-d (cron OK, script started), last_run NEpsán (script killed mid-flight). Stejný pattern pozorován i v 08:10, 08:15 cron firings.
Tím se hypotéza R1 FLOW VERIFY confirmed: ne flow, ale runtime. Pivot na R2.
Phase R2 — memory pressure audit
R2 read-only audit pull všech 132 scriptů zdroje + cron inventory. Číslo, které zarazí: 1 295.7 cron fires per hour napříč 67 cron flows. 21 fires per minute average. Worst offender:
sh_tts_orchestrator_v1: 240/h = every 15 s (TTS queue processor, fires regardless of queue state)sh_shelly3em_poller_v1: 120/h = every 30 ssh_pracka_done_detector_v1: 60/hsh_morning_kaffe_check_v1: 60/hsh_wake_confirm_v1: 60/hsh_time_sync_guard_v1: 60/hsh_intent_engine_v1: 60/h- … 60 dalších cron flows ve frekvencích 30/h-1/h
Každý fire = fresh V8 sandbox spawn v HomeyScript app. I když je work-load script byl no-op, lib eval + var load + JSON parse zabere 50-300 KB allocations. 1295 fires/h × průměr 200 KB = ~260 MB/h V8 heap churn. GC reclaim některé, ale net mem grows monotonically until threshold 125 MB → watchdog kill.
Quick Win Throttle — 5 patchů s jasným pravidlem
Strategie: minimální možný early-return guard na top of main entry každého
worst offender scriptu. Žádný redesign, žádné funkce přepisované. Single
if (...) return; blok per script:
- TTS Orchestrator: skip if
sh_tts_queueempty ANDsh_tts_queue_triggeridle. Saves ~180/h. - Kaffe Check: skip outside Prague 04:00-11:00 window. Saves ~1 020 fires/day (70 % of daily).
- Wake Confirm: skip if user not sleeping AND hour ≥ 12. Saves ~45/h.
- Wake Scorer: same logic. Saves ~20/h.
- Morning Score: same logic. Saves ~45/h.
Per patch: pull live source, drift check vs pre-staging backup (catch case kdy mezitím někdo edituje skript přes UI), apply minimal patch via Python binary-mode (preserve CRLF), staging file with manifest + SHA256. Sandbox regression 26/26 PASS před každým PUT. Bit-perfect SHA re-GET verify po PUT. Audit JSON. 5 minut work per patch.
Sequential deploy — 21 minut, 5 PUTů, žádný restart
Per SAFE_REPAIR_MODE: jedna změna, ověření, další změna. Timeline (UTC):
08:26:46 PUT sh_tts_orchestrator_v1 v1.1 ✅ SHA bit-perfect
08:43:58 PUT sh_morning_kaffe_check_v1 v1.1 ✅ SHA bit-perfect
08:47:16 PUT sh_wake_confirm_v1 v1.1 ✅ SHA bit-perfect
08:47:27 PUT sh_wake_scorer_v1 v1.1 ✅ SHA bit-perfect
08:47:34 PUT sh_morning_score_v1 v1.1 ✅ SHA bit-perfect
App state běhěm všech 5 PUTů zůstala running. Žádné nové crashy
(crashedCount stable 1920 přes 21 min). Bit-perfect SHA verify na re-GET
každého patche. Backup intact pro per-script rollback (~10s single PUT).
9 minut po prvním deploy — bathroom audio konečně idle
Hlavní win nečekal 24-hodinový soak. Sample 12 minut po TTS deploy:
Pre-deploy 08:26Z:
sh_vacancy_guardian_heartbeat_ts = 08:25:00Z
sh_vacancy_guardian_last_run_ts = 07:55:02Z ← stuck 31 min
Post-deploy 08:40Z (12 min after deploy):
sh_vacancy_guardian_heartbeat_ts = 08:35:00.156Z
sh_vacancy_guardian_last_run_ts = 08:35:08.417Z ← ✅ +8s = COMPLETED
sh_audio_current_mode = 'idle' ← ✅ reset from 'radio_bathroom'
První successful guardian run za 40+ minut. Stuck-audio-reset patch v1.1
se v té iteraci dostal k kódu a vyresetoval sh_audio_current_mode
na 'idle'. Reálně viditelná oprava primárního user-reported
problému — bathroom radio už nebude resume po TTS notifikaci, protože
mode už není pinned na radio_bathroom.
Co tenhle den NEbyl
- Nebyl to flow edit. Sandbox-first eliminace flow problému uvolnila ruce pro správnou opravu (script-level throttle + heartbeat).
- Nebyl to chaos. Šest live změn během dne, ale každá byla single PUT s pre-flight + drift check + audit + verify.
- Nebyl to PC export. Phase E kiosk byl rebuilt fresh z RPi/SSH bez
jakéhokoli čtení z
C:\. Heartbeat var + guardian v1.1 + 5 throttle patches všechny vznikly na HAOS staging dir, nikoli na PC. - Není to hotovo. Watchdog restart rate ještě plně nepoklesl při baseline monitoringu (+30 min mark). Throttle savings akumulují pomalu. Doporučený 2-6h soak před dalším rozhodnutím (Shelly poller offload, log buffer, atd.).
Pět hard rules co tohle všechno drží
| Rule | Jak se v R2 projevila |
|---|---|
| SANDBOX-FIRST | 26 sandbox testů + 19 replay scenarios PASS před každým PUT |
| AUTO-REGISTRY | Nový var sh_vacancy_guardian_heartbeat_ts přidán s audit JSON; sandbox runner registry baseline preserved |
| SAFE_REPAIR_MODE | Per-script live backup pre-PUT, SHA drift check, bit-perfect verify, rollback dokumentován jako single PUT ~10s |
| REUSE FIRST | Žádný nový HomeyScript. 6 patchů = update existing scripts. Sandbox tests T22-T26 vznikly v existing framework. |
| RPi/HAOS infra rule | Vše pulled přes Homey REST + HAOS sandbox staging dir. Žádný PC read v R2. |
Co bude soak ukázat
Sandbox runner běží každou hodinu jako 24/7 brána. Pair-logic diagnóza
sh_vacancy_guardian_heartbeat_ts vs sh_vacancy_guardian_last_run_ts
poskytne kontinuální čítač:
- OK_completing rate (kolik z 288 fires/day skutečně dokončí)
- CRASH_MID_RUN rate (kolik startů killed)
- Watchdog restart trend per hour
- Bathroom audio_mode reset frequency (kolikrát guardian executes patch B)
Pokud po 2-6 hodinách restart rate spadne pod 20/h, R2 Quick Win considered success. Pokud zůstane 20-30/h, další kandidát je Shelly poller offload na RPi Python (eliminuje 120/h fires). Pokud je stále >30/h, deeper problém — log buffer offload, cache patches, možná HomeyScript app version upgrade.
Takeaway
Šest živých zásahů do produkčního Homey v jednom dni je hodně. Bez sandbox-first by to bylo riskantní. S 26 sandbox testy + 19 replay scenarios + per-script SHA verify + audit JSON za každý PUT, ale rychlost neznamená chaos.
A reálný uživatelský win — bathroom radio už nebude resume z prázdné koupelny — byl měřitelný 9 minut po prvním deploy. Ne za týden. Ne s "ono se to časem srovná". Měřitelný timestamp, prokazatelná oprava, sandbox replay scenario, který tu situaci reprodukoval předem.