Files
boc/SIL/validation/cross-project-metrics.mjs
T
Bernt 05ed037fe8 pilot.landvex.com: HTTPS + Full Stack Verified
- DNS: pilot.landvex.com -> 16.170.83.169
- TLS: Let's Encrypt certificate (expires 2026-09-30)
- Nginx: reverse proxy with SSL termination
- API: https://pilot.landvex.com/api/v1/missions
- UI: https://pilot.landvex.com/
- Upload: POST /api/v1/missions/import (multipart/form-data)

Verified:
 https://pilot.landvex.com/health
 https://pilot.landvex.com/version
 https://pilot.landvex.com/api/v1/missions (list)
 https://pilot.landvex.com/api/v1/missions/:id (get)
 POST /api/v1/missions/import (video upload)
 UI loads with title 'LandveX Intelligence Lab'

Next: Pilot 001 — Break the system!
2026-07-02 17:34:19 +00:00

158 lines
6.2 KiB
JavaScript

#!/usr/bin/env node
// ═══════════════════════════════════════════════════════════════════════════
// SIL Validation — Tvärsnittsanalys över projekt
// Jämför SIL:s prestanda mellan olika projekt
// ═══════════════════════════════════════════════════════════════════════════
import { readFileSync, existsSync } from 'fs';
const EXTERNAL_RESULTS = '/home/bernt/.openclaw/workspace/SIL/validation/external-results.jsonl';
const INTERNAL_RESULTS = '/home/bernt/.openclaw/workspace/SIL/gold-set.jsonl';
class CrossProjectMetrics {
constructor() {
this.external = this.loadLog(EXTERNAL_RESULTS);
this.internal = this.loadLog(INTERNAL_RESULTS);
}
loadLog(path) {
if (!existsSync(path)) return [];
return readFileSync(path, 'utf8')
.split('\n')
.filter(line => line.trim())
.map(line => {
try { return JSON.parse(line); } catch { return null; }
})
.filter(Boolean);
}
/**
* Jämför interna vs externa projekt
*/
compareInternalVsExternal() {
const internalMetrics = this.calculateMetrics(this.internal, 'internal');
const externalMetrics = this.calculateMetrics(this.external, 'external');
return {
internal: internalMetrics,
external: externalMetrics,
comparison: {
recallDiff: externalMetrics.avgRecall - internalMetrics.avgRecall,
precisionDiff: externalMetrics.avgPrecision - internalMetrics.avgPrecision,
// Om externa projekt presterar nästan lika bra → SIL fångar generella mönster
generalizesWell: Math.abs(externalMetrics.avgRecall - internalMetrics.avgRecall) < 15
}
};
}
calculateMetrics(entries, type) {
if (entries.length === 0) {
return { count: 0, avgRecall: 0, avgPrecision: 0 };
}
const recalls = entries
.map(e => parseFloat(e.recall) || 0)
.filter(r => !isNaN(r));
const precisions = entries
.map(e => parseFloat(e.precision) || 0)
.filter(p => !isNaN(p));
return {
count: entries.length,
avgRecall: Math.round(recalls.reduce((a, b) => a + b, 0) / recalls.length),
avgPrecision: Math.round(precisions.reduce((a, b) => a + b, 0) / precisions.length),
type
};
}
/**
* Analysera vilka typer av projekt SIL klarar bäst/sämst
*/
analyzeByProjectType() {
const byLanguage = {};
for (const entry of this.external) {
const lang = entry.language || 'unknown';
if (!byLanguage[lang]) {
byLanguage[lang] = { count: 0, recalls: [], precisions: [] };
}
byLanguage[lang].count++;
if (entry.recall !== undefined) byLanguage[lang].recalls.push(entry.recall);
if (entry.precision !== undefined) byLanguage[lang].precisions.push(entry.precision);
}
return Object.entries(byLanguage).map(([lang, data]) => ({
language: lang,
count: data.count,
avgRecall: Math.round(data.recalls.reduce((a, b) => a + b, 0) / data.recalls.length),
avgPrecision: Math.round(data.precisions.reduce((a, b) => a + b, 0) / data.precisions.length)
}));
}
/**
* Generera rapport
*/
generateReport() {
const comparison = this.compareInternalVsExternal();
const byType = this.analyzeByProjectType();
const report = {
timestamp: new Date().toISOString(),
comparison,
byType,
conclusion: comparison.comparison.generalizesWell
? 'SIL generaliserar väl — fångar generella mönster'
: 'SIL är för specialiserad för internt projekt'
};
this.printReport(report);
return report;
}
printReport(report) {
console.log('╔═══════════════════════════════════════════════════════════════╗');
console.log('║ CROSS-PROJECT ANALYSIS ║');
console.log('║ Jämför SIL över olika projekt ║');
console.log('╚═══════════════════════════════════════════════════════════════╝');
console.log();
console.log('📊 INTERNT VS EXTERNT\n');
console.log(` Internt:`);
console.log(` PR:er: ${report.comparison.internal.count}`);
console.log(` Recall: ${report.comparison.internal.avgRecall}%`);
console.log(` Precision: ${report.comparison.internal.avgPrecision}%`);
console.log();
console.log(` Externt:`);
console.log(` Projekt: ${report.comparison.external.count}`);
console.log(` Recall: ${report.comparison.external.avgRecall}%`);
console.log(` Precision: ${report.comparison.external.avgPrecision}%`);
console.log();
console.log(` Skillnad:`);
console.log(` Recall: ${report.comparison.comparison.recallDiff > 0 ? '+' : ''}${report.comparison.comparison.recallDiff}%`);
console.log(` Precision: ${report.comparison.comparison.precisionDiff > 0 ? '+' : ''}${report.comparison.comparison.precisionDiff}%`);
console.log();
console.log('📋 PER SPRÅK\n');
for (const lang of report.byType) {
console.log(` ${lang.language}:`);
console.log(` Projekt: ${lang.count}`);
console.log(` Recall: ${lang.avgRecall}%`);
console.log(` Precision: ${lang.avgPrecision}%`);
}
console.log();
console.log('🎯 SLUTSATS\n');
console.log(` ${report.conclusion}`);
console.log();
console.log('═══════════════════════════════════════════════════════════════\n');
}
}
// ── Main ──────────────────────────────────────────────────────────────────
const metrics = new CrossProjectMetrics();
metrics.generateReport();