2026-09-02 12:55:53 -04:00
import { writeFileSync } from 'node:fs'
const C = {
ink : '#1B2331' , ink2 : '#4A5568' , ink3 : '#7A8494' , line : '#B9C2CE' ,
teal : '#2C6B70' , tealSoft : '#E4F0F0' , amber : '#B86A14' , amberSoft : '#F7ECDD' ,
slate : '#EEF1F5' , red : '#A23B3B' , redSoft : '#F6E3E3' , white : '#FFFFFF' ,
}
const FONT = 'font-family="Helvetica Neue,Helvetica,Arial,sans-serif"'
const esc = ( s ) => s . replace ( /&/g , '&' ) . replace ( /</g , '<' ) . replace ( />/g , '>' )
function text ( x , y , s , { size = 13 , fill = C . ink , anchor = 'middle' , weight = 400 , mono = false , italic = false } = { } ) {
const fam = mono ? 'font-family="Menlo,SFMono-Regular,Consolas,monospace"' : ''
return ` <text x=" ${ x } " y=" ${ y } " ${ fam } font-size=" ${ size } " fill=" ${ fill } " text-anchor=" ${ anchor } " font-weight=" ${ weight } " ${ italic ? ' font-style="italic"' : '' } > ${ esc ( s ) } </text> `
}
// title + sub lines centered in box
function box ( x , y , w , h , title , sub = [ ] , o = { } ) {
const { fill = C . white , stroke = C . line , sw = 1.2 , dash = '' , tcolor = C . ink , tsize = 14 , ssize = 12 , scolor = C . ink2 , r = 5 , tag = null , tagColor = C . amber , mono = false } = o
const lines = 1 + sub . length
const lh = 16
const total = tsize + 4 + sub . length * lh
let cy = y + h / 2 - total / 2 + tsize - 2
let s = ` <rect x=" ${ x } " y=" ${ y } " width=" ${ w } " height=" ${ h } " rx=" ${ r } " fill=" ${ fill } " stroke=" ${ stroke } " stroke-width=" ${ sw } " ${ dash ? ` stroke-dasharray=" ${ dash } " ` : '' } /> `
s += text ( x + w / 2 , cy , title , { size : tsize , fill : tcolor , weight : 600 , mono } )
for ( const l of sub ) { cy += lh ; s += text ( x + w / 2 , cy , l , { size : ssize , fill : scolor } ) }
if ( tag ) {
const tw = tag . length * 7 + 14
s += ` <rect x=" ${ x + w - tw - 6 } " y=" ${ y - 9 } " width=" ${ tw } " height="18" rx="9" fill=" ${ tagColor } "/> ` + text ( x + w - tw / 2 - 6 , y + 4 , tag , { size : 10 , fill : C . white , weight : 700 } )
}
return s
}
function group ( x , y , w , h , label , o = { } ) {
const { stroke = C . line , fill = 'none' , color = C . ink3 } = o
return ` <rect x=" ${ x } " y=" ${ y } " width=" ${ w } " height=" ${ h } " rx="8" fill=" ${ fill } " stroke=" ${ stroke } " stroke-width="1" stroke-dasharray="5 4"/> ` +
text ( x + 12 , y + 18 , label . toUpperCase ( ) , { size : 10.5 , fill : color , anchor : 'start' , weight : 700 } )
}
function arrow ( x1 , y1 , x2 , y2 , label = '' , o = { } ) {
const { dashed = false , both = false , color = C . ink2 , lx = 0 , ly = - 6 , path = null , lsize = 11.5 , lfill = C . ink2 } = o
const d = path || ` M ${ x1 } , ${ y1 } L ${ x2 } , ${ y2 } `
let s = ` <path d=" ${ d } " fill="none" stroke=" ${ color } " stroke-width="1.4" ${ dashed ? ' stroke-dasharray="5 4"' : '' } marker-end="url(#ah)" ${ both ? ' marker-start="url(#ahs)"' : '' } /> `
if ( label ) {
const mx = ( x1 + x2 ) / 2 + lx , my = ( y1 + y2 ) / 2 + ly
const tw = label . length * 6.4 + 8
s += ` <rect x=" ${ mx - tw / 2 } " y=" ${ my - 10 } " width=" ${ tw } " height="15" fill=" ${ C . white } " opacity=".92"/> ` + text ( mx , my + 2 , label , { size : lsize , fill : lfill } )
}
return s
}
function svg ( w , h , body ) {
return ` <!doctype html><body style="margin:0;background:#fff"><svg xmlns="http://www.w3.org/2000/svg" width=" ${ w } " height=" ${ h } " ${ FONT } >
< defs >
< marker id = "ah" markerWidth = "10" markerHeight = "10" refX = "9" refY = "5" orient = "auto" markerUnits = "userSpaceOnUse" > < path d = "M0,1 L9,5 L0,9 z" fill = "${C.ink2}" / > < / m a r k e r >
< marker id = "ahs" markerWidth = "10" markerHeight = "10" refX = "1" refY = "5" orient = "auto" markerUnits = "userSpaceOnUse" > < path d = "M10,1 L1,5 L10,9 z" fill = "${C.ink2}" / > < / m a r k e r >
< / d e f s >
< rect width = "${w}" height = "${h}" fill = "#fff" / >
$ { body }
< / s v g > < / b o d y > `
}
const legend = ( x , y , items ) => items . map ( ( [ fill , stroke , lbl ] , i ) =>
` <rect x=" ${ x + i * 150 } " y=" ${ y - 9 } " width="14" height="12" rx="2" fill=" ${ fill } " stroke=" ${ stroke } "/> ` + text ( x + i * 150 + 20 , y + 1 , lbl , { size : 11 , fill : C . ink2 , anchor : 'start' } ) ) . join ( '' )
const out = { }
// ---------- 1. system context ----------
{
let b = ''
b += group ( 30 , 40 , 250 , 420 , 'External systems' )
const ext = [ [ 'Trading platform' , [ 'bids · clearing · disclosures' ] ] , [ 'Dispatch / load mgmt' , [ 'regulation requests' ] ] , [ 'Metering / settlement' , [ 'meter data · statements' ] ] , [ 'Weather & market data' , [ 'read-only feeds' ] ] ]
ext . forEach ( ( [ t , s ] , i ) => { b += box ( 50 , 70 + i * 95 , 210 , 70 , t , s , { fill : C . slate } ) } )
b += group ( 330 , 40 , 600 , 490 , 'VPP AI Platform' )
b += box ( 360 , 70 , 540 , 90 , 'Cognitive plane' , [ 'five LLM agents · agent runtime · workflows' , 'minutes to days' ] , { fill : C . amberSoft , stroke : C . amber , tag : 'LLM' } )
b += box ( 360 , 215 , 540 , 90 , 'Safety chain' , [ 'rule check → simulation → envelope / human approval → fresh check → permit' , 'deterministic · the only path between planes' ] , { fill : C . tealSoft , stroke : C . teal , tag : 'NO LLM' , tagColor : C . teal } )
b += box ( 360 , 360 , 540 , 80 , 'Control plane' , [ 'execution engine · edge terminals · seconds' ] , { fill : C . tealSoft , stroke : C . teal , tag : 'NO LLM' , tagColor : C . teal } )
b += box ( 360 , 465 , 540 , 45 , 'Data foundation' , [ ] , { fill : C . slate , tsize : 13 } )
b += text ( 630 , 500 , 'time-series · relational · knowledge/vector · feature store · event log' , { size : 11.5 , fill : C . ink2 } )
b += arrow ( 630 , 160 , 630 , 215 , 'Proposal (digest + lineage)' , { lx : 110 } )
b += arrow ( 630 , 305 , 630 , 360 , '(Proposal, ExecutionPermit)' , { lx : 110 } )
b += arrow ( 280 , 115 , 360 , 115 , '' , { both : true } )
b += arrow ( 360 , 260 , 280 , 100 , '' , { path : 'M360,260 L300,260 L300,130 L280,130' } )
b += text ( 292 , 253 , 'bid + permit' , { size : 10.5 , fill : C . ink2 , anchor : 'end' } )
b += arrow ( 900 , 400 , 980 , 400 , '' )
b += arrow ( 980 , 125 , 900 , 125 , '' , { both : true } )
b += box ( 980 , 90 , 190 , 70 , 'Operators' , [ 'Case Desk · approvals' , 'scenario forks · AI cards' ] , { fill : C . white } )
b += box ( 980 , 360 , 190 , 80 , 'Flexible resources' , [ 'PV · storage · EV charging' , 'HVAC · industrial · microgrids' ] , { fill : C . white } )
b += arrow ( 630 , 440 , 630 , 465 )
b += arrow ( 360 , 400 , 360 , 140 , '' , { path : 'M360,400 L345,400 L345,140 L360,140' , dashed : true } )
b += ` <text transform="translate(340,330) rotate(-90)" font-size="10.5" fill=" ${ C . ink2 } " text-anchor="middle">execution feedback</text> `
b += text ( 1075 , 470 , '31 MW today → ≥ 80 MW target' , { size : 11 , fill : C . ink3 } )
out [ '01-system-context' ] = svg ( 1200 , 550 , b )
}
// ---------- 2. agent runtime ----------
{
let b = ''
b += group ( 30 , 40 , 250 , 330 , 'Task triggers' )
b += box ( 50 , 70 , 210 , 70 , 'Scheduled' , [ 'day-ahead 06:00 · bid window · D+1 review' ] , { fill : C . tealSoft , stroke : C . teal } )
b += box ( 50 , 165 , 210 , 70 , 'Event' , [ 'deviation · resource offline · clearing' ] , { fill : C . tealSoft , stroke : C . teal } )
b += box ( 50 , 260 , 210 , 70 , 'Manual' , [ 'operator question or task' ] , { fill : C . slate } )
b += box ( 330 , 260 , 190 , 70 , 'Router agent' , [ 'intent → template id + params' , 'schema-constrained enum' ] , { fill : C . amberSoft , stroke : C . amber , tag : 'LLM' } )
b += arrow ( 260 , 295 , 330 , 295 )
b += box ( 580 , 120 , 240 , 130 , 'Workflow engine' , [ 'deterministic step chains' , 'durable · suspend/resume' , 'one template per business process' ] , { fill : C . tealSoft , stroke : C . teal , tag : 'NO LLM' , tagColor : C . teal } )
b += arrow ( 260 , 105 , 580 , 160 , 'no LLM in the path' , { path : 'M260,105 L420,105 L420,160 L580,160' , lx : 110 , ly : - 37 } )
b += arrow ( 260 , 200 , 580 , 200 , 'rule-matched' , { lx : - 20 } )
b += arrow ( 520 , 295 , 580 , 240 , '' , { path : 'M520,295 L560,295 L560,240 L580,240' } )
b += box ( 880 , 70 , 260 , 80 , 'Agent step' , [ 'one of five agents, invoked at a step' , 'tools whitelisted per agent' ] , { fill : C . amberSoft , stroke : C . amber , tag : 'LLM' } )
b += arrow ( 820 , 150 , 880 , 110 , '' , { path : 'M820,150 L850,150 L850,110 L880,110' } )
b += box ( 880 , 190 , 260 , 80 , 'Tool registry' , [ 'typed, versioned skill contracts' , 'every call snapshotted into lineage' ] , { fill : C . tealSoft , stroke : C . teal } )
b += arrow ( 1010 , 150 , 1010 , 190 )
b += box ( 880 , 310 , 260 , 60 , 'Python skill services' , [ 'forecast · MILP · simulation' ] , { fill : C . slate } )
b += arrow ( 1010 , 270 , 1010 , 310 , 'HTTP / JSON' , { lx : 60 } )
b += box ( 580 , 300 , 240 , 70 , 'Proposal → safety chain' , [ 'numbers dereferenced from lineage' ] , { fill : C . white , stroke : C . ink2 } )
b += arrow ( 700 , 250 , 700 , 300 )
b += box ( 330 , 400 , 810 , 46 , 'Event log (event-sourced): triggers, steps, tool calls, transitions, approvals, permits, receipts' , [ ] , { fill : C . slate , tsize : 12.5 , dash : '' } )
b += arrow ( 700 , 370 , 700 , 400 , '' , { dashed : true } )
b += legend ( 30 , 445 , [ [ C . amberSoft , C . amber , 'LLM involved' ] , [ C . tealSoft , C . teal , 'Deterministic' ] ] )
b += text ( 30 , 480 , 'Workflows are the entry point, not agents. The LLM decides what to do (pick a template); the engine decides how, step by step.' , { size : 12 , fill : C . ink2 , anchor : 'start' , italic : true } )
out [ '02-agent-runtime' ] = svg ( 1200 , 500 , b )
}
// ---------- 3. context & memory ----------
{
let b = ''
2026-09-02 13:02:34 -04:00
b += group ( 30 , 40 , 1140 , 275 , 'Context assembly for one agent step · deterministic baseline + progressive disclosure' )
2026-09-02 12:55:53 -04:00
const src = [ [ 'Position ledger' , 'versioned read' ] , [ 'Time-series / snapshots' , 'immutable, checksummed' ] , [ 'Semantic memory' , 'lessons declared by template' ] , [ 'Knowledge base (RAG)' , 'effective rules only' ] ]
src . forEach ( ( [ t , s ] , i ) => { b += box ( 50 + i * 180 , 70 , 165 , 60 , t , [ s ] , { fill : C . slate , tsize : 12.5 , ssize : 11 } ) } )
2026-09-02 13:02:34 -04:00
b += box ( 50 , 170 , 705 , 55 , 'fetchContext step (deterministic, reproducible)' , [ 'compact baseline: summaries, identifiers, the few figures a step needs' ] , { fill : C . tealSoft , stroke : C . teal , tsize : 13 } )
b += box ( 50 , 240 , 705 , 55 , 'Agentic retrieval tools · read-only · whitelisted · every call logged into lineage' , [ 'rule retrieval · ledger read · snapshot fetch · forecast lookup — detail disclosed on demand, retrieved content tagged as data' ] , { fill : C . white , stroke : C . teal , tsize : 12.5 , ssize : 11 } )
b += arrow ( 755 , 267 , 790 , 267 , '' , { both : true } )
2026-09-02 12:55:53 -04:00
for ( let i = 0 ; i < 4 ; i ++ ) b += arrow ( 132 + i * 180 , 130 , 132 + i * 180 , 170 )
2026-09-02 13:02:34 -04:00
b += box ( 790 , 70 , 170 , 225 , 'Agent step' , [ 'prose + tool calls' , 'numbers only as' , '{toolCallId, path}' , 'references' ] , { fill : C . amberSoft , stroke : C . amber , tag : 'LLM' } )
2026-09-02 12:55:53 -04:00
b += arrow ( 755 , 197 , 790 , 197 )
2026-09-02 13:02:34 -04:00
b += box ( 995 , 70 , 160 , 225 , 'Assembler' , [ 'dereferences refs' , 'from recorded lineage' , '→ Proposal' , 'LLM text cannot' , 'carry a number' ] , { fill : C . tealSoft , stroke : C . teal } )
2026-09-02 12:55:53 -04:00
b += arrow ( 960 , 147 , 995 , 147 )
2026-09-02 13:02:34 -04:00
b += group ( 30 , 345 , 1140 , 165 , 'Three memory layers' )
b += box ( 50 , 380 , 355 , 105 , 'Working memory' , [ 'current task context, intermediate results' , 'runtime memory · task lifetime' , 'step-to-step parameters preferred over memory' ] , { fill : C . white } )
b += box ( 423 , 380 , 355 , 105 , 'Episodic memory' , [ 'operating states, trading, control, responses' , 'event log + time-series · permanent (audit)' , 'queried through fetchContext, not recalled' ] , { fill : C . white } )
b += box ( 796 , 380 , 355 , 105 , 'Semantic memory' , [ 'structured lessons from D+1 review' , 'knowledge + strategy store · versioned' , 'written by ReviewFinding, injected explicitly' ] , { fill : C . white } )
b += text ( 30 , 540 , 'Authoritative facts live in the domain database, never in agent memory (invariant I8). No automatic memory recall feeds production decisions.' , { size : 12 , fill : C . ink2 , anchor : 'start' , italic : true } )
out [ '03-context-memory' ] = svg ( 1200 , 560 , b )
2026-09-02 12:55:53 -04:00
}
// ---------- 4. safety chain state machine ----------
{
let b = ''
const Y = 120 , H = 56 , W = 118 , G = 32
const stages = [
[ 'DRAFT' , 'agent output' ] , [ 'RULE_CHECK' , 'policy pack' ] , [ 'SIMULATION' , 'backtest / balance' ] , [ 'ENVELOPE_CHECK' , 'within bounds?' ] ,
[ 'APPROVED' , 'auto or human' ] , [ 'FRESH_CHECK' , 're-verify state' ] , [ 'AUTHORIZED' , 'permit issued' ] , [ 'RELEASED' , 'gateway accepts' ] ,
]
const xs = stages . map ( ( _ , i ) => 30 + i * ( W + G ) )
stages . forEach ( ( [ t , s ] , i ) => {
const hi = i >= 1 && i <= 6
b += box ( xs [ i ] , Y , W , H , t , [ s ] , { fill : hi ? C . tealSoft : C . white , stroke : hi ? C . teal : C . line , tsize : 12 , ssize : 10.5 , mono : true } )
if ( i < stages . length - 1 ) b += arrow ( xs [ i ] + W , Y + H / 2 , xs [ i + 1 ] , Y + H / 2 )
} )
b += group ( xs [ 1 ] - 12 , 52 , xs [ 6 ] + W + 12 - xs [ 1 ] + 12 , 148 , 'Safety chain · deterministic · shared by every proposal type' , { stroke : C . teal , color : C . teal } )
// human branch
b += box ( xs [ 3 ] + 20 , 250 , 200 , 56 , 'PENDING_HUMAN' , [ 'suspended run · Case Desk inbox' ] , { fill : C . amberSoft , stroke : C . amber , tsize : 12 , ssize : 10.5 , mono : true } )
b += arrow ( xs [ 3 ] + W / 2 , Y + H , xs [ 3 ] + 60 , 250 , 'outside envelope' , { path : ` M ${ xs [ 3 ] + W / 2 } , ${ Y + H } L ${ xs [ 3 ] + W / 2 } ,250 ` , lx : - 70 , ly : 8 } )
b += arrow ( xs [ 2 ] + W / 2 , Y + H , xs [ 3 ] + 20 , 278 , 'sim warning' , { path : ` M ${ xs [ 2 ] + W / 2 } , ${ Y + H } L ${ xs [ 2 ] + W / 2 } ,278 L ${ xs [ 3 ] + 20 } ,278 ` , lx : - 45 , ly : 40 } )
b += arrow ( xs [ 3 ] + 220 , 278 , xs [ 4 ] + W / 2 , Y + H , 'human resume' , { path : ` M ${ xs [ 3 ] + 220 } ,278 L ${ xs [ 4 ] + W / 2 } ,278 L ${ xs [ 4 ] + W / 2 } , ${ Y + H } ` , lx : 40 , ly : 40 } )
b += text ( xs [ 4 ] + W / 2 + 16 , 300 , 'approver ≠ originator (I1)' , { size : 10.5 , fill : C . ink3 , anchor : 'start' } )
b += arrow ( xs [ 3 ] + W / 2 + 30 , Y , xs [ 4 ] + W / 2 - 30 , Y , 'within envelope → AUTO_APPROVED' , { path : ` M ${ xs [ 3 ] + W - 10 } , ${ Y } L ${ xs [ 3 ] + W - 10 } , ${ Y - 24 } L ${ xs [ 4 ] + 10 } , ${ Y - 24 } L ${ xs [ 4 ] + 10 } , ${ Y } ` , ly : - 26 , lx : 0 } )
// rejected
b += box ( xs [ 1 ] , 350 , W , 44 , 'REJECTED' , [ ] , { fill : C . redSoft , stroke : C . red , tsize : 12 , mono : true } )
b += arrow ( xs [ 1 ] + W / 2 , Y + H , xs [ 1 ] + W / 2 , 350 , 'rule failure' , { lx : 44 } )
b += arrow ( xs [ 3 ] + 60 , 306 , xs [ 1 ] + W , 372 , 'human rejects' , { path : ` M ${ xs [ 3 ] + 60 } ,306 L ${ xs [ 3 ] + 60 } ,372 L ${ xs [ 1 ] + W } ,372 ` , lx : - 30 , ly : 45 } )
// stale
b += box ( xs [ 5 ] , 350 , W , 44 , 'STALE' , [ ] , { fill : C . redSoft , stroke : C . red , tsize : 12 , mono : true } )
b += arrow ( xs [ 5 ] + W / 2 , Y + H , xs [ 5 ] + W / 2 , 350 , 'evidence changed' , { lx : 60 } )
b += arrow ( xs [ 5 ] + W / 2 , 350 , xs [ 1 ] + W - 15 , Y + H , 're-enter chain' , { path : ` M ${ xs [ 5 ] + W / 2 - 30 } ,350 L ${ xs [ 5 ] + W / 2 - 30 } ,330 L ${ xs [ 1 ] + W - 15 } ,330 L ${ xs [ 1 ] + W - 15 } , ${ Y + H + 2 } ` , dashed : true , lx : - 172 , ly : 59 } )
// after release
b += text ( xs [ 7 ] + W / 2 , 210 , 'EXECUTING → COMPLETED' , { size : 11 , fill : C . ink2 , mono : true } )
b += text ( xs [ 7 ] + W / 2 , 226 , 'or ROLLED_BACK' , { size : 11 , fill : C . ink2 , mono : true } )
b += text ( xs [ 7 ] + W / 2 , 244 , '→ ExecutionReport → review' , { size : 10.5 , fill : C . ink3 } )
// notes
b += text ( 30 , 430 , 'Approval binds to the proposal digest and a validity window (I3). Gateways accept only (Proposal, ExecutionPermit): short-lived, revocable, digest-matched (I4).' , { size : 12 , fill : C . ink2 , anchor : 'start' } )
b += text ( 30 , 452 , 'Simulation warnings always escalate to a human. No code path lets the AI reach APPROVED. Envelopes start empty and widen only on shadow-run evidence.' , { size : 12 , fill : C . ink2 , anchor : 'start' } )
out [ '04-safety-chain' ] = svg ( 1290 , 475 , b )
}
// ---------- 5. tech stack ----------
{
let b = ''
const row = ( y , h , label , items , o = { } ) => {
b += text ( 30 , y + h / 2 + 4 , label , { size : 11 , fill : C . ink3 , anchor : 'start' , weight : 700 } )
const x0 = 190 , W = 980 , n = items . length , g = 12 , w = ( W - g * ( n - 1 ) ) / n
items . forEach ( ( [ t , s ] , i ) => { b += box ( x0 + i * ( w + g ) , y , w , h , t , s , { fill : o . fill || C . white , stroke : o . stroke || C . line , tsize : 13 , ssize : 11 , tag : o . tag && i === 0 ? o . tag : null , tagColor : o . tagColor } ) } )
}
row ( 40 , 64 , 'OPERATOR UI' , [ [ 'Case Desk' , [ 'approval inbox · case view · lineage expansion' ] ] , [ 'AI insight cards' , [ 'read-only projections in existing business pages' ] ] ] )
row ( 120 , 80 , 'RUNTIME' , [ [ 'TypeScript + Mastra' , [ 'workflows (durable suspend/resume)' , 'agents as controlled steps · tool registry' ] ] , [ 'Trigger service' , [ 'cron · event bus consumers' , 'router agent for manual requests' ] ] , [ 'LLM port' , [ 'capability interface + model router' , 'LLM-down mode for degraded runs' ] ] ] , { fill : C . amberSoft , stroke : C . amber } )
row ( 226 , 80 , 'SERVICES' , [ [ 'Policy engine' , [ 'versioned, tested policy packs' ] ] , [ 'Envelope + authority' , [ 'envelope match · fresh check · permits' ] ] , [ 'Ledger + lineage' , [ 'position cascade · snapshots · assembler' ] ] , [ 'Gateways + Case Desk' , [ 'file export · simulation · cases' ] ] ] , { fill : C . tealSoft , stroke : C . teal , tag : 'NO LLM' , tagColor : C . teal } )
row ( 326 , 64 , 'CONTRACTS' , [ [ 'Domain schemas (zod)' , [ 'single source of truth' ] ] , [ 'JSON Schema' , [ 'exported, committed, versioned' ] ] , [ 'Golden fixtures' , [ 'validated by both CIs' ] ] ] )
row ( 406 , 72 , 'SKILLS' , [ [ 'Python services (FastAPI)' , [ 'load / PV / price forecasts with quantiles' , 'bid MILP · dispatch MILP · potential · report' ] ] , [ 'Generated pydantic models' , [ 'never hand-edited' ] ] ] , { fill : C . slate } )
row ( 494 , 72 , 'DATA' , [ [ 'PostgreSQL + pgvector' , [ 'relational · knowledge · outbox events' ] ] , [ 'TimescaleDB / IoTDB' , [ 'telemetry curves' ] ] , [ 'Object storage' , [ 'snapshots · archives' ] ] , [ 'Kafka (phase 2)' , [ 'outbox relay, same interface' ] ] ] )
row ( 582 , 72 , 'LLM BACKENDS' , [ [ 'Commercial API' , [ 'development' ] ] , [ 'Local open model' , [ 'pre-production' ] ] , [ 'GuangMing Power LLM' , [ 'production, on-prem adapter' ] ] ] , { fill : C . amberSoft , stroke : C . amber } )
b += text ( 30 , 688 , 'Backend swaps are gated by the L1 evaluation set and an L4 shadow comparison, not judgment. About a third of the platform is framework; the differentiating parts are the deterministic services.' , { size : 12 , fill : C . ink2 , anchor : 'start' , italic : true } )
out [ '05-tech-stack' ] = svg ( 1200 , 706 , b )
}
// ---------- 6. interfaces & contracts ----------
{
let b = ''
b += group ( 30 , 40 , 560 , 260 , 'Contract pipeline (one source of truth)' )
b += box ( 55 , 75 , 220 , 60 , 'zod schemas' , [ 'packages/domain · hand-written' ] , { fill : C . tealSoft , stroke : C . teal , mono : true , tsize : 13 } )
b += box ( 55 , 165 , 220 , 60 , 'JSON Schema' , [ 'contracts/ · committed · versioned' ] , { fill : C . slate , mono : true , tsize : 13 } )
b += arrow ( 165 , 135 , 165 , 165 , 'build export' , { lx : 60 } )
b += box ( 340 , 165 , 225 , 60 , 'pydantic models' , [ 'generated · never hand-edited' ] , { fill : C . slate , mono : true , tsize : 13 } )
b += arrow ( 275 , 195 , 340 , 195 , 'codegen' , { ly : - 8 } )
b += box ( 340 , 75 , 225 , 60 , 'Golden fixtures' , [ 'valid + invalid samples per object' ] , { fill : C . white } )
b += arrow ( 275 , 105 , 340 , 105 , '' , { } )
b += arrow ( 452 , 135 , 452 , 165 , '' , { dashed : true } )
b += box ( 55 , 245 , 510 , 40 , 'TS CI and Python CI both validate every fixture; any disagreement fails the build' , [ ] , { fill : C . white , stroke : C . ink2 , tsize : 12 } )
b += arrow ( 165 , 225 , 165 , 245 , '' , { dashed : true } )
b += arrow ( 452 , 225 , 452 , 245 , '' , { dashed : true } )
b += group ( 620 , 40 , 550 , 260 , 'Ports = trust boundaries (deterministic, not exposed to the LLM)' )
const ports = [
[ 'ProposalSubmissionPort' , 'the only way into the safety chain' ] ,
[ 'PolicyEnginePort · SimulationPort' , 'rule check and simulation' ] ,
[ 'EnvelopePort · AuthorityPort' , 'envelope match · fresh check · permits · revoke' ] ,
[ 'GatewayPort' , 'accepts (Proposal, ExecutionPermit) only' ] ,
[ 'LedgerPort · EvidencePort' , 'versioned ledger · event-sourced evidence' ] ,
[ 'CaseDeskPort' , 'open · read · apply command · watch' ] ,
]
ports . forEach ( ( [ p , d ] , i ) => {
const y = 78 + i * 36
b += text ( 640 , y + 12 , p , { size : 12 , fill : C . teal , anchor : 'start' , mono : true , weight : 600 } )
b += text ( 1150 , y + 12 , d , { size : 11.5 , fill : C . ink2 , anchor : 'end' } )
if ( i < ports . length - 1 ) b += ` <line x1="640" y1=" ${ y + 24 } " x2="1150" y2=" ${ y + 24 } " stroke=" ${ C . slate } "/> `
} )
b += group ( 30 , 330 , 1140 , 150 , 'Cross-process interfaces and data representation rules' )
const ifs = [ [ 'TS runtime → Python skills' , 'HTTP/JSON, schema-validated both ends' ] , [ 'Case Desk / front end → runtime' , 'HTTP/JSON approvals and case API' ] , [ 'External systems' , 'anti-corruption adapters, raw + translated logged' ] , [ 'Edge control link' , 'grid protocol stack, outside the JSON domain' ] ]
ifs . forEach ( ( [ t , s ] , i ) => { b += box ( 50 + i * 280 , 360 , 265 , 56 , t , [ s ] , { fill : C . white , tsize : 12.5 , ssize : 11 } ) } )
b += text ( 50 , 448 , 'Money, energy and prices as fixed-point decimal strings · units in field names (power_mw, price_yuan_per_mwh) · ISO 8601 UTC · intervals as {date, interval_index}' , { size : 11.5 , fill : C . ink2 , anchor : 'start' } )
b += text ( 50 , 466 , 'all IDs strings · enums UPPER_SNAKE · breaking schema changes bump the major version and ship a migration note' , { size : 11.5 , fill : C . ink2 , anchor : 'start' } )
out [ '06-contracts' ] = svg ( 1200 , 500 , b )
}
// ---------- 7. eval-driven development ----------
{
let b = ''
b += group ( 30 , 40 , 470 , 345 , 'Four evaluation layers' )
const layers = [
[ 'L4' , 'End-to-end decision quality' , 'shadow run · historical backtest · counterfactual attribution' ] ,
[ 'L3' , 'Safety chain correctness' , 'policy pack tests · invariant tests I1– I8 · red-team cases' ] ,
[ 'L2' , 'Professional-model skills' , 'MAPE · interval coverage · solver quality vs. bounds' ] ,
[ 'L1' , 'LLM tasks' , 'routing accuracy · schema compliance · numeric consistency' ] ,
]
layers . forEach ( ( [ l , t , s ] , i ) => {
const y = 70 + i * 72
b += ` <rect x="50" y=" ${ y } " width="430" height="60" rx="5" fill=" ${ i === 0 ? C . tealSoft : C . white } " stroke=" ${ i === 0 ? C . teal : C . line } "/> `
b += text ( 72 , y + 36 , l , { size : 16 , fill : C . teal , weight : 700 , mono : true } )
b += text ( 105 , y + 26 , t , { size : 13 , fill : C . ink , anchor : 'start' , weight : 600 } )
b += text ( 105 , y + 45 , s , { size : 11 , fill : C . ink2 , anchor : 'start' } )
} )
b += text ( 50 , 370 , 'A problem at L4 is localised to the layer that caused it.' , { size : 11.5 , fill : C . ink3 , anchor : 'start' , italic : true } )
b += group ( 530 , 40 , 640 , 345 , 'Change gates: what must pass before a change ships' )
const gates = [
[ 'Prompt / agent instructions' , 'L1 task set + L4 fixed-day backtest, no regression' ] ,
[ 'LLM backend switch' , 'full L1 set + L4 shadow comparison vs. baseline' ] ,
[ 'Skill model upgrade' , 'all L2 metrics for that skill + downstream L4 backtest' ] ,
[ 'Policy pack upgrade' , 'L3 pack tests green + impact analysis on pending proposals' ] ,
[ 'Contract schema change' , 'golden fixtures on both sides + compatibility check' ] ,
[ 'Envelope widening' , 'L4 shadow / online evidence only — no data, no widening' ] ,
]
gates . forEach ( ( [ c , g ] , i ) => {
const y = 78 + i * 46
b += text ( 550 , y + 14 , c , { size : 12.5 , fill : C . ink , anchor : 'start' , weight : 600 } )
b += text ( 550 , y + 31 , g , { size : 11.5 , fill : i === 5 ? C . amber : C . ink2 , anchor : 'start' } )
if ( i < gates . length - 1 ) b += ` <line x1="550" y1=" ${ y + 40 } " x2="1150" y2=" ${ y + 40 } " stroke=" ${ C . slate } "/> `
} )
// loop
b += group ( 30 , 410 , 1140 , 130 , 'The audit trail is the evaluation dataset' )
const loop = [ [ 'Production run' , [ 'event log + immutable snapshots' ] ] , [ 'Replay datasets' , [ 'representative days, frozen' ] ] , [ 'Eval run (archived)' , [ 'committed baseline, CI --check' ] ] , [ 'ReviewFinding' , [ 'D+1 attribution' ] ] , [ 'New eval cases' , [ 'every finding → candidate case' ] ] ]
loop . forEach ( ( [ t , s ] , i ) => {
b += box ( 50 + i * 228 , 445 , 200 , 60 , t , s , { fill : i === 2 ? C . tealSoft : C . white , stroke : i === 2 ? C . teal : C . line , tsize : 12.5 , ssize : 11 } )
if ( i < loop . length - 1 ) b += arrow ( 250 + i * 228 , 475 , 278 + i * 228 , 475 )
} )
b += arrow ( 1062 , 505 , 150 , 505 , '' , { path : 'M1062,505 L1062,525 L150,525 L150,506' , dashed : true } )
out [ '07-eval-driven' ] = svg ( 1200 , 560 , b )
}
for ( const [ name , html ] of Object . entries ( out ) ) writeFileSync ( ` ${ process . argv [ 2 ] } / ${ name } .html ` , html )
console . log ( Object . keys ( out ) . join ( '\n' ) )