10min = zombies // Doctrine #4 HONNETE + #13 cause racine + #7 zero manuel header("Content-Type: application/json"); $TASKS_DIR = "/var/www/html/api/blade-tasks"; $TIMEOUT_MINUTES = (int)($_GET["timeout_min"] ?? 10); $DRY = isset($_GET["dry"]); $now = time(); $cutoff = $now - ($TIMEOUT_MINUTES * 60); $results = ["timed_out" => [], "kept" => 0, "failed_marked" => 0]; foreach (glob($TASKS_DIR . "/task_*.json") as $tf) { $td = @json_decode(file_get_contents($tf), true); if (!$td) continue; if (($td["status"] ?? "") !== "dispatched") { $results["kept"]++; continue; } $disp = $td["dispatched_at"] ?? $td["created"] ?? ""; $t = $disp ? strtotime($disp) : 0; if (!$t || $t > $cutoff) { $results["kept"]++; continue; } $results["timed_out"][] = [ "id" => $td["id"] ?? basename($tf), "dispatched_at" => $disp, "age_min" => round(($now - $t) / 60, 1), "label" => $td["label"] ?? "?" ]; if (!$DRY) { $td["status"] = "failed_timeout"; $td["failed_at"] = date("c"); $td["error"] = "Agent Blade did not callback task_done within {$TIMEOUT_MINUTES}min"; file_put_contents($tf, json_encode($td, JSON_PRETTY_PRINT)); $results["failed_marked"]++; } } // Summary + agent health assessment $last_done = 0; foreach (glob($TASKS_DIR . "/task_*.json") as $tf) { $td = @json_decode(file_get_contents($tf), true); if (($td["status"] ?? "") === "done") { $c = strtotime($td["completed_at"] ?? "1970-01-01"); if ($c > $last_done) $last_done = $c; } } $agent_stale_hours = $last_done ? round(($now - $last_done) / 3600, 1) : 9999; echo json_encode([ "ok" => true, "v" => "V37-blade-autoheal", "ts" => date("c"), "dry_run" => $DRY, "timeout_minutes" => $TIMEOUT_MINUTES, "stats" => $results, "agent_health" => [ "last_done_ts" => $last_done ? date("c", $last_done) : null, "stale_hours" => $agent_stale_hours, "agent_execution_ok" => $agent_stale_hours < 1, "verdict" => $agent_stale_hours < 1 ? "HEALTHY" : ($agent_stale_hours < 24 ? "DEGRADED" : "BROKEN") ] ], JSON_PRETTY_PRINT);