PABLO.ENGINEERING
// WRÓĆ DO WSZYSTKICH WPISÓW
// 3 min

Canary i storm guard: cooldown per problem i per sesja

Dzień upłynął na porządkowaniu kanału alertów i domykaniu drobnych prac wokół agentów. Główny wysiłek poszedł w to, żeby alarm mówił tylko wtedy, gdy ma coś do powiedzenia.

Problem: alarm, który krzyczał bez powodu

Canary wysyłał 49 i więcej identycznych wiadomości "canary OK" co 5 minut, a wcześniej serie po 102, 94 i 30 wiadomości. Root cause był arytmetyczny: 24-godzinny licznik czasu ignorował własny znacznik czasu, więc sprawdzenie "ostatniej prawdziwej wiadomości" odpalało się przy każdym uruchomieniu po dniu ciszy. Drugi problem siedział w outbound_storm_guard.py, gdzie zmieniająca się liczba wiadomości omijała cooldown, a jeden globalny timer wyciszał niezwiązane ze sobą sesje.

Co zrobiłem:

  • W health_monitor.py canary liczy teraz od max(lastMessageAt, canaryAt), a cooldown trzymam w nowym polu lastAlertedAt, którego wynik OK już nie czyści. Commit f0889b5 poszedł przez autodeploy, 33/33 testy regresyjne zielone.
  • Przed wdrożeniem kodu zrobiłem awaryjne ręczne zatrzymanie: podniosłem lastMessageAt w state.json i canary zatrzymał się o 13:55.
  • W outbound_storm_guard.py dodałem cooldown per klucz (agent|session), zapis stanu tylko po udanej wysyłce, flock plus busy_timeout, próg duplikatów 3, filtrowane zapytanie z row_number() i SEQ_LIMIT oraz parsowanie JSON przez json.loads. Commit bc41094, 11/11 testów zielonych. Usunąłem sześć defektów.
  • Zostawiłem last_alert_at i jego sygnaturę obok nowej mapy cooldownów, bo tygodniowy raporter czyta state.json["last_alert_at"]. Odświeżyłem też self.sha256 bez zmiany mtime pliku, bo okno raportowania tygodniowego jest sterowane przez mtime.
  • Leaf-scanner: dzienny skan 126 plików (aneta), 124 (gym) i 143 (spanish) zgłosił 1 marker instrukcji między agentami do ręcznego przeglądu. Chodzi o plik pamięci .../workspace-gym/memory/dreaming/light/2026-09-30.md, który kazał agentowi czytać profil innego agenta.
  • Drobiazgi: zalogowałem 5 błędów runtime gatewaya, głównie chybienia narzędzi (edycja bez zgodnego tekstu w pliku testowym, odczyt wycelowany w katalog, operacja odrzucona bez jawnego właściciela agenta). Memory-verifier wykonał 12 sprawdzeń między 19:00 a 21:45, po 6 rekordów każde, zero napraw. Dsml-guardian wykonał 12 sprawdzeń co 5 minut od 20:55 do 21:50, bez sygnałów. Quiz-watchdog zakończył przebieg w 8 sekund i słusznie milczał w dniu bez aktywności słownikowej.

Dlaczego to ważne (Use Case):

Kanał alarmowy budzi zaufanie tylko wtedy, gdy milczy, kiedy nic się nie dzieje. Bez tego prawdziwy incydent tonie w powodzi identycznych powiadomień. Cooldown per klucz (agent|session) sprawia, że jeden hałaśliwy agent nie wycisza pozostałych sesji, a kanał nadal działa tam, gdzie trzeba. Osobno: automatyczna kontrola granic między agentami wyłapała instrukcję, która mogła wyprowadzić prywatne notatki jednego asystenta do drugiego.

Czego się nauczyłem:

  • Status "OK" nie powinien czyścić cooldownu, który blokuje następny alarm. Inaczej powtarzalny problem generuje nowe ostrzeżenie przy każdym powrocie.
  • Jeden globalny licznik czasu nie wystarcza. Zmieniająca się liczba wiadomości omijała cooldown, a wspólny timer wyciszał niezwiązane sesje. Klucz (agent|session) rozdziela te przypadki.
  • Zapis stanu tylko po udanej wysyłce: nieudana wysyłka nie może kasować cooldownu, bo wtedy kolejna próba startuje z zerowym licznikiem.
#daily#openclaw#reliability#observability