05ed037fe8
- 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!
187 lines
5.4 KiB
JavaScript
187 lines
5.4 KiB
JavaScript
#!/usr/bin/env node
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// Architecture Drift Detector — Verifierar att implementation följer kontrakt
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
import { readFileSync } from 'fs';
|
|
|
|
/**
|
|
* Kontraktets invariants
|
|
*/
|
|
const CONTRACT = {
|
|
executionOrder: [
|
|
'PLANNER',
|
|
'CONTEXT',
|
|
'MEMORY',
|
|
'KNOWLEDGE',
|
|
'INTENT',
|
|
'EOS',
|
|
'DEVELOPER',
|
|
'REVIEWER',
|
|
'COMMIT',
|
|
'OPERATOR'
|
|
],
|
|
|
|
requiredPhases: ['PLANNER', 'EOS', 'DEVELOPER'],
|
|
|
|
forbiddenTransitions: [
|
|
{ from: 'DEVELOPER', to: 'EOS', reason: 'EOS måste alltid köras före Developer' }
|
|
],
|
|
|
|
requiredChecks: {
|
|
'EOS': ['passed', 'blockedBy', 'reason']
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Extrahera exekveringsordning från Runtime-kod
|
|
*/
|
|
function extractExecutionOrder(runtimeCode) {
|
|
const order = [];
|
|
const methodPattern = /async run(\w+)\(/g;
|
|
let match;
|
|
|
|
while ((match = methodPattern.exec(runtimeCode)) !== null) {
|
|
const phase = match[1].toUpperCase();
|
|
if (!order.includes(phase)) {
|
|
order.push(phase);
|
|
}
|
|
}
|
|
|
|
return order;
|
|
}
|
|
|
|
/**
|
|
* Verifiera exekveringsordning
|
|
*/
|
|
function verifyExecutionOrder(actualOrder) {
|
|
const errors = [];
|
|
|
|
// Kontrollera att alla obligatoriska faser finns
|
|
for (const required of CONTRACT.requiredPhases) {
|
|
// EOS kan heta EOS eller EOSCHECK
|
|
const found = actualOrder.some(phase =>
|
|
phase === required ||
|
|
(required === 'EOS' && phase.includes('EOS'))
|
|
);
|
|
if (!found) {
|
|
errors.push(`Missing required phase: ${required}`);
|
|
}
|
|
}
|
|
|
|
// Kontrollera att ordningen stämmer med kontraktet
|
|
let contractIndex = 0;
|
|
for (const phase of actualOrder) {
|
|
const expectedIndex = CONTRACT.executionOrder.indexOf(phase);
|
|
if (expectedIndex === -1) continue; // Okänd fas, hoppa över
|
|
|
|
if (expectedIndex < contractIndex) {
|
|
errors.push(`Phase ${phase} is out of order. Expected after ${CONTRACT.executionOrder[contractIndex]}`);
|
|
} else {
|
|
contractIndex = expectedIndex;
|
|
}
|
|
}
|
|
|
|
// Kontrollera förbjudna övergångar
|
|
for (let i = 0; i < actualOrder.length - 1; i++) {
|
|
const from = actualOrder[i];
|
|
const to = actualOrder[i + 1];
|
|
|
|
for (const forbidden of CONTRACT.forbiddenTransitions) {
|
|
if (from === forbidden.from && to === forbidden.to) {
|
|
errors.push(`Forbidden transition: ${from} → ${to}. ${forbidden.reason}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
/**
|
|
* Verifiera att EOS-check alltid körs före Developer
|
|
*/
|
|
function verifyEOSBeforeDeveloper(runtimeCode) {
|
|
const errors = [];
|
|
|
|
// Hitta runEOSCheck och runDeveloper
|
|
const eosIndex = runtimeCode.indexOf('runEOSCheck');
|
|
const devIndex = runtimeCode.indexOf('runDeveloper');
|
|
|
|
if (eosIndex === -1) {
|
|
errors.push('runEOSCheck not found in Runtime');
|
|
}
|
|
|
|
if (devIndex === -1) {
|
|
errors.push('runDeveloper not found in Runtime');
|
|
}
|
|
|
|
if (eosIndex !== -1 && devIndex !== -1 && eosIndex > devIndex) {
|
|
errors.push('runEOSCheck must be called before runDeveloper');
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
/**
|
|
* Verifiera att Runtime inte har direktåtkomst till Policy Registry
|
|
*/
|
|
function verifyMemoryIsolation(runtimeCode) {
|
|
const errors = [];
|
|
|
|
// Kontrollera att Runtime inte modifierar Policy Registry direkt
|
|
if (runtimeCode.includes('policyRegistry.') && runtimeCode.includes('=')) {
|
|
errors.push('Runtime may not modify Policy Registry directly');
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
/**
|
|
* Huvudfunktion för drift-detektering
|
|
*/
|
|
export function detectArchitectureDrift(runtimePath) {
|
|
const runtimeCode = readFileSync(runtimePath, 'utf8');
|
|
|
|
const executionOrder = extractExecutionOrder(runtimeCode);
|
|
const orderErrors = verifyExecutionOrder(executionOrder);
|
|
const eosErrors = verifyEOSBeforeDeveloper(runtimeCode);
|
|
const isolationErrors = verifyMemoryIsolation(runtimeCode);
|
|
|
|
const allErrors = [...orderErrors, ...eosErrors, ...isolationErrors];
|
|
|
|
return {
|
|
drift: allErrors.length,
|
|
errors: allErrors,
|
|
executionOrder,
|
|
passed: allErrors.length === 0
|
|
};
|
|
}
|
|
|
|
/**
|
|
* CLI-användning
|
|
*/
|
|
if (process.argv[1] === new URL(import.meta.url).pathname) {
|
|
const runtimePath = process.argv[2] || './agent-runtime-v10.mjs';
|
|
|
|
console.log('═══════════════════════════════════════════════════════════════');
|
|
console.log(' ARCHITECTURE DRIFT DETECTOR');
|
|
console.log('═══════════════════════════════════════════════════════════════\n');
|
|
|
|
const result = detectArchitectureDrift(runtimePath);
|
|
|
|
console.log(`Runtime: ${runtimePath}`);
|
|
console.log(`Execution Order: ${result.executionOrder.join(' → ')}`);
|
|
console.log(`Drift: ${result.drift}`);
|
|
|
|
if (result.errors.length > 0) {
|
|
console.log('\n❌ ERRORS:');
|
|
for (const error of result.errors) {
|
|
console.log(` - ${error}`);
|
|
}
|
|
process.exit(1);
|
|
} else {
|
|
console.log('\n✅ No architecture drift detected');
|
|
process.exit(0);
|
|
}
|
|
}
|