/dev/null"), true); if ($l99 && isset($l99['passed'])) $out .= sprintf("L99 : %d/%d\n", $l99['passed'], $l99['total']); // Commits récents $commits = trim(@shell_exec("cd /var/www/html && git log --oneline -10 2>&1")); $out .= "\n-- 10 derniers commits --\n" . $commits; // Tags récents $tags = trim(@shell_exec("cd /var/www/html && git tag --sort=-creatordate 2>&1 | head -10")); $out .= "\n\n-- 10 derniers tags --\n" . $tags; // Wave-267→270 + Doctrine 133/134 $doctrines = []; foreach (["133","134"] as $n) { $p = "/opt/wevads/vault/doctrine-{$n}-*.md"; $f = glob($p); $doctrines[] = "doctrine-$n: " . (empty($f) ? "MISSING" : basename($f[0])); } $out .= "\n\n-- Doctrines récentes --\n" . implode("\n", $doctrines); return $out; } function handler_kpi_source() { $out = "=== KPI SOURCES - REFERENTIEL UNIQUE === "; $out .= "Source UNIQUE: /api/source-of-truth.json (rebuild cron 10min - doctrine 136) "; $out .= "Full registry: /api/wevia-truth-registry.json (1.78MB detail) "; $out .= "Aggregator KPI: /api/wtp-kpi-global-v2.php (cache 30s) "; // Source primary: source-of-truth.json (fresh, rebuilt every 10min) $sot = @json_decode(@file_get_contents("/var/www/html/api/source-of-truth.json"), true); if ($sot) { $age = time() - filemtime("/var/www/html/api/source-of-truth.json"); $out .= sprintf("-- source-of-truth.json (age %ds) -- ", $age); foreach (["agents_count","skills_count","intents_count","brains_count","doctrines_count","dashboards_count","providers_count","ethica_total","docker_running","nonreg_score","autonomy_score","autonomy_level"] as $k) { if (isset($sot[$k])) $out .= sprintf(" %-20s: %s ", $k, $sot[$k]); } } // Complement: wtp-kpi-global-v2 $agg = @json_decode(@file_get_contents("http://127.0.0.1/api/wtp-kpi-global-v2.php"), true); if ($agg && isset($agg["synthesis"])) { $out .= " -- wtp-kpi-global-v2.php synthesis -- "; foreach ($agg["synthesis"] as $k => $v) { $out .= sprintf(" %-25s: %s ", $k, $v === null ? "null" : $v); } } return $out; } function handler_dashboards_overlap() { $out = "=== DASHBOARDS / HUBS OVERLAP ===\n"; $hubs = [ 'weval-technology-platform.html' => 'WTP — ERP portal', 'all-ia-hub.html' => 'All IA Hub', 'wevia-master.html' => 'WEVIA Master chat', 'wevia-orchestrator.html' => 'Arena Orchestrator', 'ops-center.html' => 'Ops Center', 'architecture.html' => 'Architecture', 'agents-archi.html' => 'Agents archi 3D', ]; foreach ($hubs as $file => $desc) { $path = "/var/www/html/$file"; if (!file_exists($path)) { $out .= sprintf(" [MISSING] %-40s %s\n", $file, $desc); continue; } $sz = round(filesize($path)/1024); // Count KPI fetches in each $c = @file_get_contents($path); $n_fetch = preg_match_all('/fetch\s*\(\s*[\'"][^\'"]*\/api\//', $c); $n_api = preg_match_all('/\/api\/[a-z0-9_\-]+\.(?:php|json)/i', $c); $out .= sprintf(" %-40s %3dKB · %d fetch · %d api refs · %s\n", $file, $sz, $n_fetch, $n_api, $desc); } // Source unique reco $out .= "\n-- Source unique KPI existante --\n"; $out .= " /api/wtp-kpi-global-v2.php (cache 30s, 12 KPI agrégés)\n"; $out .= " → Recommandation: TOUS les hubs consomment cette API\n"; return $out; } function handler_orphan_pages() { $out = "=== PAGES ORPHELINES (detection exhaustive) === "; $html_dir = "/var/www/html"; $all = glob("$html_dir/*.html"); $total = count($all); // Scanner hubs principaux $hubs = ["weval-technology-platform.html","all-ia-hub.html","wevia-master.html", "wevia-orchestrator.html","ops-center.html","architecture.html", "agents-archi.html","index.html","login.html","register.html","404.html", "dashboards-index.html","wiki.html","wevia.html"]; // Scan TOUS les .html comme referenceurs (pas juste hubs) $linked = []; foreach ($all as $p) { $c = @file_get_contents($p); if (!$c) continue; // Pattern exhaustif: href, src, window.open, location.href, string "/foo.html" $patterns = [ '/(?:href|src|action)=["\']([^"\']+\.html)["\']/i', '/window\.(?:open|location\.href)\s*=\s*["\']([^"\']+\.html)/', '/location\.(?:href|replace|assign)\s*\(\s*["\']([^"\']+\.html)/', '/navigateTo\(["\']([^"\']+\.html)/', '/["\'](\/[a-z0-9_\-]+\.html)["\']/i', ]; foreach ($patterns as $pat) { if (preg_match_all($pat, $c, $mm)) { foreach ($mm[1] as $target) { $target = ltrim($target, "/"); $target = explode("?", explode("#", $target)[0])[0]; $linked[$target] = true; } } } } $orphans = []; foreach ($all as $p) { $name = basename($p); if (in_array($name, $hubs)) continue; if (isset($linked[$name])) continue; $orphans[] = [ "name" => $name, "size_kb" => round(filesize($p)/1024, 1), "age_days" => intval((time() - filemtime($p))/86400), ]; } usort($orphans, fn($a,$b) => $b["size_kb"] - $a["size_kb"]); $out .= sprintf("Total .html scannes: %d ", $total); $out .= sprintf("Linked (refs JS + HTML): %d ", count($linked)); $out .= sprintf("Orphelines VRAIES (zero ref nulle part): %d ", count($orphans)); if (count($orphans) === 0) { $out .= "Aucune orpheline detectee. Architecture totalement reliee. "; } else { $out .= "-- Liste detail -- "; foreach ($orphans as $o) { $out .= sprintf(" %-60s %6.1fKB %3dj ", $o["name"], $o["size_kb"], $o["age_days"]); } $out .= " -- Action PHASE 3A -- "; $out .= " Ces pages ne sont linkees de nulle part. Choix: "; $out .= " 1. Relier dans un hub (si utile) "; $out .= " 2. Documenter en intent explicite (chat-v2 peut y renvoyer) "; $out .= " 3. Supprimer apres GOLD + validation Yacine (ZERO suppression sans accord) "; } return $out; } function handler_autowire_capabilities() { $out = "=== AUTO-WIRE CAPABILITIES ===\n"; // Opus-intents file analysis $opus_file = "/var/www/html/api/opus-intents.php"; if (file_exists($opus_file)) { $c = file_get_contents($opus_file); $n_intents = preg_match_all('/^\s*(?:case\s+["\']|if\s*\(.*preg_match|elseif\s*\(.*preg_match|\$map\[.*=>)/m', $c); $out .= sprintf("opus-intents.php : %d KB, ~%d intents detected\n", round(strlen($c)/1024), $n_intents); } // Priority NL intents $pri = @json_decode(@file_get_contents("/opt/wevia-brain/priority-intents-nl.json"), true); $out .= sprintf("priority-intents-nl.json : %d intents\n", is_array($pri) ? count($pri) : 0); // Fast-path $fp = "/var/www/weval/wevia-ia/wevia-fast-path-v3.php"; if (file_exists($fp)) { $c = @file_get_contents($fp); $n = preg_match_all('/preg_match|case\s+["\']/i', $c ?: ''); $out .= sprintf("fast-path-v3.php : %d intents approx\n", $n); } // Opus46 intents $o46 = glob("/var/www/html/api/wevia-opus46-intents.php")[0] ?? null; if ($o46) { $c = file_get_contents($o46); $n = preg_match_all('/^\s*(?:if|elseif)\s*\(.*preg_match/m', $c); $out .= sprintf("opus46-intents.php : %d intents EXEC\n", $n); } // Auto-wired 30 days: git log filter $auto_commits = trim(@shell_exec("cd /var/www/html && git log --since='30 days ago' --grep='intent\\|autowire\\|auto-wire' --oneline 2>&1 | wc -l")); $out .= sprintf("Commits auto-wire 30 derniers jours : %s\n", $auto_commits ?: '0'); // Capabilities check $out .= "\n-- Capabilities check --\n"; $out .= " [YES] Exécution shell: shell_exec disponible\n"; $out .= " [YES] Git commit: sudo NOPASSWD OK\n"; $out .= " [YES] PHP self-patch: sudo chattr +i/-i OK\n"; $sel_exists = file_exists("/opt/weval-ops/selenium-runner.py") || file_exists("/home/claude/selenium-runner.py") ? "partial" : "MISSING"; $out .= " [" . strtoupper($sel_exists) . "] Selenium Chrome: runner centralisé\n"; $blade_log = glob("/var/log/blade-sentinel.log"); $out .= " [" . (empty($blade_log) ? "MISSING" : "OK") . "] Blade Sentinel: log récent\n"; $out .= "\n-- GAP d'autonomie (à combler PHASE 3) --\n"; $out .= " * Pas d'intent 'auto_wire' capable de créer un nouvel intent depuis NL\n"; $out .= " * Pas d'intent 'clone_github_tool' capable de cloner+adapter un tool OSS\n"; $out .= " * Selenium Chrome runner centralisé absent → création compte via Blade impossible\n"; return $out; } function handler_tips_library() { $out = "=== TIPS LIBRARY INVENTORY ===\n"; $cats = [ "Selenium account creation" => "selenium", "Token renewal (GitHub/Gitea)" => "rotation.?token|github.?pat|gitea.?token", "Office recovery (Azure AD)" => "azure.?ad|tenant.*office", "IP rotation (CF/PMTA)" => "ip.?rotat|warmup.?ip", "DeepSeek web cookies" => "deepseek", "ThuggieGPT web" => "thuggie", "Claude.ai web cookies" => "claude.ai.*cook|claude.ai.*session", "Gemini web" => "gemini", "Qwen web" => "qwen", "Mythos / Opus 4.6 tips" => "mythos|opus.?4\\.6", ]; static $corpus = null; if ($corpus === null) { $files = []; foreach (["/opt/wevads/vault", "/var/www/html/wiki", "/opt/weval-ops/wiki"] as $d) { if (is_dir($d)) { $fs = @glob("$d/*.md") ?: []; $files = array_merge($files, array_slice($fs, 0, 200)); } } $corpus = ""; foreach ($files as $f) { $corpus .= "\n=== $f ===\n" . @file_get_contents($f); } $out .= "Corpus: " . count($files) . " .md files, " . round(strlen($corpus)/1024) . " KB scanned\n\n"; } foreach ($cats as $label => $regex) { $n = @preg_match_all("/$regex/i", $corpus); $status = $n > 0 ? "[OK $n]" : "[MISS]"; $out .= sprintf(" %-38s %s\n", $label, $status); } $out .= "\n-- Missing categories = PHASE 3B targets --\n"; $out .= " * Pour chaque [MISS]: rediger doctrine dediee /opt/wevads/vault/\n"; $out .= " * Creer intent execute_tip() pilote Selenium via Blade\n"; return $out; }