PDO::ERRMODE_EXCEPTION]); $resolved = []; // Rule 1: blade-agent-exec — auto-resolve if agent callback restored (last done < 30min) $last_done = 0; foreach (glob("/var/www/html/api/blade-tasks/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; } } if ($last_done && (time() - $last_done) < 1800) { $s = $db->prepare("UPDATE weval.andon_alerts SET status='resolved', resolved_at=NOW(), resolved_by='auto-V38', resolution_path=:r WHERE station='blade-agent-exec' AND status='open' RETURNING id"); $s->execute([":r" => "Agent Blade callback restored (last done " . date("c", $last_done) . ")"]); foreach ($s->fetchAll(PDO::FETCH_ASSOC) as $r) $resolved[] = ["rule" => "blade-recovered", "id" => $r["id"]]; } // Rule 2: Auto-expire stale open alerts > 30 days $s = $db->prepare("UPDATE weval.andon_alerts SET status='expired', resolved_at=NOW(), resolved_by='auto-V38-expired' WHERE status='open' AND created_at < NOW() - INTERVAL '30 days' RETURNING id, station"); $s->execute(); foreach ($s->fetchAll(PDO::FETCH_ASSOC) as $r) $resolved[] = ["rule" => "stale-30d", "id" => $r["id"], "station" => $r["station"]]; // Rule 3: Duplicate open alerts same station — keep latest, expire olders $s = $db->query(" WITH ranked AS ( SELECT id, station, ROW_NUMBER() OVER (PARTITION BY station ORDER BY created_at DESC) rn FROM weval.andon_alerts WHERE status='open' ) UPDATE weval.andon_alerts a SET status='deduplicated', resolved_at=NOW(), resolved_by='auto-V38-dedup' FROM ranked r WHERE a.id = r.id AND r.rn > 1 RETURNING a.id, a.station "); foreach ($s->fetchAll(PDO::FETCH_ASSOC) as $r) $resolved[] = ["rule" => "dedup", "id" => $r["id"], "station" => $r["station"]]; echo json_encode([ "ok" => true, "v" => "V38-andon-autoresolve", "ts" => date("c"), "resolved_count" => count($resolved), "resolved" => $resolved ], JSON_PRETTY_PRINT);