adoption rate KPI * POST ?action=track -> log feature use (feature, user, session_id) * GET ?action=list -> features inventory */ header("Content-Type: application/json"); header("Access-Control-Allow-Origin: *"); $STORAGE = "/opt/weval-l99/data/feature-adoption.jsonl"; $INVENTORY = "/opt/weval-l99/data/feature-inventory.json"; @mkdir(dirname($STORAGE), 0755, true); // Default inventory if not exists if (!file_exists($INVENTORY)) { $default_features = [ "wtp_dashboard", "wtp_pilotage_widget", "wtp_sparklines", "wtp_l99_brain", "wevia_master_chat", "wevia_orchestrator", "all_ia_hub", "agents_archi", "architecture_live", "openclaw", "nonreg_dashboard", "stripe_live", "ethica_hcp", "wevads_ia", "director_chat", "orphans_hub", "wikidoc", "vault_manager", "nps_feedback", "csat_rating", "ticket_create" ]; @file_put_contents($INVENTORY, json_encode(["features" => $default_features, "total" => count($default_features)])); } $action = $_GET["action"] ?? ($_POST["action"] ?? "stats"); if ($action === "track" && $_SERVER["REQUEST_METHOD"] === "POST") { $feature = substr(trim($_POST["feature"] ?? ""), 0, 80); $user = substr(trim($_POST["user"] ?? "anonymous"), 0, 60); $session = substr(trim($_POST["session_id"] ?? ""), 0, 40); if (!$feature) { echo json_encode(["ok"=>false,"error"=>"feature_required"]); exit; } $record = ["ts"=>date("c"),"feature"=>$feature,"user"=>$user,"session_id"=>$session]; @file_put_contents($STORAGE, json_encode($record)."\n", FILE_APPEND | LOCK_EX); echo json_encode(["ok"=>true,"tracked"=>$feature]); exit; } if ($action === "list") { $inv = @json_decode(@file_get_contents($INVENTORY), true); echo json_encode($inv ?: ["features"=>[],"total"=>0]); exit; } // stats = adoption rate calculation $inv = @json_decode(@file_get_contents($INVENTORY), true); $total_features = intval($inv["total"] ?? 0); $used_features = []; $events = []; if (is_readable($STORAGE)) { foreach (file($STORAGE) as $line) { $r = @json_decode(trim($line), true); if ($r && isset($r["feature"])) { $used_features[$r["feature"]] = ($used_features[$r["feature"]] ?? 0) + 1; $events[] = $r; } } } $adopted = count($used_features); $adoption_rate = $total_features > 0 ? round(($adopted / $total_features) * 100) : 0; // 7d & 30d activity $now = time(); $evt_7d = 0; $evt_30d = 0; $users_7d = []; $users_30d = []; foreach ($events as $e) { $t = strtotime($e["ts"]); if ($t >= $now - 7*86400) { $evt_7d++; $users_7d[$e["user"] ?? ""] = true; } if ($t >= $now - 30*86400) { $evt_30d++; $users_30d[$e["user"] ?? ""] = true; } } echo json_encode([ "ok"=>true, "source"=>"sovereign_jsonl_tracker", "ts"=>date("c"), "adoption_rate_pct"=>$adoption_rate, "features_total"=>$total_features, "features_adopted"=>$adopted, "features_top5"=>array_slice(array_reverse(array_keys($used_features)), 0, 5), "events_total"=>count($events), "events_7d"=>$evt_7d, "events_30d"=>$evt_30d, "unique_users_7d"=>count($users_7d), "unique_users_30d"=>count($users_30d), "status"=>count($events)===0 ? "wire_needed" : ($adoption_rate >= 70 ? "ok" : "warn"), "drill"=>"Features used / Features available ยท Track via POST ?action=track", ]);