Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
ROOT CAUSE identified: V163 last </div> anchor landed MID-ATTRIBUTE of V132 100pct badge <a> link. HTML parser ignored the nested invalid markup so context-col never reached DOM despite being in served HTML source. Playwright trace showed: Served HTML: 1 context-col div present DOM after ready: Element not present at DOM-ready 0 findable via querySelectorAll Fix V164: 1. Located broken insertion: between border:1px solid and rgba(...) style 2. Extracted context-col block 2272 chars from broken location 3. Re-inserted BEFORE real main close after V132 100pct </a> complete tag Post-fix Playwright verify: split-layout found: x=1071 width=849 height=1036 chat-col found: x=1071 width=492 height=1036 context-col found: x=1563 width=357 height=1036 4 tabs present 4 KPI cards present Files: /var/www/html/wevia-master.html 47549 bytes (balanced 83 divs) GOLD preserved V162 base L99 153/153 PASS (31 consecutive versions V125-V164) Doctrines 0 13 14 16 54 60 95 100 applied UX premium zero regression
352 lines
17 KiB
PHP
352 lines
17 KiB
PHP
<?php
|
|
// WAVE 252 · Predictive Solution Scanner + Gap Analysis + Dev Effort Estimator
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
header('Access-Control-Allow-Origin: *');
|
|
set_time_limit(30);
|
|
|
|
function pg_c() { return @pg_connect('host=10.1.0.3 port=5432 dbname=paperclip user=admin password=admin123 connect_timeout=3'); }
|
|
function load_secrets() {
|
|
$s = [];
|
|
if (!is_readable('/etc/weval/secrets.env')) return $s;
|
|
foreach (file('/etc/weval/secrets.env', FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES) as $l) {
|
|
if (empty(trim($l))||$l[0]==='#') continue;
|
|
$p = strpos($l,'='); if ($p) $s[trim(substr($l,0,$p))] = trim(substr($l,$p+1)," \t\"'");
|
|
}
|
|
return $s;
|
|
}
|
|
|
|
// Multi-user production readiness checklist template
|
|
function mup_checklist() {
|
|
return [
|
|
'auth' => ['name'=>'Auth multi-user (SSO/SAML)', 'dev_days'=>5, 'critical'=>true],
|
|
'rbac' => ['name'=>'RBAC (roles/permissions)', 'dev_days'=>3, 'critical'=>true],
|
|
'billing' => ['name'=>'Billing Stripe (plans/quotas)', 'dev_days'=>4, 'critical'=>true],
|
|
'tenant_isolation' => ['name'=>'Multi-tenant DB isolation', 'dev_days'=>6, 'critical'=>true],
|
|
'rate_limit' => ['name'=>'Rate limiting per user', 'dev_days'=>2, 'critical'=>true],
|
|
'monitoring' => ['name'=>'Monitoring/alerts production', 'dev_days'=>2, 'critical'=>false],
|
|
'logs' => ['name'=>'Logs centralisés (audit trail)', 'dev_days'=>2, 'critical'=>false],
|
|
'backup' => ['name'=>'Backup + disaster recovery', 'dev_days'=>3, 'critical'=>true],
|
|
'docs_api' => ['name'=>'API docs (Swagger/OpenAPI)', 'dev_days'=>2, 'critical'=>false],
|
|
'docs_user' => ['name'=>'User docs + onboarding flow', 'dev_days'=>3, 'critical'=>true],
|
|
'i18n' => ['name'=>'i18n (EN/FR/AR)', 'dev_days'=>4, 'critical'=>false],
|
|
'gdpr' => ['name'=>'GDPR compliance (data export/delete)', 'dev_days'=>3, 'critical'=>true],
|
|
'email_tx' => ['name'=>'Emails transactionnels (welcome/reset/invoice)', 'dev_days'=>2, 'critical'=>true],
|
|
'support' => ['name'=>'Support system (helpdesk/chat)', 'dev_days'=>3, 'critical'=>false],
|
|
'mobile' => ['name'=>'Mobile responsive/PWA', 'dev_days'=>4, 'critical'=>false],
|
|
'tests' => ['name'=>'Tests E2E Playwright + coverage', 'dev_days'=>5, 'critical'=>true],
|
|
'perf' => ['name'=>'Performance (Lighthouse 90+)', 'dev_days'=>3, 'critical'=>false],
|
|
'security' => ['name'=>'Pentest + security audit', 'dev_days'=>4, 'critical'=>true],
|
|
'landing' => ['name'=>'Landing page + demo flow', 'dev_days'=>3, 'critical'=>true],
|
|
'pricing' => ['name'=>'Pricing page + ROI calculator', 'dev_days'=>2, 'critical'=>true],
|
|
];
|
|
}
|
|
|
|
// Solutions catalog avec capability scan (what's done vs what's needed)
|
|
function solutions_catalog() {
|
|
return [
|
|
[
|
|
'id'=>'ethica-hcp', 'rank'=>1, 'name'=>'Ethica HCP Database MENA',
|
|
'category'=>'Pharma Data', 'status'=>'PROD', 'maturity'=>95,
|
|
'effort'=>2, 'reward'=>9, 'mad_est'=>600000, 'days_to_prod'=>28,
|
|
'tam_mad'=>120000000, // 12M€ * 10 for MAD
|
|
'market_match_score'=>0, // computed
|
|
'capabilities_done' => ['auth','rbac','tenant_isolation','logs','gdpr','email_tx','docs_user','rate_limit','landing','pricing','backup','monitoring','docs_api','tests'],
|
|
'capabilities_todo' => ['billing','i18n','support','mobile','perf','security'],
|
|
'signals' => ['Pharma 11 leads MQL 80', '157K HCPs DB', 'Kaouther Najar SQL', 'MENA expansion'],
|
|
'target_segment'=>'Pharma',
|
|
],
|
|
[
|
|
'id'=>'weval-saas', 'rank'=>2, 'name'=>'WEVAL SaaS Freemium',
|
|
'category'=>'AI Platform', 'status'=>'BETA', 'maturity'=>70,
|
|
'effort'=>6, 'reward'=>9, 'mad_est'=>800000, 'days_to_prod'=>45,
|
|
'tam_mad'=>80000000,
|
|
'market_match_score'=>0,
|
|
'capabilities_done' => ['auth','logs','landing','docs_api','tests'],
|
|
'capabilities_todo' => ['rbac','tenant_isolation','billing','rate_limit','gdpr','email_tx','docs_user','i18n','support','mobile','perf','security','backup','pricing','monitoring'],
|
|
'signals' => ['Software MQL 95', 'Cloud MQL 90', 'Freemium viral'],
|
|
'target_segment'=>'Software',
|
|
],
|
|
[
|
|
'id'=>'wevads-brain', 'rank'=>3, 'name'=>'WEVADS Brain Outreach',
|
|
'category'=>'Email Engine', 'status'=>'PROD', 'maturity'=>92,
|
|
'effort'=>3, 'reward'=>8, 'mad_est'=>450000, 'days_to_prod'=>21,
|
|
'tam_mad'=>40000000,
|
|
'market_match_score'=>0,
|
|
'capabilities_done' => ['auth','rbac','tenant_isolation','monitoring','logs','rate_limit','backup','email_tx','security','tests'],
|
|
'capabilities_todo' => ['billing','docs_api','docs_user','i18n','support','mobile','landing','pricing','gdpr','perf'],
|
|
'signals' => ['PMTA+Kumo+Postfix triple', '9 winners SACRED', '95%+ delivery'],
|
|
'target_segment'=>'B2B outreach',
|
|
],
|
|
[
|
|
'id'=>'docuseal', 'rank'=>4, 'name'=>'DocuSeal E-signature MENA',
|
|
'category'=>'Legal Tech', 'status'=>'PROD', 'maturity'=>85,
|
|
'effort'=>2, 'reward'=>7, 'mad_est'=>250000, 'days_to_prod'=>14,
|
|
'tam_mad'=>35000000,
|
|
'market_match_score'=>0,
|
|
'capabilities_done' => ['auth','rbac','logs','gdpr','docs_user','docs_api','mobile','backup','tests'],
|
|
'capabilities_todo' => ['tenant_isolation','billing','rate_limit','i18n','support','email_tx','landing','pricing','perf','security','monitoring'],
|
|
'signals' => ['Banque 11 leads MQL 76', 'Retail 6 MQL 76', 'E-sign MENA gap'],
|
|
'target_segment'=>'Banque/Retail/Pharma',
|
|
],
|
|
[
|
|
'id'=>'dark-scout', 'rank'=>5, 'name'=>'Dark Scout Intel',
|
|
'category'=>'Competitive Intel', 'status'=>'PROD', 'maturity'=>75,
|
|
'effort'=>4, 'reward'=>7, 'mad_est'=>300000, 'days_to_prod'=>30,
|
|
'tam_mad'=>20000000,
|
|
'market_match_score'=>0,
|
|
'capabilities_done' => ['auth','logs','monitoring','docs_api','tests'],
|
|
'capabilities_todo' => ['rbac','tenant_isolation','billing','rate_limit','gdpr','email_tx','docs_user','i18n','support','mobile','landing','pricing','perf','security','backup'],
|
|
'signals' => ['Software MQL 95', '34 scans', 'Intel MENA gap'],
|
|
'target_segment'=>'Software/Consulting',
|
|
],
|
|
[
|
|
'id'=>'wevia-master', 'rank'=>6, 'name'=>'WEVIA Master Orchestrator',
|
|
'category'=>'AI Orchestration', 'status'=>'BETA', 'maturity'=>65,
|
|
'effort'=>5, 'reward'=>9, 'mad_est'=>700000, 'days_to_prod'=>60,
|
|
'tam_mad'=>150000000,
|
|
'market_match_score'=>0,
|
|
'capabilities_done' => ['auth','logs','docs_api','tests','monitoring'],
|
|
'capabilities_todo' => ['rbac','tenant_isolation','billing','rate_limit','gdpr','email_tx','docs_user','i18n','support','mobile','landing','pricing','perf','security','backup'],
|
|
'signals' => ['269 tools', '17 providers cascade', '0€ inference'],
|
|
'target_segment'=>'Enterprise AI',
|
|
],
|
|
[
|
|
'id'=>'blade-ai', 'rank'=>7, 'name'=>'Blade AI Web Agent',
|
|
'category'=>'Automation', 'status'=>'PROD', 'maturity'=>80,
|
|
'effort'=>3, 'reward'=>6, 'mad_est'=>200000, 'days_to_prod'=>21,
|
|
'tam_mad'=>15000000,
|
|
'market_match_score'=>0,
|
|
'capabilities_done' => ['auth','logs','monitoring','tests','docs_api','mobile'],
|
|
'capabilities_todo' => ['rbac','tenant_isolation','billing','rate_limit','gdpr','email_tx','docs_user','i18n','support','landing','pricing','perf','security','backup'],
|
|
'signals' => ['232 tasks automated', 'Selenium+Chrome', 'Cloud MQL 90'],
|
|
'target_segment'=>'Cloud/Software',
|
|
],
|
|
[
|
|
'id'=>'wepredict', 'rank'=>8, 'name'=>'WePredict AI Cockpits',
|
|
'category'=>'Predictive Analytics', 'status'=>'BETA', 'maturity'=>72,
|
|
'effort'=>4, 'reward'=>7, 'mad_est'=>350000, 'days_to_prod'=>35,
|
|
'tam_mad'=>30000000,
|
|
'market_match_score'=>0,
|
|
'capabilities_done' => ['auth','logs','monitoring','docs_api','tests'],
|
|
'capabilities_todo' => ['rbac','tenant_isolation','billing','rate_limit','gdpr','email_tx','docs_user','i18n','support','mobile','landing','pricing','perf','security','backup'],
|
|
'signals' => ['16 cockpits', '64 predictions', 'Deal close probability'],
|
|
'target_segment'=>'Software/Sales',
|
|
],
|
|
[
|
|
'id'=>'arena', 'rank'=>9, 'name'=>'WEVAL Arena Command Center',
|
|
'category'=>'Multi-LLM', 'status'=>'PROD', 'maturity'=>88,
|
|
'effort'=>3, 'reward'=>6, 'mad_est'=>180000, 'days_to_prod'=>14,
|
|
'tam_mad'=>10000000,
|
|
'market_match_score'=>0,
|
|
'capabilities_done' => ['auth','logs','monitoring','docs_api','tests','mobile','perf'],
|
|
'capabilities_todo' => ['rbac','tenant_isolation','billing','rate_limit','gdpr','email_tx','docs_user','i18n','support','landing','pricing','security','backup'],
|
|
'signals' => ['409 options', '715 agents', '17 providers'],
|
|
'target_segment'=>'Developers',
|
|
],
|
|
[
|
|
'id'=>'paperclip', 'rank'=>10, 'name'=>'Paperclip PM + CRM',
|
|
'category'=>'Project Mgmt', 'status'=>'BETA', 'maturity'=>60,
|
|
'effort'=>6, 'reward'=>5, 'mad_est'=>150000, 'days_to_prod'=>45,
|
|
'tam_mad'=>8000000,
|
|
'market_match_score'=>0,
|
|
'capabilities_done' => ['auth','logs','docs_api'],
|
|
'capabilities_todo' => ['rbac','tenant_isolation','billing','rate_limit','gdpr','email_tx','docs_user','i18n','support','mobile','landing','pricing','perf','security','backup','tests','monitoring'],
|
|
'signals' => ['48 leads tracked', 'Self-host'],
|
|
'target_segment'=>'SMB',
|
|
],
|
|
];
|
|
}
|
|
|
|
// WePredict-style regression prediction: market_match_score
|
|
// Formula: weighted market signals (industry MQL + lead count + SQL qualification rate)
|
|
function predict_market_score($solution) {
|
|
$pg = pg_c();
|
|
if (!$pg) return 50;
|
|
|
|
// Map solution target to industries
|
|
$target_map = [
|
|
'Pharma' => ['Pharma'],
|
|
'Banque/Retail/Pharma' => ['Banque','Retail','Pharma'],
|
|
'Software' => ['Software','Cloud'],
|
|
'Software/Consulting' => ['Software','Cloud','Streaming'],
|
|
'Software/Sales' => ['Software','Cloud'],
|
|
'Cloud/Software' => ['Cloud','Software'],
|
|
'Enterprise AI' => ['Pharma','Banque','Software','Cloud','Retail'],
|
|
'Developers' => ['Software','Cloud'],
|
|
'SMB' => ['Retail','Telecom','Mining'],
|
|
'B2B outreach' => ['Banque','Retail','Pharma','Software','Telecom'],
|
|
];
|
|
$industries = $target_map[$solution['target_segment']] ?? ['Pharma','Banque'];
|
|
$in_list = "'" . implode("','", array_map(function($i){return pg_escape_string($i);}, $industries)) . "'";
|
|
|
|
$sql = "SELECT COUNT(*) AS n, ROUND(AVG(mql_score)) AS avg_mql,
|
|
SUM(CASE WHEN sql_qualified THEN 1 ELSE 0 END) AS sql_q,
|
|
ROUND(SUM(CASE WHEN sql_qualified THEN 1 ELSE 0 END)::numeric / NULLIF(COUNT(*),0) * 100) AS sql_pct
|
|
FROM weval_leads WHERE industry IN ($in_list)";
|
|
$r = @pg_query($pg, $sql);
|
|
$stats = $r ? pg_fetch_assoc($r) : [];
|
|
pg_close($pg);
|
|
|
|
$leads = (int)($stats['n'] ?? 0);
|
|
$avg_mql = (int)($stats['avg_mql'] ?? 70);
|
|
$sql_q = (int)($stats['sql_q'] ?? 0);
|
|
$sql_pct = (int)($stats['sql_pct'] ?? 0);
|
|
|
|
// Predictive score:
|
|
// - Lead density (normalized 0-100): leads/48 * 100
|
|
// - Avg MQL (0-100 already)
|
|
// - SQL qualification rate (0-100)
|
|
// - Maturity (product readiness, 0-100)
|
|
$lead_density = min(100, ($leads / 48) * 100);
|
|
$maturity = (int)$solution['maturity'];
|
|
|
|
$score = round(
|
|
$lead_density * 0.25 +
|
|
$avg_mql * 0.30 +
|
|
$sql_pct * 0.15 +
|
|
$maturity * 0.30
|
|
);
|
|
|
|
return [
|
|
'score' => $score,
|
|
'breakdown' => [
|
|
'lead_density' => round($lead_density, 1),
|
|
'avg_mql' => $avg_mql,
|
|
'sql_pct' => $sql_pct,
|
|
'maturity' => $maturity,
|
|
'leads_in_target' => $leads,
|
|
'sql_qualified_in_target' => $sql_q,
|
|
],
|
|
'recommendation' => $score >= 75 ? 'LAUNCH NOW' : ($score >= 60 ? 'ACCELERATE' : ($score >= 45 ? 'NURTURE' : 'PIVOT')),
|
|
];
|
|
}
|
|
|
|
// Dev effort estimation for multi-user production
|
|
function dev_effort_to_production($solution) {
|
|
$checklist = mup_checklist();
|
|
$todo = $solution['capabilities_todo'] ?? [];
|
|
$total_days = 0;
|
|
$critical_days = 0;
|
|
$done_days = 0;
|
|
$nice_days = 0;
|
|
|
|
foreach ($todo as $cap) {
|
|
if (!isset($checklist[$cap])) continue;
|
|
$d = $checklist[$cap]['dev_days'];
|
|
$total_days += $d;
|
|
if ($checklist[$cap]['critical']) $critical_days += $d;
|
|
else $nice_days += $d;
|
|
}
|
|
|
|
// Done
|
|
$done = $solution['capabilities_done'] ?? [];
|
|
foreach ($done as $cap) {
|
|
if (isset($checklist[$cap])) $done_days += $checklist[$cap]['dev_days'];
|
|
}
|
|
|
|
// Dev cost estimate: 1 senior dev = 2500 MAD/day, 1 junior = 1200 MAD/day
|
|
// Assume 1 senior + 1 junior in parallel = 3700 MAD/day but -30% parallel overhead
|
|
$dev_cost_per_day = 3700 * 0.7; // 2590 MAD/day effective
|
|
$dev_cost_mad = round($total_days * $dev_cost_per_day);
|
|
|
|
// Parallelizable? Yes, roughly 60% of tasks can overlap
|
|
$calendar_days = round($total_days * 0.65);
|
|
|
|
return [
|
|
'total_dev_days' => $total_days,
|
|
'critical_path_days' => $critical_days,
|
|
'nice_to_have_days' => $nice_days,
|
|
'calendar_days_est' => $calendar_days,
|
|
'dev_cost_mad' => $dev_cost_mad,
|
|
'breakeven_customers' => $solution['mad_est'] > 0 ? round($dev_cost_mad / $solution['mad_est'] * 10, 1) / 10 : null,
|
|
'completion_pct' => count($done) > 0 ? round(count($done) / (count($done) + count($todo)) * 100) : 0,
|
|
];
|
|
}
|
|
|
|
// Main action routing
|
|
$action = $_GET['action'] ?? 'full_analysis';
|
|
|
|
if ($action === 'full_analysis') {
|
|
$solutions = solutions_catalog();
|
|
$checklist = mup_checklist();
|
|
|
|
foreach ($solutions as &$s) {
|
|
$prediction = predict_market_score($s);
|
|
$s['market_prediction'] = $prediction;
|
|
$s['dev_effort'] = dev_effort_to_production($s);
|
|
|
|
// Winning formula: combine market prediction + ICE + maturity
|
|
$ice_raw = ($s['reward'] * ($s['mad_est'] / 10000)) / max(1, $s['effort']);
|
|
$s['ice_score'] = round($ice_raw, 1);
|
|
|
|
// FINAL WINNING SCORE: combines all predictors
|
|
$s['winning_score'] = round(
|
|
$prediction['score'] * 0.40 +
|
|
min(100, $ice_raw / 3) * 0.25 +
|
|
$s['maturity'] * 0.25 +
|
|
(100 - min(100, $s['dev_effort']['calendar_days_est'])) * 0.10
|
|
);
|
|
|
|
// Decision: SHIP IT vs DEV vs PIVOT
|
|
if ($s['winning_score'] >= 78) $s['decision'] = 'SHIP_IT';
|
|
elseif ($s['winning_score'] >= 68) $s['decision'] = 'ACCELERATE';
|
|
elseif ($s['winning_score'] >= 55) $s['decision'] = 'DEV_SPRINT';
|
|
elseif ($s['winning_score'] >= 40) $s['decision'] = 'NURTURE';
|
|
else $s['decision'] = 'PIVOT_OR_PARK';
|
|
}
|
|
unset($s);
|
|
|
|
// Sort by winning_score desc
|
|
usort($solutions, function($a,$b){return $b['winning_score'] - $a['winning_score'];});
|
|
|
|
// Overall GAP analysis
|
|
$all_missing = [];
|
|
foreach ($solutions as $s) {
|
|
foreach ($s['capabilities_todo'] as $cap) {
|
|
$all_missing[$cap] = ($all_missing[$cap] ?? 0) + 1;
|
|
}
|
|
}
|
|
arsort($all_missing);
|
|
$top_gaps = [];
|
|
foreach ($all_missing as $cap => $n) {
|
|
if (isset($checklist[$cap])) {
|
|
$top_gaps[] = [
|
|
'capability' => $cap,
|
|
'name' => $checklist[$cap]['name'],
|
|
'dev_days' => $checklist[$cap]['dev_days'],
|
|
'critical' => $checklist[$cap]['critical'],
|
|
'missing_in_solutions' => $n,
|
|
];
|
|
}
|
|
}
|
|
|
|
echo json_encode([
|
|
'ok' => true,
|
|
'wave' => 252,
|
|
'ts' => date('c'),
|
|
'solutions_count' => count($solutions),
|
|
'solutions' => $solutions,
|
|
'top_gaps' => array_slice($top_gaps, 0, 15),
|
|
'mup_checklist' => $checklist,
|
|
'summary' => [
|
|
'ship_it' => count(array_filter($solutions, function($s){return $s['decision']==='SHIP_IT';})),
|
|
'accelerate' => count(array_filter($solutions, function($s){return $s['decision']==='ACCELERATE';})),
|
|
'dev_sprint' => count(array_filter($solutions, function($s){return $s['decision']==='DEV_SPRINT';})),
|
|
'nurture' => count(array_filter($solutions, function($s){return $s['decision']==='NURTURE';})),
|
|
'pivot' => count(array_filter($solutions, function($s){return $s['decision']==='PIVOT_OR_PARK';})),
|
|
'total_mad_pipeline' => array_sum(array_column($solutions, 'mad_est')),
|
|
'total_dev_days' => array_sum(array_map(function($s){return $s['dev_effort']['total_dev_days'];}, $solutions)),
|
|
'total_dev_cost_mad' => array_sum(array_map(function($s){return $s['dev_effort']['dev_cost_mad'];}, $solutions)),
|
|
],
|
|
], JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'checklist') {
|
|
echo json_encode(['ok'=>true, 'wave'=>252, 'checklist'=>mup_checklist()], JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
http_response_code(400);
|
|
echo json_encode(['error'=>'unknown action']);
|