allarmi: il marcatore "gia' detto" si scrive DOPO l'invio, non prima
Debito §5.2 chiuso su decisione dell'operatore. `run_once` salvava lo stato coi marcatori `alerted` gia' a True e l'invio lo faceva il chiamante DOPO: col 6,9% di invii falliti misurato (2 su 29), un 🚨 perso restava perso per l'EPISODIO INTERO — l'ora dopo lo stato diceva "gia' detto" e usciva WATCH/MUTO. Gli episodi storici durano 200-2.324 ore, quindi il buco non era teorico. - `run_once(state_path, sender=None)`: il sender e' INIETTATO, non importato — e' cio' che tiene la funzione testabile senza rete d'uscita, che era la ragione del disegno precedente. Senza sender il comportamento resta quello di prima e il report lo DICE (`invio`), invece di lasciar credere che qualcosa sia partito. - Su invio fallito si disfano SOLO i marcatori "gia' detto", non le misure: · asset in ALERT -> alerted=False, l'ora dopo ri-allerta; · lock MAINT/ALERT -> alerted_soft/hard=False ma le ORE restano a correre, cosi' una manutenzione che sfora la grazia sale ad ALERT anche col trasporto giu' (disfare anche le ore congelerebbe l'escalation proprio mentre non si riesce a parlare); · lock RIENTRATO -> si ripristina l'intero LockState, perche' il rientro si annuncia una volta sola e senza le ore non ci sarebbe piu' niente da dire. - `notify(..., tentativi=)`: il retry esisteva in `send` e non arrivava qui. venue_watch ora manda con 3 tentativi. - L'esito dell'invio finisce nel log del cron invece di sparire. 5 test nuovi. Il primo e' quello che conta — dopo un invio fallito, l'ora dopo ri-allerta — col suo controllo positivo (un invio riuscito consuma l'allarme UNA volta sola), senza il quale "ri-allerta sempre" passerebbe. Suite: 782 passati, 2 falliti (i due del gate GTAA, non toccati qui). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+29
-15
@@ -16,14 +16,37 @@ ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live.deribit import check_specs # noqa: E402
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
from src.live.notifier import notify, ultimo_errore # noqa: E402
|
||||
from src.live.venue_watch import (PERSIST_HOURS, THRESHOLD_BPS, # noqa: E402
|
||||
run_once)
|
||||
|
||||
|
||||
def _titolo(rep: dict) -> str:
|
||||
"""⚠️ Il titolo dice cosa FARE, non solo cosa e' successo: chi lo legge sul telefono deve
|
||||
sapere il primo passo senza aprire il repo (runbook completo in src/live/venue_watch.py).
|
||||
⚠️ Dal 19/08 DIPENDE dalla gravita'. Prima era sempre il 🚨 col prelievo di prova, anche per
|
||||
un blocco da manutenzione annunciata: il 18/08 sono usciti quattro messaggi identici che
|
||||
chiedevano di prelevare, tutti dopo che il book aveva gia' ripreso. Un allarme massimo speso
|
||||
per un evento atteso e' un allarme che non verra' letto il giorno che e' vero."""
|
||||
if rep.get("severity") == "warn":
|
||||
return "⚠️ VENUE WATCH — Deribit non operativa, ma spiegata. Nessuna azione"
|
||||
return "🚨 VENUE WATCH — possibile stress su Deribit. PRIMO PASSO: prelievo di prova"
|
||||
|
||||
|
||||
def _manda(rep: dict) -> tuple[bool, str]:
|
||||
"""Il sender iniettato in `run_once`: e' lui a decidere se i marcatori 'gia' detto' si
|
||||
scrivono. `tentativi=3` perche' su questo percorso il 6,9% degli invii si perde, e qui
|
||||
perderne uno significa perdere l'episodio intero."""
|
||||
ok = notify(_titolo(rep), {f"alert {i+1}": a for i, a in enumerate(rep["alerts"])},
|
||||
tentativi=3)
|
||||
if ok:
|
||||
return True, "ok"
|
||||
return False, (ultimo_errore() or "motivo non registrato")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
quiet = "--quiet" in sys.argv
|
||||
rep = run_once()
|
||||
rep = run_once(sender=_manda)
|
||||
|
||||
if not quiet:
|
||||
print("=" * 78)
|
||||
@@ -65,21 +88,12 @@ def main() -> int:
|
||||
print(f" SPEC: {k}: {v}")
|
||||
|
||||
if rep["alerts"]:
|
||||
# ⚠️ Il titolo dice cosa FARE, non solo cosa e' successo: chi lo legge sul telefono deve
|
||||
# sapere il primo passo senza aprire il repo (runbook completo in src/live/venue_watch.py).
|
||||
# ⚠️ Dal 19/08 il titolo DIPENDE dalla gravita'. Prima era sempre il 🚨 col prelievo di
|
||||
# prova, anche per un blocco da manutenzione annunciata: il 18/08 sono usciti quattro
|
||||
# messaggi identici che chiedevano di prelevare, tutti dopo che il book aveva gia'
|
||||
# ripreso. Un allarme massimo speso per un evento atteso e' un allarme che non verra'
|
||||
# letto il giorno che e' vero — ed e' l'unico giorno che conta.
|
||||
if rep.get("severity") == "warn":
|
||||
titolo = "⚠️ VENUE WATCH — Deribit non operativa, ma spiegata. Nessuna azione"
|
||||
else:
|
||||
titolo = ("🚨 VENUE WATCH — possibile stress su Deribit. "
|
||||
"PRIMO PASSO: prelievo di prova")
|
||||
notify(titolo, {f"alert {i+1}": a for i, a in enumerate(rep["alerts"])})
|
||||
# L'invio l'ha gia' fatto `run_once` attraverso `_manda`: qui si stampa e si riporta
|
||||
# l'ESITO. Un invio fallito si vede nel log del cron invece di sparire — e lo stato su
|
||||
# disco e' stato disfatto, quindi l'ora prossima ci riprova da solo.
|
||||
for a in rep["alerts"]:
|
||||
print(f" ALERT: {a}")
|
||||
print(f" telegram: {rep.get('invio')}")
|
||||
return 2 if rep.get("severity") == "alert" else 0
|
||||
return 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user