SIMON Master Console Rebuild
True command center for SIMON + WEB360
This rebuild gives you a cleaner production spine: interactive tree, live viewer, guarded editor, dynamic graphics, architecture map, and note storage. It is designed to sit beside your existing
console.php
and become the stronger replacement path.
Dashboard
Tree
Viewer
System Map
File Registry
Notes
Files
15,312
Folders
410
Scanned Size
3.85 GB
PHP Files
891
Editable Text Files
12,573
File viewer
guarded to /htdocs
/simon/simon_infinity_forge.php
<?php declare(strict_types=1); /** * SIMON Infinity Forge * Interactive architecture-learning game for the ConnLink function exchange. * PHP 8.1+ */ const FORGE_VERSION = '1.0.0'; const FORGE_STATE_FILE = __DIR__ . '/_data/simon/infinity_forge_state.json'; function forge_json_response(array $data, int $status = 200): never { http_response_code($status); header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-store'); echo json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); exit; } function forge_read_json(string $path): array { if (!is_file($path) || !is_readable($path)) { return []; } $raw = file_get_contents($path); if ($raw === false || trim($raw) === '') { return []; } $decoded = json_decode($raw, true); return is_array($decoded) ? $decoded : []; } function forge_write_json(string $path, array $data): bool { $dir = dirname($path); if (!is_dir($dir) && !mkdir($dir, 0750, true) && !is_dir($dir)) { return false; } $tmp = $path . '.tmp'; $encoded = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); if ($encoded === false || file_put_contents($tmp, $encoded, LOCK_EX) === false) { return false; } return rename($tmp, $path); } function forge_manifest_candidates(): array { return [ __DIR__ . '/function_flow_manifest.json', __DIR__ . '/_data/simon/function_flow_manifest.json', dirname(__DIR__) . '/_data/simon/function_flow_manifest.json', __DIR__ . '/_simon_private_scanner/function_flow_manifest.json', ]; } function forge_load_manifest(): array { foreach (forge_manifest_candidates() as $path) { $data = forge_read_json($path); if ($data !== []) { $data['_source_path'] = $path; return $data; } } return []; } function forge_demo_nodes(): array { return [ ['id'=>'file-index','label'=>'index.php','kind'=>'file','role'=>'scan.function.discover','owner'=>'SIMON BUG','zone'=>'discover','health'=>82,'risk'=>34,'power'=>7,'lesson'=>'A scanner should discover and describe functions, not execute every shared role.'], ['id'=>'engine-parse','label'=>'Function Parsing Engine','kind'=>'engine','role'=>'scan.function.parse','owner'=>'SIMON BUG','zone'=>'discover','health'=>76,'risk'=>22,'power'=>6,'lesson'=>'Parsing extracts names, parameters, calls, database references, and behavior clues.'], ['id'=>'engine-similarity','label'=>'Similarity Engine','kind'=>'engine','role'=>'scan.function.compare','owner'=>'SIMON BUG','zone'=>'discover','health'=>71,'risk'=>29,'power'=>7,'lesson'=>'Similarity should compare exact code, normalized structure, purpose, and behavior separately.'], ['id'=>'engine-difference','label'=>'Difference Engine','kind'=>'engine','role'=>'scan.function.difference','owner'=>'SIMON BUG','zone'=>'discover','health'=>68,'risk'=>32,'power'=>8,'lesson'=>'A useful comparison explains what differs, not merely that two functions are similar.'], ['id'=>'engine-canonical','label'=>'Canonicalization Engine','kind'=>'engine','role'=>'evolution.function.canonicalize','owner'=>'SIMON Intelligence','zone'=>'discover','health'=>63,'risk'=>45,'power'=>9,'lesson'=>'Canonicalization selects the strongest verified behavior from multiple candidates.'], ['id'=>'file-manifest','label'=>'function_flow_manifest.json','kind'=>'data','role'=>'registry.function.manifest','owner'=>'SIMON Registry','zone'=>'exchange','health'=>86,'risk'=>18,'power'=>8,'lesson'=>'The manifest is a fast map. MariaDB should remain the durable source of truth.'], ['id'=>'file-connlink','label'=>'connlink_function_exchange.php','kind'=>'file','role'=>'connlink.function.exchange','owner'=>'ConnLink','zone'=>'exchange','health'=>88,'risk'=>24,'power'=>10,'lesson'=>'ConnLink routes roles to approved canonical implementations through stable contracts.'], ['id'=>'engine-contract','label'=>'Contract Engine','kind'=>'engine','role'=>'connlink.contract.validate','owner'=>'ConnLink','zone'=>'exchange','health'=>79,'risk'=>21,'power'=>8,'lesson'=>'Contracts protect callers from incompatible parameters and return structures.'], ['id'=>'engine-router','label'=>'Routing Engine','kind'=>'engine','role'=>'connlink.route.resolve','owner'=>'ConnLink','zone'=>'exchange','health'=>84,'risk'=>28,'power'=>9,'lesson'=>'Applications should request a role. ConnLink decides which implementation supplies it.'], ['id'=>'engine-guardian','label'=>'Approval Engine','kind'=>'engine','role'=>'guardian.change.approve','owner'=>'Guardian','zone'=>'exchange','health'=>81,'risk'=>16,'power'=>8,'lesson'=>'Higher-risk changes require checkpoints, tests, authority, and rollback.'], ['id'=>'app-web360','label'=>'WEB360','kind'=>'app','role'=>'app.web360','owner'=>'WEB360','zone'=>'execute','health'=>74,'risk'=>31,'power'=>7,'lesson'=>'Applications keep their business logic but call shared roles through ConnLink.'], ['id'=>'app-chat','label'=>'Gaylord Chat','kind'=>'app','role'=>'app.gaylord_chat','owner'=>'Gaylord Chat','zone'=>'execute','health'=>69,'risk'=>36,'power'=>6,'lesson'=>'A shared profile-save role prevents each application from inventing another user update function.'], ['id'=>'provider-ai','label'=>'AI Provider','kind'=>'provider','role'=>'ai.text.generate','owner'=>'ConnLink AI','zone'=>'execute','health'=>78,'risk'=>37,'power'=>9,'lesson'=>'Provider adapters normalize different vendors behind one capability contract.'], ['id'=>'db-mariadb','label'=>'MariaDB Registry','kind'=>'database','role'=>'registry.persistence','owner'=>'SIMON Data','zone'=>'execute','health'=>91,'risk'=>20,'power'=>10,'lesson'=>'Durable state belongs in MariaDB; generated JSON serves fast reads and recovery.'], ['id'=>'engine-monitor','label'=>'Monitoring Engine','kind'=>'engine','role'=>'runtime.function.monitor','owner'=>'SIMON Runtime','zone'=>'feedback','health'=>72,'risk'=>25,'power'=>7,'lesson'=>'Runtime evidence reveals whether a cleaner function is actually safer and faster.'], ['id'=>'engine-evolution','label'=>'Evolution Engine','kind'=>'engine','role'=>'evolution.cycle.merge','owner'=>'SIMON Intelligence','zone'=>'feedback','health'=>66,'risk'=>33,'power'=>10,'lesson'=>'The outer merge combines static scans, runtime results, failures, and new requirements.'], ['id'=>'engine-rollback','label'=>'Rollback Engine','kind'=>'engine','role'=>'evolution.change.rollback','owner'=>'Guardian','zone'=>'feedback','health'=>83,'risk'=>13,'power'=>8,'lesson'=>'A change is not safe until restoration has been designed and tested.'], ['id'=>'file-map','label'=>'simon_infinity_function_map.php','kind'=>'file','role'=>'visualize.function.flow','owner'=>'SIMON Spatial','zone'=>'feedback','health'=>77,'risk'=>12,'power'=>5,'lesson'=>'The visual map should read the registry rather than perform another full host scan.'], ]; } function forge_nodes_from_manifest(array $manifest): array { $functions = $manifest['functions'] ?? []; if (!is_array($functions) || $functions === []) { return forge_demo_nodes(); } $nodes = []; $limit = 24; foreach ($functions as $key => $fn) { if (!is_array($fn)) continue; $role = (string)($fn['role'] ?? $fn['role_key'] ?? 'function.unclassified'); $owner = (string)($fn['owner'] ?? $fn['owner_system'] ?? 'Unassigned'); $status = (string)($fn['status'] ?? 'discovered'); $zone = match (true) { str_contains($role, 'connlink'), str_contains($role, 'route'), str_contains($role, 'contract') => 'exchange', str_contains($role, 'runtime'), str_contains($role, 'monitor'), str_contains($role, 'feedback') => 'feedback', str_contains($role, 'app.'), str_contains($role, 'provider'), str_contains($role, 'database') => 'execute', default => 'discover', }; $riskFloat = (float)($fn['risk'] ?? $fn['risk_score'] ?? 0.25); $risk = $riskFloat <= 1 ? (int)round($riskFloat * 100) : (int)min(100, $riskFloat); $confidenceFloat = (float)($fn['confidence'] ?? 0.72); $health = (int)max(20, min(99, round(($confidenceFloat <= 1 ? $confidenceFloat * 100 : $confidenceFloat)))); $nodes[] = [ 'id' => preg_replace('/[^a-zA-Z0-9_-]+/', '-', (string)($fn['function_id'] ?? $key)), 'label' => (string)($fn['name'] ?? $fn['function_name'] ?? $role), 'kind' => 'function', 'role' => $role, 'owner' => $owner, 'zone' => $zone, 'health' => $health, 'risk' => $risk, 'power' => (int)max(3, min(10, round(($health + (100 - $risk)) / 20))), 'lesson' => 'Status: ' . $status . '. Route shared behavior by role, then verify it with runtime evidence.', ]; if (count($nodes) >= $limit) break; } return $nodes ?: forge_demo_nodes(); } if (isset($_GET['api'])) { $action = (string)$_GET['api']; if ($action === 'state') { forge_json_response(['ok'=>true, 'state'=>forge_read_json(FORGE_STATE_FILE)]); } if ($action === 'save') { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { forge_json_response(['ok'=>false, 'error'=>'POST required'], 405); } $input = json_decode((string)file_get_contents('php://input'), true); if (!is_array($input)) { forge_json_response(['ok'=>false, 'error'=>'Invalid JSON'], 400); } $state = [ 'version' => FORGE_VERSION, 'saved_at' => date(DATE_ATOM), 'score' => (int)($input['score'] ?? 0), 'level' => (int)($input['level'] ?? 1), 'cycle' => (int)($input['cycle'] ?? 0), 'nodes' => is_array($input['nodes'] ?? null) ? array_slice($input['nodes'], 0, 100) : [], 'connections' => is_array($input['connections'] ?? null) ? array_slice($input['connections'], 0, 250) : [], 'completedMissions' => is_array($input['completedMissions'] ?? null) ? array_slice($input['completedMissions'], 0, 100) : [], ]; if (!forge_write_json(FORGE_STATE_FILE, $state)) { forge_json_response(['ok'=>false, 'error'=>'State folder is not writable'], 500); } forge_json_response(['ok'=>true, 'state'=>$state]); } forge_json_response(['ok'=>false, 'error'=>'Unknown action'], 404); } $manifest = forge_load_manifest(); $nodes = forge_nodes_from_manifest($manifest); $state = forge_read_json(FORGE_STATE_FILE); $manifestSource = (string)($manifest['_source_path'] ?? 'Demo learning dataset'); header('Content-Type: text/html; charset=utf-8'); header('X-Content-Type-Options: nosniff'); header('Referrer-Policy: strict-origin-when-cross-origin'); ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"> <meta name="theme-color" content="#070817"> <title>SIMON Infinity Forge</title> <style> :root{--bg:#050615;--glass:rgba(15,20,49,.72);--line:rgba(121,238,255,.32);--cyan:#71f2ff;--violet:#a879ff;--pink:#ff67cf;--green:#79ffc2;--amber:#ffd679;--red:#ff778f;--text:#f6f8ff;--muted:#9ba6c7} *{box-sizing:border-box}html,body{margin:0;min-height:100%;background:radial-gradient(circle at 50% 30%,#17133d 0,#08091d 38%,#03040c 100%);color:var(--text);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;overflow-x:hidden} body:before{content:"";position:fixed;inset:0;background-image:radial-gradient(circle,#fff 0 1px,transparent 1.5px);background-size:73px 73px;opacity:.13;pointer-events:none} button,input{font:inherit}.shell{min-height:100vh;padding:18px}.topbar{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:16px;align-items:center;margin-bottom:14px}.brand h1{font-size:clamp(22px,4vw,42px);margin:0;letter-spacing:.04em}.brand p{margin:5px 0 0;color:var(--muted)}.badge{border:1px solid var(--line);background:var(--glass);backdrop-filter:blur(18px);padding:10px 14px;border-radius:999px;color:var(--cyan)} .layout{display:grid;grid-template-columns:280px minmax(0,1fr) 300px;gap:14px}.panel{border:1px solid rgba(145,199,255,.18);background:linear-gradient(155deg,rgba(20,25,60,.88),rgba(6,8,27,.74));box-shadow:0 22px 80px rgba(0,0,0,.34),inset 0 1px rgba(255,255,255,.05);backdrop-filter:blur(18px);border-radius:22px}.sidebar{padding:14px;min-height:720px}.panel h2{font-size:13px;text-transform:uppercase;letter-spacing:.14em;color:var(--cyan);margin:0 0 12px}.stats{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-bottom:14px}.stat{padding:10px;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:rgba(255,255,255,.035)}.stat strong{display:block;font-size:22px}.stat span{color:var(--muted);font-size:11px}.meter{height:9px;background:rgba(255,255,255,.07);border-radius:999px;overflow:hidden;margin-top:7px}.meter i{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--pink),var(--cyan),var(--green));transition:width .5s ease}.mission{padding:12px;border-radius:15px;background:rgba(117,94,255,.09);border:1px solid rgba(168,121,255,.24);margin-bottom:9px;cursor:pointer}.mission.done{opacity:.52;border-color:rgba(121,255,194,.25)}.mission b{display:block;font-size:13px}.mission small{display:block;color:var(--muted);margin-top:4px;line-height:1.4}.controls{display:grid;gap:8px}.btn{border:1px solid rgba(113,242,255,.25);background:rgba(113,242,255,.08);color:var(--text);padding:10px 12px;border-radius:13px;cursor:pointer;transition:.2s}.btn:hover{transform:translateY(-1px);border-color:var(--cyan);box-shadow:0 0 24px rgba(113,242,255,.15)}.btn.primary{background:linear-gradient(135deg,rgba(168,121,255,.42),rgba(113,242,255,.25));font-weight:700}.btn.danger{border-color:rgba(255,119,143,.35);background:rgba(255,119,143,.09)} .board{position:relative;min-height:720px;overflow:hidden}.board-head{position:absolute;left:18px;right:18px;top:14px;z-index:7;display:flex;justify-content:space-between;align-items:center;gap:10px}.mode{display:flex;gap:7px;flex-wrap:wrap}.chip{padding:7px 10px;border-radius:999px;border:1px solid rgba(255,255,255,.1);background:rgba(3,5,18,.7);color:var(--muted);font-size:12px}.chip.active{color:var(--cyan);border-color:rgba(113,242,255,.4)} #flowSvg{position:absolute;inset:0;width:100%;height:100%}.infinity-main{fill:none;stroke:url(#energy);stroke-width:12;filter:url(#glow);opacity:.78}.infinity-ghost{fill:none;stroke:rgba(255,255,255,.08);stroke-width:30}.flow-dash{fill:none;stroke:#fff;stroke-width:2.5;stroke-linecap:round;stroke-dasharray:2 22;animation:flow 8s linear infinite;opacity:.72}.outer-ring{fill:none;stroke:url(#outer);stroke-width:3;stroke-dasharray:9 12;animation:flow 18s linear infinite;opacity:.52}@keyframes flow{to{stroke-dashoffset:-480}} .zone-label{position:absolute;z-index:3;text-align:center;pointer-events:none}.zone-label b{display:block;font-size:12px;letter-spacing:.14em}.zone-label span{font-size:11px;color:var(--muted)}.zl-left{left:8%;top:18%}.zl-center{left:50%;top:45%;transform:translate(-50%,-50%)}.zl-right{right:8%;top:18%}.zl-outer{left:50%;bottom:8%;transform:translateX(-50%)} .node{position:absolute;width:144px;min-height:70px;padding:10px 10px 9px;border:1px solid rgba(255,255,255,.14);border-radius:16px;background:linear-gradient(145deg,rgba(18,25,61,.96),rgba(8,11,31,.88));box-shadow:0 14px 35px rgba(0,0,0,.35),0 0 24px rgba(113,242,255,.08);cursor:grab;user-select:none;touch-action:none;z-index:5;transition:box-shadow .2s,border-color .2s,transform .2s}.node:active{cursor:grabbing}.node.selected{border-color:var(--cyan);box-shadow:0 0 0 2px rgba(113,242,255,.13),0 0 35px rgba(113,242,255,.28)}.node.bad{border-color:rgba(255,119,143,.55)}.node.good{border-color:rgba(121,255,194,.48)}.node .kind{font-size:9px;text-transform:uppercase;letter-spacing:.12em;color:var(--violet)}.node strong{display:block;font-size:12px;margin:4px 0 5px;line-height:1.25}.node .mini{display:flex;gap:5px;flex-wrap:wrap}.node .mini span{font-size:9px;color:var(--muted);padding:3px 5px;background:rgba(255,255,255,.05);border-radius:8px}.pulse{position:absolute;width:11px;height:11px;border-radius:50%;background:#fff;box-shadow:0 0 18px var(--cyan),0 0 36px var(--violet);z-index:4;pointer-events:none;offset-path:path('M 110 360 C 215 70 420 70 520 360 C 620 650 825 650 930 360 C 825 70 620 70 520 360 C 420 650 215 650 110 360');animation:travel 8s linear infinite}@keyframes travel{to{offset-distance:100%}} .inspector{padding:14px;min-height:720px}.inspector-card{padding:13px;border:1px solid rgba(255,255,255,.08);border-radius:16px;background:rgba(255,255,255,.03);margin-bottom:10px}.inspector-card h3{font-size:15px;margin:0 0 6px}.inspector-card p{color:var(--muted);font-size:12px;line-height:1.5;margin:6px 0}.kv{display:grid;grid-template-columns:85px 1fr;gap:6px;font-size:11px;margin-top:8px}.kv span:nth-child(odd){color:var(--muted)}.lesson{border-color:rgba(255,214,121,.25);background:rgba(255,214,121,.05)}.quiz button{width:100%;text-align:left;margin-top:7px}.log{height:156px;overflow:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px;color:#bec8e8;line-height:1.5}.log div{padding:4px 0;border-bottom:1px solid rgba(255,255,255,.04)}.toast{position:fixed;left:50%;bottom:28px;transform:translate(-50%,20px);opacity:0;z-index:30;padding:12px 16px;border-radius:14px;background:rgba(8,13,31,.94);border:1px solid rgba(113,242,255,.32);box-shadow:0 20px 70px #000;color:var(--text);transition:.25s;pointer-events:none}.toast.show{opacity:1;transform:translate(-50%,0)} @media(max-width:1100px){.layout{grid-template-columns:240px minmax(0,1fr)}.inspector{grid-column:1/-1;min-height:auto}.board{min-height:650px}.sidebar{min-height:650px}} @media(max-width:760px){.shell{padding:10px}.topbar{grid-template-columns:1fr}.layout{grid-template-columns:1fr}.sidebar,.inspector{min-height:auto}.board{min-height:720px;order:-1}.node{width:126px}.board-head{top:10px;left:10px;right:10px}.pulse{display:none}} </style> </head> <body> <div class="shell"> <header class="topbar"> <div class="brand"> <h1>SIMON Infinity Forge</h1> <p>Build the loop. Route the mind. Test the consequence. Evolve the system.</p> </div> <div class="badge">Mindset: Own your mind. Question the default.</div> </header> <main class="layout"> <aside class="panel sidebar"> <h2>System Intelligence</h2> <div class="stats"> <div class="stat"><strong id="score">0</strong><span>Forge score</span></div> <div class="stat"><strong id="level">1</strong><span>Evolution level</span></div> <div class="stat"><strong id="cycle">0</strong><span>Cycles run</span></div> <div class="stat"><strong id="connections">0</strong><span>Connections</span></div> </div> <div class="stat" style="margin-bottom:14px"><span>System coherence</span><div class="meter"><i id="coherenceBar"></i></div><strong id="coherence" style="font-size:15px;margin-top:5px">0%</strong></div> <h2>Missions</h2> <div id="missions"></div> <h2>Forge Controls</h2> <div class="controls"> <button class="btn primary" id="runCycle">Run Evolution Cycle</button> <button class="btn" id="connectMode">Connect Nodes</button> <button class="btn" id="autoArrange">Rebuild Infinity Layout</button> <button class="btn" id="saveState">Save Forge</button> <button class="btn danger" id="resetGame">Reset Simulation</button> </div> <p style="font-size:10px;color:var(--muted);line-height:1.5;margin-top:12px">Manifest: <?= htmlspecialchars($manifestSource, ENT_QUOTES, 'UTF-8') ?></p> </aside> <section class="panel board" id="board"> <div class="board-head"> <div class="mode"><span class="chip active" id="modeChip">MODE: MOVE</span><span class="chip">v<?= FORGE_VERSION ?></span></div> <div class="chip" id="objective">Connect discovery to ConnLink, then runtime to evolution.</div> </div> <svg id="flowSvg" viewBox="0 0 1040 720" preserveAspectRatio="none" aria-label="Interactive infinity architecture board"> <defs> <linearGradient id="energy" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#ff67cf"/><stop offset=".45" stop-color="#71f2ff"/><stop offset="1" stop-color="#79ffc2"/></linearGradient> <linearGradient id="outer" x1="0" y1="1" x2="1" y2="0"><stop offset="0" stop-color="#a879ff"/><stop offset=".5" stop-color="#71f2ff"/><stop offset="1" stop-color="#ffd679"/></linearGradient> <filter id="glow"><feGaussianBlur stdDeviation="5" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter> <marker id="arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#71f2ff"/></marker> </defs> <path class="outer-ring" d="M80 360 C80 70 300 25 520 360 C740 695 960 650 960 360 C960 70 740 25 520 360 C300 695 80 650 80 360 Z"/> <path class="infinity-ghost" d="M110 360 C215 70 420 70 520 360 C620 650 825 650 930 360 C825 70 620 70 520 360 C420 650 215 650 110 360"/> <path class="infinity-main" d="M110 360 C215 70 420 70 520 360 C620 650 825 650 930 360 C825 70 620 70 520 360 C420 650 215 650 110 360"/> <path class="flow-dash" d="M110 360 C215 70 420 70 520 360 C620 650 825 650 930 360 C825 70 620 70 520 360 C420 650 215 650 110 360"/> <g id="connectionLayer"></g> </svg> <div class="pulse"></div> <div class="zone-label zl-left"><b>DISCOVERY / INTELLIGENCE</b><span>scan · compare · improve</span></div> <div class="zone-label zl-center"><b>CONNLINK EXCHANGE</b><span>route · validate · dispatch</span></div> <div class="zone-label zl-right"><b>EXECUTION / EXPERIENCE</b><span>use · observe · measure</span></div> <div class="zone-label zl-outer"><b>EVOLUTION MERGE</b><span>feedback returns as better judgment</span></div> <div id="nodeLayer"></div> </section> <aside class="panel inspector"> <h2>Node Inspector</h2> <div class="inspector-card" id="nodeInfo"><h3>Select a node</h3><p>Move it, connect it, and inspect why it belongs in that part of the loop.</p></div> <div class="inspector-card lesson" id="lessonCard"><h3>Own Your Mind</h3><p>Do not optimize what you have not understood. A repeated function can be duplication, compatibility, specialization, or a warning. Compare behavior before deleting structure.</p></div> <div class="inspector-card quiz" id="quizCard"><h3>Thinking Challenge</h3><p id="quizQuestion">Why should an application call a role instead of a specific provider function?</p><div id="quizAnswers"></div></div> <div class="inspector-card"><h3>Evolution Log</h3><div class="log" id="eventLog"></div></div> </aside> </main> </div> <div class="toast" id="toast"></div> <script> const INITIAL_NODES = <?= json_encode($nodes, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>; const SAVED_STATE = <?= json_encode($state, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>; const board = document.getElementById('board'); const nodeLayer = document.getElementById('nodeLayer'); const connectionLayer = document.getElementById('connectionLayer'); const nodes = new Map(); let connections = []; let selectedId = null; let connectStart = null; let connectMode = false; let score = Number(SAVED_STATE.score || 0); let level = Number(SAVED_STATE.level || 1); let cycle = Number(SAVED_STATE.cycle || 0); let completedMissions = new Set(SAVED_STATE.completedMissions || []); const missions = [ {id:'m1', title:'Complete the center exchange', text:'Connect one discovery node to ConnLink and ConnLink to one execution node.', reward:120}, {id:'m2', title:'Close the learning loop', text:'Connect runtime or monitoring back to an evolution node.', reward:160}, {id:'m3', title:'Protect the system', text:'Connect Approval or Rollback to ConnLink or Evolution.', reward:100}, {id:'m4', title:'Think beyond duplication', text:'Inspect four different nodes and read their lessons.', reward:80}, {id:'m5', title:'Run three evolution cycles', text:'Measure the board, learn, and iterate three times.', reward:180}, ]; let inspected = new Set(); const quizzes = [ {q:'Why should applications call a role instead of a specific provider function?', a:['It removes all testing','It allows routing, versioning, and provider changes behind one contract','It makes every file larger'], correct:1, explain:'A role is stable. Its implementation can change without forcing every caller to be rewritten.'}, {q:'When is a similar function not necessarily a duplicate?', a:['When it serves a different contract or security boundary','Never','Only when the name is longer'], correct:0, explain:'Purpose, callers, side effects, and contracts matter more than naming.'}, {q:'What belongs at the outer evolution merge?', a:['Only source code','Static scan evidence plus runtime outcomes, failures, and new requirements','Passwords and raw private payloads'], correct:1, explain:'Evolution needs evidence from both design and actual use, while sensitive data should remain protected.'}, {q:'What makes a high-risk refactor safe?', a:['A prettier diagram','Tests, approval, checkpoint, monitoring, and rollback','Renaming it final_v7'], correct:1, explain:'Safety is an operational chain, not confidence wearing a nice shirt.'}, ]; let quizIndex = 0; function log(message){const el=document.getElementById('eventLog');const row=document.createElement('div');row.textContent=new Date().toLocaleTimeString()+' '+message;el.prepend(row)} function toast(message){const el=document.getElementById('toast');el.textContent=message;el.classList.add('show');clearTimeout(toast.t);toast.t=setTimeout(()=>el.classList.remove('show'),2200)} function zonePosition(zone, index, total){ const points={ discover:[[14,48],[19,27],[31,18],[40,29],[35,54],[25,67]], exchange:[[46,37],[50,51],[53,64],[48,77]], execute:[[61,29],[72,18],[84,29],[88,49],[79,67],[65,57]], feedback:[[70,80],[50,87],[30,80],[12,63]] }; const list=points[zone]||points.discover;return list[index%list.length]; } function restorePositions(){const saved=new Map((SAVED_STATE.nodes||[]).map(n=>[n.id,n]));const counts={};const totals={};INITIAL_NODES.forEach(n=>totals[n.zone]=(totals[n.zone]||0)+1);INITIAL_NODES.forEach(n=>{const s=saved.get(n.id);if(s){n.x=s.x;n.y=s.y}else{const idx=counts[n.zone]||0;const p=zonePosition(n.zone,idx,totals[n.zone]);counts[n.zone]=idx+1;n.x=p[0];n.y=p[1]}});connections=Array.isArray(SAVED_STATE.connections)?SAVED_STATE.connections:[]} function createNode(data){ const el=document.createElement('div');el.className='node';el.dataset.id=data.id;el.innerHTML=`<span class="kind">${escapeHtml(data.kind)} · ${escapeHtml(data.zone)}</span><strong>${escapeHtml(data.label)}</strong><div class="mini"><span>H ${data.health}</span><span>R ${data.risk}</span><span>P ${data.power}</span></div>`; el.style.left=`calc(${data.x}% - 72px)`;el.style.top=`calc(${data.y}% - 35px)`;nodeLayer.appendChild(el);data.el=el;nodes.set(data.id,data); let drag=null; el.addEventListener('pointerdown',e=>{e.preventDefault();selectNode(data.id);if(connectMode){handleConnect(data.id);return}const r=board.getBoundingClientRect();drag={dx:e.clientX-el.getBoundingClientRect().left,dy:e.clientY-el.getBoundingClientRect().top,r};el.setPointerCapture(e.pointerId)}); el.addEventListener('pointermove',e=>{if(!drag)return;const x=Math.max(0,Math.min(drag.r.width-144,e.clientX-drag.r.left-drag.dx));const y=Math.max(50,Math.min(drag.r.height-75,e.clientY-drag.r.top-drag.dy));data.x=((x+72)/drag.r.width)*100;data.y=((y+35)/drag.r.height)*100;el.style.left=x+'px';el.style.top=y+'px';renderConnections()}); el.addEventListener('pointerup',()=>{drag=null;evaluateBoard()}); } function escapeHtml(v){return String(v).replace(/[&<>'"]/g,c=>({'&':'&','<':'<','>':'>',"'":''','"':'"'}[c]))} function selectNode(id){selectedId=id;nodes.forEach(n=>n.el.classList.toggle('selected',n.id===id));const n=nodes.get(id);inspected.add(id);document.getElementById('nodeInfo').innerHTML=`<h3>${escapeHtml(n.label)}</h3><p>${escapeHtml(n.lesson)}</p><div class="kv"><span>Role</span><span>${escapeHtml(n.role)}</span><span>Owner</span><span>${escapeHtml(n.owner)}</span><span>Zone</span><span>${escapeHtml(n.zone)}</span><span>Health</span><span>${n.health}%</span><span>Risk</span><span>${n.risk}%</span></div>`;checkMissions()} function handleConnect(id){if(!connectStart){connectStart=id;toast('Choose the destination node');nodes.get(id).el.classList.add('selected');return}if(connectStart===id){connectStart=null;toast('Connection cancelled');return}const exists=connections.some(c=>c.from===connectStart&&c.to===id);if(!exists){connections.push({from:connectStart,to:id});score+=20;log(`Connected ${nodes.get(connectStart).label} → ${nodes.get(id).label}`)}connectStart=null;renderConnections();evaluateBoard();checkMissions()} function renderConnections(){connectionLayer.innerHTML='';const br=board.getBoundingClientRect();connections.forEach(c=>{const a=nodes.get(c.from),b=nodes.get(c.to);if(!a||!b)return;const x1=a.x/100*1040,y1=a.y/100*720,x2=b.x/100*1040,y2=b.y/100*720;const path=document.createElementNS('http://www.w3.org/2000/svg','path');const mx=(x1+x2)/2;path.setAttribute('d',`M ${x1} ${y1} C ${mx} ${y1}, ${mx} ${y2}, ${x2} ${y2}`);path.setAttribute('fill','none');path.setAttribute('stroke','#71f2ff');path.setAttribute('stroke-opacity','.55');path.setAttribute('stroke-width','2');path.setAttribute('marker-end','url(#arrow)');connectionLayer.appendChild(path)})} function semanticScore(from,to){const a=nodes.get(from),b=nodes.get(to);if(!a||!b)return 0;let s=0;if(a.zone!==b.zone)s+=12;if(a.zone==='discover'&&b.zone==='exchange')s+=18;if(a.zone==='exchange'&&b.zone==='execute')s+=18;if(a.zone==='execute'&&b.zone==='feedback')s+=18;if(a.zone==='feedback'&&b.zone==='discover')s+=22;if(a.owner==='Guardian'||b.owner==='Guardian')s+=6;if(a.risk>60&&b.owner!=='Guardian')s-=20;return s} function evaluateBoard(){let good=0,bad=0;connections.forEach(c=>{const s=semanticScore(c.from,c.to);if(s>15)good++;if(s<0)bad++});const uniqueZones=new Set();connections.forEach(c=>{uniqueZones.add(nodes.get(c.from)?.zone);uniqueZones.add(nodes.get(c.to)?.zone)});const coherence=Math.max(0,Math.min(100,Math.round(good*11+uniqueZones.size*8-bad*12+Math.min(20,connections.length*2))));document.getElementById('coherence').textContent=coherence+'%';document.getElementById('coherenceBar').style.width=coherence+'%';score=Math.max(score,coherence*3+connections.length*12);level=Math.max(1,Math.floor(score/500)+1);updateStats();return coherence} function updateStats(){document.getElementById('score').textContent=score;document.getElementById('level').textContent=level;document.getElementById('cycle').textContent=cycle;document.getElementById('connections').textContent=connections.length} function arrange(){const counts={},totals={};INITIAL_NODES.forEach(n=>totals[n.zone]=(totals[n.zone]||0)+1);INITIAL_NODES.forEach(n=>{const idx=counts[n.zone]||0,p=zonePosition(n.zone,idx,totals[n.zone]);counts[n.zone]=idx+1;n.x=p[0];n.y=p[1];n.el.style.left=`calc(${n.x}% - 72px)`;n.el.style.top=`calc(${n.y}% - 35px)`});renderConnections();evaluateBoard();toast('Infinity structure restored')} function runCycle(){cycle++;const coherence=evaluateBoard();let delta=Math.round(coherence/8)+connections.reduce((sum,c)=>sum+Math.max(0,semanticScore(c.from,c.to)),0);const failures=connections.filter(c=>semanticScore(c.from,c.to)<0).length;delta-=failures*25;score=Math.max(0,score+delta);log(`Cycle ${cycle}: coherence ${coherence}%, evolution gain ${delta}, routing faults ${failures}`);toast(failures?`Cycle found ${failures} risky route${failures>1?'s':''}`:`Evolution cycle gained ${delta} intelligence`);checkMissions();updateStats()} function missionSatisfied(id){ if(id==='m1'){return connections.some(c=>nodes.get(c.from)?.zone==='discover'&&nodes.get(c.to)?.zone==='exchange')&&connections.some(c=>nodes.get(c.from)?.zone==='exchange'&&nodes.get(c.to)?.zone==='execute')} if(id==='m2')return connections.some(c=>nodes.get(c.from)?.zone==='execute'&&nodes.get(c.to)?.zone==='feedback')||connections.some(c=>nodes.get(c.from)?.zone==='feedback'&&nodes.get(c.to)?.zone==='discover'); if(id==='m3')return connections.some(c=>{const a=nodes.get(c.from),b=nodes.get(c.to);return /Approval|Rollback/.test(a?.label||'')||/Approval|Rollback/.test(b?.label||'')}); if(id==='m4')return inspected.size>=4; if(id==='m5')return cycle>=3; return false; } function checkMissions(){missions.forEach(m=>{if(!completedMissions.has(m.id)&&missionSatisfied(m.id)){completedMissions.add(m.id);score+=m.reward;log(`Mission complete: ${m.title} (+${m.reward})`);toast(`Mission complete: ${m.title}`)}});renderMissions();updateStats()} function renderMissions(){const root=document.getElementById('missions');root.innerHTML='';missions.forEach(m=>{const d=document.createElement('div');d.className='mission'+(completedMissions.has(m.id)?' done':'');d.innerHTML=`<b>${completedMissions.has(m.id)?'✓ ':''}${escapeHtml(m.title)}</b><small>${escapeHtml(m.text)} · +${m.reward}</small>`;root.appendChild(d)})} function renderQuiz(){const q=quizzes[quizIndex%quizzes.length];document.getElementById('quizQuestion').textContent=q.q;const root=document.getElementById('quizAnswers');root.innerHTML='';q.a.forEach((answer,i)=>{const b=document.createElement('button');b.className='btn';b.textContent=answer;b.onclick=()=>{if(i===q.correct){score+=50;toast('Correct. Intelligence +50');log('Thinking challenge solved: '+q.explain)}else{score=Math.max(0,score-10);toast('Not quite. Read the explanation and challenge the assumption.');log('Learning correction: '+q.explain)}quizIndex++;renderQuiz();updateStats()};root.appendChild(b)})} async function saveState(){const payload={score,level,cycle,nodes:[...nodes.values()].map(n=>({id:n.id,x:n.x,y:n.y})),connections,completedMissions:[...completedMissions]};try{const r=await fetch('?api=save',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});const j=await r.json();if(!j.ok)throw new Error(j.error||'Save failed');toast('Forge saved');log('Simulation state saved')}catch(e){toast(e.message);log('Save error: '+e.message)}} function resetGame(){if(!confirm('Reset the Forge simulation? Production remains untouched. Civilization survives another administrative decision.'))return;score=0;level=1;cycle=0;connections=[];completedMissions.clear();inspected.clear();arrange();renderMissions();updateStats();log('Forge reset')} document.getElementById('connectMode').onclick=()=>{connectMode=!connectMode;connectStart=null;document.getElementById('modeChip').textContent='MODE: '+(connectMode?'CONNECT':'MOVE');document.getElementById('connectMode').textContent=connectMode?'Exit Connect Mode':'Connect Nodes';toast(connectMode?'Select two nodes to connect':'Move mode enabled')}; document.getElementById('runCycle').onclick=runCycle;document.getElementById('autoArrange').onclick=arrange;document.getElementById('saveState').onclick=saveState;document.getElementById('resetGame').onclick=resetGame; restorePositions();INITIAL_NODES.forEach(createNode);renderConnections();renderMissions();renderQuiz();evaluateBoard();updateStats();log('Infinity Forge initialized with '+INITIAL_NODES.length+' nodes'); </script> </body> </html>
Save file
Quick jump
open a path
Open