Files
boc/EOS/decision-provenance.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

193 lines
5.3 KiB
JavaScript

#!/usr/bin/env node
// ═══════════════════════════════════════════════════════════════════════════
// Decision Provenance — Komplett beslutsunderlag för varje EOS-beslut
// ═══════════════════════════════════════════════════════════════════════════
import { createHash } from 'crypto';
class DecisionProvenance {
constructor() {
this.decisions = [];
}
/**
* Skapa ett komplett beslutsunderlag
*/
record(task, runtimeResult, trace) {
const decision = {
// Identitet
id: this.generateId(task, runtimeResult),
timestamp: new Date().toISOString(),
// Task
task: {
description: task.description,
type: task.type,
action: task.action,
target: task.target
},
// Runtime-version
runtime: {
version: 'v10',
contract: '1.0',
evidence_resolver: 'v5',
policy_engine: 'v4'
},
// Kontext
context: {
sources: trace.nodes
.filter(n => n.phase === 'CONTEXT')
.map(n => n.result),
git: trace.nodes
.find(n => n.phase === 'CONTEXT')?.result?.git || null
},
// Minne
memory: {
entries: trace.nodes
.filter(n => n.phase === 'MEMORY')
.map(n => n.result),
relevant_decisions: trace.nodes
.find(n => n.phase === 'MEMORY')?.result?.results?.relevantDecisions || []
},
// Knowledge Graph
knowledge: {
graph_version: '1.0',
capabilities: trace.nodes
.find(n => n.phase === 'KNOWLEDGE')?.result?.results?.relatedCapabilities || []
},
// Intent Resolution
intent: {
candidates: trace.nodes
.find(n => n.phase === 'INTENT')?.result?.candidates || [],
resolution: trace.nodes
.find(n => n.phase === 'INTENT')?.result?.resolution,
confidence: trace.nodes
.find(n => n.phase === 'INTENT')?.result?.confidence,
evidence_quality: trace.nodes
.find(n => n.phase === 'INTENT')?.result?.evidenceQuality
},
// Policy
policies: {
checked: trace.nodes
.filter(n => n.phase === 'EOS')
.map(n => ({
policy: n.result?.blockedBy,
passed: n.result?.passed,
reason: n.result?.reason
})),
registry_version: '1.0'
},
// Beslut
decision: {
operation: runtimeResult.status === 'blocked' ? 'BLOCKED' : 'ALLOWED',
reason: runtimeResult.reason || null,
policy: runtimeResult.policy || null,
evidence: runtimeResult.evidence || null
},
// Confidence
confidence: trace.nodes
.find(n => n.phase === 'INTENT')?.result?.confidence || 0,
// Review (placeholder — fylls i av Reviewer)
review: {
reviewer: null,
approved: null,
comments: []
},
// Commit (placeholder — fylls i vid commit)
commit: {
hash: null,
branch: null,
message: null
},
// Runtime Contract
runtime_contract: {
version: '1.0',
invariants_verified: true,
architecture_drift: 0
},
// Replay-hash
replay_hash: this.generateReplayHash(task, runtimeResult)
};
this.decisions.push(decision);
return decision;
}
generateId(task, result) {
const data = `${task.description}-${Date.now()}`;
return createHash('sha256').update(data).digest('hex').substring(0, 16);
}
generateReplayHash(task, result) {
const data = JSON.stringify({
task: task.description,
status: result.status,
policy: result.policy,
timestamp: new Date().toISOString()
});
return createHash('sha256').update(data).digest('hex');
}
/**
* Exportera beslut till fil
*/
export(decisionId, path) {
const decision = this.decisions.find(d => d.id === decisionId);
if (!decision) return null;
const fs = require('fs');
const filePath = path || `./provenance/decision-${decisionId}.json`;
fs.mkdirSync('./provenance', { recursive: true });
fs.writeFileSync(filePath, JSON.stringify(decision, null, 2));
return filePath;
}
/**
* Hämta statistik
*/
getStats() {
const total = this.decisions.length;
const blocked = this.decisions.filter(d => d.decision.operation === 'BLOCKED').length;
const allowed = total - blocked;
const avgConfidence = total > 0
? this.decisions.reduce((sum, d) => sum + d.confidence, 0) / total
: 0;
return {
total,
blocked,
allowed,
blockedPercentage: total > 0 ? Math.round((blocked / total) * 100) : 0,
avgConfidence: Math.round(avgConfidence * 100) / 100
};
}
/**
* Lista alla beslut
*/
list() {
return this.decisions.map(d => ({
id: d.id,
timestamp: d.timestamp,
operation: d.decision.operation,
confidence: d.confidence
}));
}
}
export { DecisionProvenance };