Files
boc/EOS/engineering-readiness.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

320 lines
11 KiB
JavaScript

#!/usr/bin/env node
// ═══════════════════════════════════════════════════════════════════════════
// EOS Engineering Readiness — Mognadsindex per område
// Erik-krav: "EOS borde inte bara säga STOP. Det borde också kunna ge ett mognadsindex."
// ═══════════════════════════════════════════════════════════════════════════
import { execSync } from 'child_process';
import { readFileSync, existsSync, writeFileSync } from 'fs';
const READINESS_PATH = '/home/bernt/.openclaw/workspace/EOS/readiness-report.json';
/**
* Områden och deras mätningar
*/
const READINESS_AREAS = [
{
id: 'git-discipline',
name: 'Git-disciplin',
weight: 1.0,
measures: [
{
name: 'Ocommitade filer',
check: () => {
try {
const status = execSync('git status --short', {
cwd: '/home/bernt/repos/quixzoom.com',
encoding: 'utf8'
});
const uncommitted = status.trim().split('\n').filter(l => l.trim()).length;
// 0 filer = 100%, 100+ filer = 0%
const score = Math.max(0, 100 - uncommitted);
return { score, evidence: `${uncommitted} ocommitade filer` };
} catch {
return { score: 0, evidence: 'Kunde inte köra git status' };
}
}
},
{
name: 'Git-konfiguration',
check: () => {
const hasGit = existsSync('/home/bernt/repos/quixzoom.com/.git');
return { score: hasGit ? 100 : 0, evidence: hasGit ? 'Git-repo hittat' : 'Inget Git-repo' };
}
}
]
},
{
id: 'deployment',
name: 'Deployment',
weight: 1.0,
measures: [
{
name: 'CI/CD pipeline',
check: () => {
const hasCI = existsSync('/home/bernt/repos/quixzoom.com/.github/workflows') ||
existsSync('/home/bernt/repos/quixzoom.com/.gitlab-ci.yml');
return { score: hasCI ? 100 : 0, evidence: hasCI ? 'CI/CD hittad' : 'Ingen CI/CD' };
}
},
{
name: 'Deployment-script',
check: () => {
const hasDeploy = existsSync('/home/bernt/repos/quixzoom.com/deploy.sh') ||
existsSync('/home/bernt/repos/quixzoom.com/scripts/deploy.sh');
return { score: hasDeploy ? 100 : 0, evidence: hasDeploy ? 'Deploy-script hittat' : 'Inget deploy-script' };
}
}
]
},
{
id: 'infrastructure',
name: 'Infrastruktur',
weight: 1.0,
measures: [
{
name: 'Infrastructure as Code',
check: () => {
const hasIaC = existsSync('/home/bernt/repos/quixzoom.com/terraform') ||
existsSync('/home/bernt/repos/quixzoom.com/cloudformation') ||
existsSync('/home/bernt/repos/quixzoom.com/cdk');
return { score: hasIaC ? 100 : 0, evidence: hasIaC ? 'IaC hittad' : 'Ingen IaC' };
}
},
{
name: 'Miljökonfiguration',
check: () => {
const hasEnv = existsSync('/home/bernt/repos/quixzoom.com/.env.example');
return { score: hasEnv ? 100 : 0, evidence: hasEnv ? '.env.example hittad' : 'Ingen .env.example' };
}
}
]
},
{
id: 'api-contract',
name: 'API-kontrakt',
weight: 0.8,
measures: [
{
name: 'OpenAPI-spec',
check: () => {
const hasOpenAPI = existsSync('/home/bernt/repos/quixzoom.com/openapi.yaml') ||
existsSync('/home/bernt/repos/quixzoom.com/openapi.json');
return { score: hasOpenAPI ? 100 : 0, evidence: hasOpenAPI ? 'OpenAPI hittad' : 'Ingen OpenAPI' };
}
},
{
name: 'API-dokumentation',
check: () => {
const hasDocs = existsSync('/home/bernt/repos/quixzoom.com/API.md') ||
existsSync('/home/bernt/repos/quixzoom.com/docs/api.md');
return { score: hasDocs ? 100 : 0, evidence: hasDocs ? 'API-docs hittad' : 'Ingen API-dokumentation' };
}
}
]
},
{
id: 'testability',
name: 'Testbarhet',
weight: 1.0,
measures: [
{
name: 'Tester',
check: () => {
const hasTests = existsSync('/home/bernt/repos/quixzoom.com/tests') ||
existsSync('/home/bernt/repos/quixzoom.com/__tests__');
return { score: hasTests ? 100 : 0, evidence: hasTests ? 'Tester hittade' : 'Inga tester' };
}
},
{
name: 'Test-konfiguration',
check: () => {
const hasConfig = existsSync('/home/bernt/repos/quixzoom.com/jest.config.js') ||
existsSync('/home/bernt/repos/quixzoom.com/vitest.config.ts');
return { score: hasConfig ? 100 : 0, evidence: hasConfig ? 'Test-config hittad' : 'Ingen test-config' };
}
}
]
},
{
id: 'observability',
name: 'Observerbarhet',
weight: 0.8,
measures: [
{
name: 'Loggning',
check: () => {
const hasLogging = existsSync('/home/bernt/repos/quixzoom.com/src/lib/logger.ts') ||
existsSync('/home/bernt/repos/quixzoom.com/src/utils/logger.js');
return { score: hasLogging ? 100 : 0, evidence: hasLogging ? 'Logger hittad' : 'Ingen logger' };
}
},
{
name: 'Health checks',
check: () => {
const hasHealth = existsSync('/home/bernt/repos/quixzoom.com/src/routes/health.ts') ||
existsSync('/home/bernt/repos/quixzoom.com/src/api/health.js');
return { score: hasHealth ? 100 : 0, evidence: hasHealth ? 'Health endpoint hittad' : 'Ingen health endpoint' };
}
}
]
},
{
id: 'agent-safety',
name: 'Agent Safety',
weight: 1.0,
measures: [
{
name: 'EOS-kontrakt',
check: () => {
const hasContract = existsSync('/home/bernt/.openclaw/workspace/EOS/engineering-contract.mjs');
return { score: hasContract ? 100 : 0, evidence: hasContract ? 'EOS-kontrakt hittat' : 'Inget EOS-kontrakt' };
}
},
{
name: 'Immutable Truth',
check: () => {
const hasTruth = existsSync('/home/bernt/.openclaw/workspace/EOS/immutable-truth.mjs');
return { score: hasTruth ? 100 : 0, evidence: hasTruth ? 'Immutable Truth hittad' : 'Ingen Immutable Truth' };
}
}
]
}
];
class EngineeringReadiness {
constructor() {
this.areas = READINESS_AREAS;
}
/**
* Beräkna mognadsindex för alla områden
*/
calculate() {
console.log('📊 Beräknar Engineering Readiness...\n');
const results = [];
let totalScore = 0;
let totalWeight = 0;
for (const area of this.areas) {
const areaResult = this.calculateArea(area);
results.push(areaResult);
totalScore += areaResult.score * area.weight;
totalWeight += area.weight;
}
const overallScore = Math.round(totalScore / totalWeight);
const report = {
timestamp: new Date().toISOString(),
overall: overallScore,
areas: results,
// Thresholds
thresholds: {
critical: 80, // Under 80% = STOP
warning: 60 // Under 60% = ESCALATE
},
// Status
status: overallScore >= 80 ? 'READY' :
overallScore >= 60 ? 'WARNING' : 'CRITICAL'
};
this.saveReport(report);
this.printReport(report);
return report;
}
calculateArea(area) {
let areaScore = 0;
const measures = [];
for (const measure of area.measures) {
const result = measure.check();
measures.push({
name: measure.name,
score: result.score,
evidence: result.evidence
});
areaScore += result.score;
}
const avgScore = Math.round(areaScore / area.measures.length);
return {
id: area.id,
name: area.name,
weight: area.weight,
score: avgScore,
measures,
status: avgScore >= 80 ? 'READY' :
avgScore >= 60 ? 'WARNING' : 'CRITICAL'
};
}
saveReport(report) {
writeFileSync(READINESS_PATH, JSON.stringify(report, null, 2));
}
printReport(report) {
console.log('╔═══════════════════════════════════════════════════════════════╗');
console.log('║ ENGINEERING READINESS ║');
console.log('║ Mognadsindex per område ║');
console.log('╚═══════════════════════════════════════════════════════════════╝');
console.log();
// Overall
const overallIcon = report.status === 'READY' ? '✅' :
report.status === 'WARNING' ? '⚠️' : '🔴';
console.log(`${overallIcon} ÖVERGRIAPANDE: ${report.overall}%`);
console.log(` Status: ${report.status}`);
console.log();
// Per område
console.log('📋 PER OMRÅDE\n');
for (const area of report.areas) {
const icon = area.status === 'READY' ? '✅' :
area.status === 'WARNING' ? '⚠️' : '🔴';
const bar = '█'.repeat(area.score / 5) + '░'.repeat(20 - area.score / 5);
console.log(`${icon} ${area.name}`);
console.log(` [${bar}] ${area.score}%`);
console.log(` Vikt: ${area.weight}x`);
for (const measure of area.measures) {
const mIcon = measure.score >= 80 ? '✅' : measure.score >= 60 ? '⚠️' : '❌';
console.log(` ${mIcon} ${measure.name}: ${measure.score}% (${measure.evidence})`);
}
console.log();
}
// Thresholds
console.log('🎯 TRÖSKLAR\n');
console.log(` Critical: <${report.thresholds.critical}% (STOP)`);
console.log(` Warning: <${report.thresholds.warning}% (ESCALATE)`);
console.log(` Ready: ≥${report.thresholds.critical}%`);
console.log();
// Rekommendation
console.log('💡 REKOMMENDATION\n');
if (report.status === 'CRITICAL') {
console.log(' 🔴 STOP — Agenter får INTE utföra ändringar');
console.log(' Åtgärda critical-områden innan fortsatt arbete');
} else if (report.status === 'WARNING') {
console.log(' ⚠️ WARNING — Begränsad agent-aktivitet');
console.log(' Agenter får endast göra säkra ändringar');
} else {
console.log(' ✅ READY — Agenter kan arbeta normalt');
}
console.log();
console.log('═══════════════════════════════════════════════════════════════\n');
}
}
// ── Main ──────────────────────────────────────────────────────────────────
const readiness = new EngineeringReadiness();
readiness.calculate();