SPB Git forge

spb/ka-guardian

Public
26commits 1branches 0releases
4.5 MBsize
maindefault branch
19 days agolast push
Python 57.1% Shell 13.8% CSS 12% JavaScript 10% HTML 7.2%

fix(runner): watchdog de timeout indépendant du flux stdout

Le contrôle du deadline vivait dans la boucle for line in proc.stdout : il ne
s exécutait qu à l arrivée d une ligne. Un claude qui se tait (outil qui pend)
ou qui n exit jamais après son résultat laissait la mission « running » pour
toujours et bloquait le nœud (mission f9eb9d1b59c8 du 2026-08-24 : travail fini
à 10:38, processus zombie tué à la main à 15:46). Un thread watchdog tue
désormais le processus au deadline quoi qu il arrive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 1 mo ago (Aug 24, 2026) parent 33c1c36

1 changed file +15 −0

modified runner/runner.py +15 −0
@@ -234,6 +234,21 @@ def run_mission(m: MissionIn) -> None:
234 234 result_text, cost, turns = "", None, None
235 235 spent_estimate = 0.0
236 236 deadline = time.time() + m.timeout_seconds
237 +
238 + def _watchdog() -> None:
239 + # Filet indépendant du flux stdout : le contrôle du deadline dans
240 + # la boucle de lecture ne s'exécute qu'à l'arrivée d'une ligne —
241 + # si claude se tait (outil qui pend) ou n'exit jamais après son
242 + # résultat (mission f9eb9d1b59c8 du 2026-08-24, zombie 5 h), la
243 + # mission restait « running » pour toujours et bloquait le nœud.
244 + while proc.poll() is None:
245 + if time.time() > deadline:
246 + proc.kill()
247 + post_event(m.callback_url, {"type": "error", "data": {"error": f"durée max atteinte ({m.timeout_seconds // 60} min) — session interrompue par le watchdog"}})
248 + return
249 + time.sleep(30)
250 +
251 + threading.Thread(target=_watchdog, daemon=True).start()
237 252 with transcript.open("w") as tf:
238 253 for line in proc.stdout: # type: ignore[union-attr]
239 254 tf.write(line)
240 255