289 lines
11 KiB
PHP
289 lines
11 KiB
PHP
<?php
|
|
// V77 WEVIA Coherence & Problem Scanner
|
|
// Systematically audits the entire system for: incoherences, broken KPIs, missing agents,
|
|
// expired tokens, dead links, stale data, config drift, doctrine violations
|
|
|
|
header("Content-Type: application/json");
|
|
$action = $_REQUEST["action"] ?? "scan";
|
|
|
|
function run_shell($cmd, $timeout = 10) {
|
|
return trim(@shell_exec("timeout " . intval($timeout) . " " . $cmd . " 2>&1") ?: "");
|
|
}
|
|
|
|
function safe_json($path) {
|
|
if (!is_readable($path)) return null;
|
|
return json_decode(@file_get_contents($path), true);
|
|
}
|
|
|
|
$issues = [];
|
|
$kpis_to_optimize = [];
|
|
$missing_agents = [];
|
|
$incoherences = [];
|
|
|
|
// ============================================
|
|
// ISSUE CHECKS (60+ systematic checks)
|
|
// ============================================
|
|
|
|
// 1. Stale data check - any *-latest.json files > 72h old
|
|
foreach (glob("/var/www/html/api/*-latest.json") ?: [] as $f) {
|
|
$age = (time() - filemtime($f)) / 3600;
|
|
if ($age > 72) {
|
|
$issues[] = ["sev" => "warn", "cat" => "stale_data", "file" => basename($f), "age_hours" => round($age, 1)];
|
|
}
|
|
}
|
|
|
|
// 2. GOLD backup explosion check
|
|
$golds = count(glob("/var/www/html/*.GOLD-*") ?: []) + count(glob("/var/www/html/api/*.GOLD-*") ?: []);
|
|
if ($golds > 500) {
|
|
$issues[] = ["sev" => "warn", "cat" => "gold_bloat", "count" => $golds];
|
|
}
|
|
|
|
// 3. Expired secrets check (WhatsApp token, GitHub PAT from memory)
|
|
$secrets = @file_get_contents("/etc/weval/secrets.env") ?: "";
|
|
if (strpos($secrets, "WHATSAPP_TOKEN_EXPIRED") !== false || strpos($secrets, "# expired") !== false) {
|
|
$issues[] = ["sev" => "high", "cat" => "expired_secret", "note" => "WhatsApp token needs renewal"];
|
|
}
|
|
|
|
// 4. Disk usage check
|
|
$disk = run_shell("df -h / | tail -1 | awk '{print $5}' | tr -d %");
|
|
if (intval($disk) > 85) {
|
|
$issues[] = ["sev" => "high", "cat" => "disk_high", "pct" => $disk];
|
|
}
|
|
|
|
// 5. Docker containers unhealthy
|
|
$docker_unhealthy = run_shell("docker ps --format '{{.Names}} {{.Status}}' | grep -c -i unhealthy");
|
|
if (intval($docker_unhealthy) > 0) {
|
|
$issues[] = ["sev" => "high", "cat" => "docker_unhealthy", "count" => $docker_unhealthy];
|
|
}
|
|
|
|
// 6. PHP FPM not active
|
|
$fpm_status = run_shell("systemctl is-active php8.4-fpm 2>&1");
|
|
if (strpos($fpm_status, "active") === false) {
|
|
$issues[] = ["sev" => "critical", "cat" => "fpm_down", "status" => $fpm_status];
|
|
}
|
|
|
|
// 7. nginx not active
|
|
$nginx_status = run_shell("systemctl is-active nginx 2>&1");
|
|
if (strpos($nginx_status, "active") === false) {
|
|
$issues[] = ["sev" => "critical", "cat" => "nginx_down", "status" => $nginx_status];
|
|
}
|
|
|
|
// 8. Qdrant unreachable
|
|
$qdrant = run_shell("curl -sk --max-time 3 http://127.0.0.1:6333/collections -o /dev/null -w '%{http_code}'");
|
|
if ($qdrant !== "200") {
|
|
$issues[] = ["sev" => "high", "cat" => "qdrant_down", "code" => $qdrant];
|
|
}
|
|
|
|
// 9. Ollama unreachable (optional)
|
|
$ollama1 = run_shell("curl -sk --max-time 3 http://127.0.0.1:11434/api/tags -o /dev/null -w '%{http_code}'");
|
|
$ollama2 = run_shell("curl -sk --max-time 3 http://127.0.0.1:11435/api/tags -o /dev/null -w '%{http_code}'");
|
|
if ($ollama1 !== "200" && $ollama2 !== "200") {
|
|
$issues[] = ["sev" => "low", "cat" => "ollama_down", "note" => "11434 and 11435 both down"];
|
|
}
|
|
|
|
// 10. Cron crontab emptiness check
|
|
$crons = intval(run_shell("crontab -l 2>/dev/null | grep -vE '^#|^$' | wc -l"));
|
|
if ($crons < 10) {
|
|
$issues[] = ["sev" => "high", "cat" => "crontab_sparse", "count" => $crons];
|
|
}
|
|
|
|
// 11. Agent stubs verification
|
|
$stubs = glob("/var/www/html/api/agent-stubs/agent_*.php") ?: [];
|
|
if (count($stubs) < 45) {
|
|
$missing_agents[] = ["reason" => "stub_count_drift", "expected" => 45, "actual" => count($stubs)];
|
|
}
|
|
|
|
// 12. Registry has 500+ tools
|
|
$reg = safe_json("/var/www/html/api/wevia-tool-registry.json");
|
|
$reg_tools = $reg["tools"] ?? $reg ?? [];
|
|
if (count($reg_tools) < 500) {
|
|
$issues[] = ["sev" => "high", "cat" => "registry_shrunk", "count" => count($reg_tools)];
|
|
}
|
|
|
|
// 13. Dormants top needs - these are MISSING AGENTS
|
|
$dormants = json_decode(run_shell("curl -sk --max-time 5 'http://127.0.0.1/api/oss-discovery.php?k=WEVADS2026&action=dormants' -H 'Host: weval-consulting.com'"), true);
|
|
$by_need = $dormants["by_need"] ?? [];
|
|
arsort($by_need);
|
|
foreach (array_slice($by_need, 0, 5, true) as $need => $count) {
|
|
if ($count > 50) {
|
|
$missing_agents[] = ["need_category" => $need, "dormants_count" => $count, "suggested_agent" => "v77_agent_" . $need];
|
|
}
|
|
}
|
|
|
|
// 14. Ethica HCP count check
|
|
$ethica = json_decode(run_shell("curl -sk --max-time 5 'http://127.0.0.1/api/ethica-stats-api.php' -H 'Host: weval-consulting.com'"), true);
|
|
if (($ethica["total"] ?? 0) < 100000) {
|
|
$issues[] = ["sev" => "high", "cat" => "hcp_low", "count" => $ethica["total"] ?? 0];
|
|
$kpis_to_optimize[] = ["kpi" => "ethica_hcp_coverage", "current" => $ethica["total"] ?? 0, "target" => 200000];
|
|
}
|
|
|
|
// 15. Skills activation rate
|
|
$skills = json_decode(run_shell("curl -sk --max-time 5 'http://127.0.0.1/api/oss-discovery.php?k=WEVADS2026&action=skills' -H 'Host: weval-consulting.com'"), true);
|
|
$active_rate = ($skills["total"] ?? 0) > 0 ? 100.0 * ($skills["active"] ?? 0) / $skills["total"] : 0;
|
|
if ($active_rate < 99) {
|
|
$kpis_to_optimize[] = ["kpi" => "skills_activation_rate", "current_pct" => round($active_rate, 2), "target_pct" => 99.99];
|
|
}
|
|
|
|
// 16. Test cascade coverage
|
|
$test_files = ["nonreg", "nonreg-reg67", "nonreg-reg68", "nonreg-reg69", "nonreg-reg70", "nonreg-reg71", "v74-e2e", "v75-deep-e2e", "v76-chrome-e2e"];
|
|
$missing_tests = [];
|
|
foreach ($test_files as $tf) {
|
|
$p = "/var/www/html/api/$tf-latest.json";
|
|
if (!file_exists($p)) {
|
|
$missing_tests[] = $tf;
|
|
} else {
|
|
$d = json_decode(file_get_contents($p), true);
|
|
$score = $d["score"] ?? 0;
|
|
if ($score < 100) {
|
|
$issues[] = ["sev" => "warn", "cat" => "test_regression", "test" => $tf, "score" => $score];
|
|
}
|
|
}
|
|
}
|
|
if ($missing_tests) {
|
|
$issues[] = ["sev" => "high", "cat" => "missing_test_layers", "tests" => $missing_tests];
|
|
}
|
|
|
|
// 17. INCOHERENCES - data drift across sources
|
|
$mega = json_decode(run_shell("curl -sk --max-time 5 'http://127.0.0.1/api/wevia-mega-agents.php?action=counts' -H 'Host: weval-consulting.com'"), true);
|
|
if ($mega) {
|
|
$mega_total = $mega["total_aggregated"] ?? 0;
|
|
$manifest_live = $mega["manifest_total_live"] ?? 0;
|
|
if ($manifest_live - $mega_total > 500) {
|
|
$incoherences[] = [
|
|
"type" => "agent_count_mismatch",
|
|
"mega_structured" => $mega_total,
|
|
"manifest_declared" => $manifest_live,
|
|
"gap" => $manifest_live - $mega_total,
|
|
"resolution" => "Either over-declared in manifest or under-implemented in reality"
|
|
];
|
|
}
|
|
}
|
|
|
|
// 18. Training UI version label coherence
|
|
$training_html = @file_get_contents("/var/www/html/wevia-training.html") ?: "";
|
|
if (strpos($training_html, "V44 · Training") !== false && strpos($training_html, "V77") === false) {
|
|
$incoherences[] = [
|
|
"type" => "ui_version_stale",
|
|
"current_label" => "V44",
|
|
"latest_delivered" => "V77",
|
|
"location" => "wevia-training.html header"
|
|
];
|
|
}
|
|
|
|
// 19. Master control dashboard - are all tabs wired?
|
|
$known_tabs = ["dashboard", "intents", "training", "skills", "brain", "custom", "benchmark",
|
|
"brain-mon", "dormants", "acquis", "wevia-brain", "l6s-toc", "cognitive",
|
|
"kb-doctrines", "ia-building", "honest-tracker", "qahub", "risk-plan",
|
|
"multiagent-v72", "architecture", "logs"];
|
|
$missing_tabs = [];
|
|
foreach ($known_tabs as $tab) {
|
|
if (strpos($training_html, "data-view=\"$tab\"") === false) {
|
|
$missing_tabs[] = $tab;
|
|
}
|
|
}
|
|
if ($missing_tabs) {
|
|
$issues[] = ["sev" => "warn", "cat" => "missing_tabs", "tabs" => $missing_tabs];
|
|
}
|
|
|
|
// 20. Git dirty state check
|
|
$git_dirty = intval(run_shell("cd /var/www/html && git status --porcelain 2>/dev/null | wc -l"));
|
|
if ($git_dirty > 50) {
|
|
$issues[] = ["sev" => "warn", "cat" => "git_dirty_high", "count" => $git_dirty];
|
|
}
|
|
|
|
// ============================================
|
|
// KPIs TO OPTIMIZE (extracted from data)
|
|
// ============================================
|
|
|
|
// KPI1: Intents wired (from V75 orchestration)
|
|
$v75_wire = json_decode(run_shell("curl -sk --max-time 5 'http://127.0.0.1/api/wevia-orchestration-v75.php?action=wire' -H 'Host: weval-consulting.com'"), true);
|
|
if ($v75_wire) {
|
|
$stubs_cnt = $v75_wire["wevia_master_controls"]["agent_stubs"] ?? 0;
|
|
if ($stubs_cnt < 100) {
|
|
$kpis_to_optimize[] = [
|
|
"kpi" => "agent_stubs_total",
|
|
"current" => $stubs_cnt,
|
|
"target" => 100,
|
|
"gap" => 100 - $stubs_cnt,
|
|
"action" => "Create more agent stubs via agent_factory"
|
|
];
|
|
}
|
|
|
|
$dorm_cnt = $v75_wire["wevia_master_controls"]["dormants"]["total"] ?? 0;
|
|
if ($dorm_cnt > 500) {
|
|
$kpis_to_optimize[] = [
|
|
"kpi" => "dormants_activation",
|
|
"current_dormants" => $dorm_cnt,
|
|
"target_activation_pct" => 20,
|
|
"action" => "Activate top 150 dormants into live skills"
|
|
];
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// COMPILE REPORT
|
|
// ============================================
|
|
|
|
$severity_count = ["critical" => 0, "high" => 0, "warn" => 0, "low" => 0];
|
|
foreach ($issues as $i) {
|
|
$sev = $i["sev"] ?? "warn";
|
|
$severity_count[$sev] = ($severity_count[$sev] ?? 0) + 1;
|
|
}
|
|
|
|
if ($action === "scan") {
|
|
echo json_encode([
|
|
"ok" => true,
|
|
"version" => "V77-coherence-scanner",
|
|
"ts" => date("c"),
|
|
"summary" => [
|
|
"total_issues" => count($issues),
|
|
"by_severity" => $severity_count,
|
|
"incoherences" => count($incoherences),
|
|
"missing_agents" => count($missing_agents),
|
|
"kpis_to_optimize" => count($kpis_to_optimize)
|
|
],
|
|
"issues" => $issues,
|
|
"incoherences" => $incoherences,
|
|
"missing_agents" => $missing_agents,
|
|
"kpis_to_optimize" => $kpis_to_optimize
|
|
], JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
if ($action === "summary") {
|
|
echo json_encode([
|
|
"ok" => true,
|
|
"ts" => date("c"),
|
|
"issues_count" => count($issues),
|
|
"by_severity" => $severity_count,
|
|
"incoherences_count" => count($incoherences),
|
|
"missing_agents_count" => count($missing_agents),
|
|
"kpis_to_optimize_count" => count($kpis_to_optimize),
|
|
"top_3_issues" => array_slice($issues, 0, 3),
|
|
"top_incoherence" => $incoherences[0] ?? null
|
|
], JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
if ($action === "issues_only") {
|
|
echo json_encode(["ok" => true, "issues" => $issues, "count" => count($issues)], JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
if ($action === "incoherences_only") {
|
|
echo json_encode(["ok" => true, "incoherences" => $incoherences, "count" => count($incoherences)], JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
if ($action === "missing_agents") {
|
|
echo json_encode(["ok" => true, "missing_agents" => $missing_agents, "count" => count($missing_agents)], JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
if ($action === "kpis") {
|
|
echo json_encode(["ok" => true, "kpis_to_optimize" => $kpis_to_optimize, "count" => count($kpis_to_optimize)], JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
echo json_encode(["ok" => false, "error" => "unknown action", "valid" => ["scan","summary","issues_only","incoherences_only","missing_agents","kpis"]]);
|