@neuroverseos/nv-sim 0.1.2 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +376 -66
  2. package/dist/adapters/mirofish.js +461 -0
  3. package/dist/adapters/scienceclaw.js +750 -0
  4. package/dist/assets/index-CHmUN8s0.js +532 -0
  5. package/dist/assets/index-DWgMnB7I.css +1 -0
  6. package/dist/assets/mirotir-logo-DUexumBH.svg +185 -0
  7. package/dist/assets/reportEngine-BVdQ2_nW.js +1 -0
  8. package/dist/components/ConstraintsPanel.js +11 -0
  9. package/dist/components/StakeholderBuilder.js +32 -0
  10. package/dist/components/ui/badge.js +24 -0
  11. package/dist/components/ui/button.js +70 -0
  12. package/dist/components/ui/card.js +57 -0
  13. package/dist/components/ui/input.js +44 -0
  14. package/dist/components/ui/label.js +45 -0
  15. package/dist/components/ui/select.js +70 -0
  16. package/dist/engine/aiProvider.js +681 -0
  17. package/dist/engine/auditTrace.js +352 -0
  18. package/dist/engine/behavioralAnalysis.js +605 -0
  19. package/dist/engine/cli.js +1408 -299
  20. package/dist/engine/dynamicsGovernance.js +588 -0
  21. package/dist/engine/fullGovernedLoop.js +367 -0
  22. package/dist/engine/governance.js +8 -3
  23. package/dist/engine/governedSimulation.js +114 -17
  24. package/dist/engine/index.js +56 -1
  25. package/dist/engine/liveAdapter.js +342 -0
  26. package/dist/engine/liveVisualizer.js +3063 -0
  27. package/dist/engine/metrics/science.metrics.js +335 -0
  28. package/dist/engine/narrativeInjection.js +305 -0
  29. package/dist/engine/policyEnforcement.js +1611 -0
  30. package/dist/engine/policyEngine.js +799 -0
  31. package/dist/engine/primeRadiant.js +540 -0
  32. package/dist/engine/reasoningEngine.js +57 -3
  33. package/dist/engine/reportEngine.js +97 -0
  34. package/dist/engine/scenarioComparison.js +463 -0
  35. package/dist/engine/scenarioLibrary.js +231 -0
  36. package/dist/engine/swarmSimulation.js +54 -1
  37. package/dist/engine/worldComparison.js +358 -0
  38. package/dist/engine/worldStorage.js +232 -0
  39. package/dist/favicon.ico +0 -0
  40. package/dist/index.html +23 -0
  41. package/dist/lib/reasoningEngine.js +290 -0
  42. package/dist/lib/simulationAdapter.js +686 -0
  43. package/dist/lib/swarmParser.js +291 -0
  44. package/dist/lib/types.js +2 -0
  45. package/dist/lib/utils.js +8 -0
  46. package/dist/placeholder.svg +1 -0
  47. package/dist/robots.txt +14 -0
  48. package/dist/runtime/govern.js +473 -0
  49. package/dist/runtime/index.js +75 -0
  50. package/dist/runtime/types.js +11 -0
  51. package/package.json +17 -12
  52. package/variants/.gitkeep +0 -0
@@ -51,9 +51,14 @@ const analyzer_1 = require("./analyzer");
51
51
  const api_1 = require("./api");
52
52
  const governedSimulation_1 = require("./governedSimulation");
53
53
  const chaosEngine_1 = require("./chaosEngine");
54
+ const worldComparison_1 = require("./worldComparison");
55
+ const liveVisualizer_1 = require("./liveVisualizer");
56
+ const narrativeInjection_1 = require("./narrativeInjection");
57
+ const scenarioLibrary_1 = require("./scenarioLibrary");
54
58
  const scenarioCapsule_1 = require("./scenarioCapsule");
59
+ const behavioralAnalysis_1 = require("./behavioralAnalysis");
55
60
  const fs = __importStar(require("fs"));
56
- const http = __importStar(require("http"));
61
+ const child_process_1 = require("child_process");
57
62
  // ============================================
58
63
  // CLI ARGUMENT PARSING
59
64
  // ============================================
@@ -68,7 +73,8 @@ async function main() {
68
73
  }
69
74
  switch (command) {
70
75
  case "compare": {
71
- const presetId = args[1] || "trading";
76
+ const presetId = (args[1] && !args[1].startsWith("--")) ? args[1] : "trading";
77
+ const narrativeEvents = (0, narrativeInjection_1.parseInjectArgs)(args);
72
78
  console.log(`\n NV-SIM — Governed Simulation Comparison\n`);
73
79
  console.log(` "We ran the same simulation twice.`);
74
80
  console.log(` The only difference was the world rules."\n`);
@@ -78,7 +84,7 @@ async function main() {
78
84
  if (presetId === "trading" || presetId === "flash_crash") {
79
85
  console.log(` Scenario: Flash Crash — Algorithmic Cascade\n`);
80
86
  worldId = "trading-flash-crash";
81
- result = await (0, governedSimulation_1.runGovernedComparison)(governedSimulation_1.TRADING_DEMO.scenario, governedSimulation_1.TRADING_DEMO.world, governedSimulation_1.TRADING_DEMO.paths);
87
+ result = await (0, governedSimulation_1.runGovernedComparison)(governedSimulation_1.TRADING_DEMO.scenario, governedSimulation_1.TRADING_DEMO.world, governedSimulation_1.TRADING_DEMO.paths, narrativeEvents.length > 0 ? narrativeEvents : undefined);
82
88
  }
83
89
  else {
84
90
  // Try to use an existing preset
@@ -126,11 +132,135 @@ async function main() {
126
132
  harms_stakeholders: [],
127
133
  },
128
134
  ];
129
- result = await (0, governedSimulation_1.runGovernedComparison)(request, template.world.inline_definition, paths);
135
+ result = await (0, governedSimulation_1.runGovernedComparison)(request, template.world.inline_definition, paths, narrativeEvents.length > 0 ? narrativeEvents : undefined);
136
+ }
137
+ if (narrativeEvents.length > 0) {
138
+ console.log(`\n NARRATIVE INJECTIONS`);
139
+ console.log(" " + "-".repeat(50));
140
+ for (const ne of narrativeEvents) {
141
+ console.log(` [Round ${ne.round}] "${ne.headline}" (${ne.severity}, ${ne.propagation})`);
142
+ }
130
143
  }
131
144
  printComparisonResults(result, worldId);
132
145
  break;
133
146
  }
147
+ case "worlds": {
148
+ const worldAId = args[1];
149
+ const worldBId = args[2];
150
+ if (!worldAId || !worldBId) {
151
+ console.error(" Usage: npx nv-sim worlds <world-a> <world-b>");
152
+ console.error(" Example: npx nv-sim worlds trading strait_of_hormuz");
153
+ console.error(` Available: trading, ${Object.keys(scenarioCapsule_1.SCENARIO_TEMPLATES).join(", ")}`);
154
+ process.exit(1);
155
+ }
156
+ console.log(`\n NV-SIM — World Comparison\n`);
157
+ console.log(` "Same agents. Different rules.`);
158
+ console.log(` The world shapes the outcome."\n`);
159
+ console.log(" " + "=".repeat(60));
160
+ const wcResult = await (0, worldComparison_1.runWorldComparison)(worldAId, worldBId, {
161
+ onProgress: (label, phase) => {
162
+ console.log(` ${label}: ${phase}...`);
163
+ },
164
+ });
165
+ printWorldComparisonResults(wcResult);
166
+ break;
167
+ }
168
+ case "scenario": {
169
+ const scenarioId = args[1];
170
+ const doCompare = args.includes("--compare");
171
+ if (!scenarioId || scenarioId.startsWith("--")) {
172
+ console.error(" Usage: nv-sim scenario <scenario-id> [--compare]");
173
+ console.error(` Available: ${Object.keys(scenarioLibrary_1.SCENARIO_LIBRARY).join(", ")}`);
174
+ console.error(" Run `nv-sim scenarios` to see all scenarios.");
175
+ process.exit(1);
176
+ }
177
+ const scenario = scenarioLibrary_1.SCENARIO_LIBRARY[scenarioId];
178
+ if (!scenario) {
179
+ console.error(` Unknown scenario: ${scenarioId}`);
180
+ console.error(` Available: ${Object.keys(scenarioLibrary_1.SCENARIO_LIBRARY).join(", ")}`);
181
+ process.exit(1);
182
+ }
183
+ console.log(`\n NV-SIM — Scenario: ${scenario.title}\n`);
184
+ console.log(` ${scenario.description}\n`);
185
+ console.log(" " + "=".repeat(60));
186
+ console.log(` World: ${scenario.world}`);
187
+ console.log(` Events:`);
188
+ const scenarioEvents = (0, scenarioLibrary_1.resolveScenarioEvents)(scenario);
189
+ for (const ev of scenarioEvents) {
190
+ console.log(` [Round ${ev.round}] ${ev.headline} (${ev.severity}, ${ev.propagation})`);
191
+ }
192
+ console.log();
193
+ // Resolve world
194
+ const { resolveWorld } = await Promise.resolve().then(() => __importStar(require("./worldComparison")));
195
+ const resolved = resolveWorld(scenario.world);
196
+ const rounds = scenario.rounds ?? resolved.swarm.rounds ?? 5;
197
+ const request = {
198
+ scenario: resolved.scenario,
199
+ stakeholders: resolved.stakeholders,
200
+ assumptions: resolved.assumptions,
201
+ constraints: resolved.constraints,
202
+ depth: resolved.depth,
203
+ swarm: { ...resolved.swarm, rounds },
204
+ };
205
+ // Run primary scenario
206
+ console.log(` Running scenario on world "${scenario.world}"...`);
207
+ const primaryResult = await (0, governedSimulation_1.runGovernedComparison)(request, resolved.world, resolved.paths, scenarioEvents);
208
+ printComparisonResults(primaryResult, scenario.world);
209
+ // --compare: run across additional worlds
210
+ if (doCompare && scenario.compareWorlds && scenario.compareWorlds.length > 0) {
211
+ console.log(`\n ${"=".repeat(60)}`);
212
+ console.log(` CROSS-WORLD COMPARISON`);
213
+ console.log(` Running same scenario across ${scenario.compareWorlds.length} additional world(s)...\n`);
214
+ for (const altWorldId of scenario.compareWorlds) {
215
+ const altResolved = resolveWorld(altWorldId);
216
+ const altRounds = scenario.rounds ?? altResolved.swarm.rounds ?? 5;
217
+ const altRequest = {
218
+ scenario: altResolved.scenario,
219
+ stakeholders: altResolved.stakeholders,
220
+ assumptions: altResolved.assumptions,
221
+ constraints: altResolved.constraints,
222
+ depth: altResolved.depth,
223
+ swarm: { ...altResolved.swarm, rounds: altRounds },
224
+ };
225
+ console.log(` Running on world "${altWorldId}"...`);
226
+ const altResult = await (0, governedSimulation_1.runGovernedComparison)(altRequest, altResolved.world, altResolved.paths, scenarioEvents);
227
+ // Print condensed comparison
228
+ const pm = primaryResult.governed.metrics;
229
+ const am = altResult.governed.metrics;
230
+ console.log(`\n ${scenario.world} vs ${altWorldId}`);
231
+ console.log(" " + "-".repeat(50));
232
+ console.log(` ${scenario.world.padEnd(25)} Stability: ${(pm.stabilityScore * 100).toFixed(0)}% Collapse: ${(pm.collapseProbability * 100).toFixed(0)}% Volatility: ${(pm.maxVolatility * 100).toFixed(0)}%`);
233
+ console.log(` ${altWorldId.padEnd(25)} Stability: ${(am.stabilityScore * 100).toFixed(0)}% Collapse: ${(am.collapseProbability * 100).toFixed(0)}% Volatility: ${(am.maxVolatility * 100).toFixed(0)}%`);
234
+ const stabDiff = pm.stabilityScore - am.stabilityScore;
235
+ const winner = Math.abs(stabDiff) < 0.02 ? "tie" : stabDiff > 0 ? scenario.world : altWorldId;
236
+ console.log(` More resilient: ${winner === "tie" ? "roughly equal" : winner}`);
237
+ }
238
+ console.log(`\n Same events. Different rules. Different outcomes.`);
239
+ }
240
+ console.log(`\n Design rules. Run reality. See what changes.`);
241
+ console.log(` neuroverse-simulations | @neuroverseos\n`);
242
+ break;
243
+ }
244
+ case "scenarios": {
245
+ console.log(`\n NV-SIM — Scenario Library\n`);
246
+ console.log(` "Named stress scenarios — run a crisis, not a config."\n`);
247
+ console.log(" " + "=".repeat(60));
248
+ const grouped = (0, scenarioLibrary_1.getScenariosByCategory)();
249
+ for (const [category, scenarios] of Object.entries(grouped)) {
250
+ console.log(`\n ${category.toUpperCase()}`);
251
+ console.log(" " + "-".repeat(40));
252
+ for (const s of scenarios) {
253
+ console.log(` ${s.id.padEnd(28)} ${s.title}`);
254
+ console.log(` ${"".padEnd(28)} ${s.description}`);
255
+ console.log(` ${"".padEnd(28)} World: ${s.world} | Events: ${s.events.length}${s.compareWorlds ? ` | Compare: ${s.compareWorlds.join(", ")}` : ""}\n`);
256
+ }
257
+ }
258
+ console.log(" " + "=".repeat(60));
259
+ console.log(` Usage: nv-sim scenario <id>`);
260
+ console.log(` nv-sim scenario <id> --compare (cross-world comparison)`);
261
+ console.log(`\n Example: nv-sim scenario taiwan_crisis --compare\n`);
262
+ break;
263
+ }
134
264
  case "analyze": {
135
265
  const filePath = args[1];
136
266
  let input;
@@ -279,47 +409,253 @@ async function main() {
279
409
  }
280
410
  case "visualize":
281
411
  case "view": {
282
- const presetId = args[1] || "trading";
283
- console.log(`\n NV-SIM Running simulation for visualization...\n`);
284
- let comparisonResult;
285
- if (presetId === "trading" || presetId === "flash_crash") {
286
- comparisonResult = await (0, governedSimulation_1.runGovernedComparison)(governedSimulation_1.TRADING_DEMO.scenario, governedSimulation_1.TRADING_DEMO.world, governedSimulation_1.TRADING_DEMO.paths);
412
+ console.log(`\n NV-SIM Scenario Control Platform\n`);
413
+ const port = 3456;
414
+ (0, liveVisualizer_1.startInteractiveServer)(port, (url) => {
415
+ console.log(` Dashboard: ${url}`);
416
+ console.log(" Interactive world controls ready.\n");
417
+ console.log(" Change world rules, inject narrative events, run simulations.");
418
+ console.log(" Press Ctrl+C to stop.\n");
419
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
420
+ (0, child_process_1.exec)(`${openCmd} ${url}`, () => { });
421
+ });
422
+ await new Promise(() => { }); // block forever
423
+ break;
424
+ }
425
+ case "serve":
426
+ case "govern": {
427
+ // Local governance runtime — external simulators POST to /api/evaluate
428
+ const governPort = args.includes("--port") ? parseInt(args[args.indexOf("--port") + 1], 10) : 3456;
429
+ console.log(`\n NV-SIM — Local Governance Runtime\n`);
430
+ console.log(` Runs on YOUR machine. No cloud. No cost.\n`);
431
+ console.log(" " + "=".repeat(60));
432
+ console.log(` Endpoint: http://localhost:${governPort}/api/evaluate`);
433
+ console.log(` Method: POST`);
434
+ console.log(` Contract: { actor, action, payload?, state?, world? }`);
435
+ console.log(` Response: { decision: ALLOW|BLOCK|MODIFY, reason, evidence }`);
436
+ console.log(" " + "=".repeat(60));
437
+ (0, liveVisualizer_1.startInteractiveServer)(governPort, (url) => {
438
+ console.log(`\n Server ready: ${url}`);
439
+ console.log(` Governance: ${url}/api/evaluate`);
440
+ console.log(` Dashboard: ${url}`);
441
+ console.log(` SSE stream: ${url}/events`);
442
+ console.log("");
443
+ console.log(" Session & Reporting:");
444
+ console.log(` Stats: GET ${url}/api/session`);
445
+ console.log(` Report: GET ${url}/api/session/report`);
446
+ console.log(` Report JSON: GET ${url}/api/session/report.json`);
447
+ console.log(` Reset: POST ${url}/api/session/reset`);
448
+ console.log(` Save: POST ${url}/api/session/save\n`);
449
+ console.log(" Workflow: evaluate → report → apply new rules → reset → compare");
450
+ console.log(" Waiting for simulator connections...");
451
+ console.log(" Press Ctrl+C to stop.\n");
452
+ });
453
+ await new Promise(() => { }); // block forever
454
+ break;
455
+ }
456
+ case "run": {
457
+ // Run an external simulator with governance
458
+ const simId = args[1] ?? "mirofish";
459
+ const runPort = args.includes("--port") ? parseInt(args[args.indexOf("--port") + 1], 10) : 3456;
460
+ const noViewer = args.includes("--no-viewer");
461
+ console.log(`\n NV-SIM — Governed Simulation Runner\n`);
462
+ console.log(` Simulator: ${simId}`);
463
+ console.log(" " + "=".repeat(60));
464
+ // Step 1: Start governance server
465
+ console.log(` [1/3] Starting governance server on port ${runPort}...`);
466
+ const { createAdapter: ca, ADAPTER_REGISTRY: ar } = await Promise.resolve().then(() => __importStar(require("./liveAdapter")));
467
+ (0, liveVisualizer_1.startInteractiveServer)(runPort, (url) => {
468
+ console.log(` [1/3] Governance ready: ${url}/api/evaluate`);
469
+ // Step 2: Start simulator via live adapter
470
+ console.log(` [2/3] Starting ${simId} simulator...`);
471
+ // Check if we have a registry entry, otherwise try raw command
472
+ const adapterEntry = ar[simId];
473
+ if (!adapterEntry) {
474
+ console.error(` Unknown simulator: ${simId}`);
475
+ console.error(` Available: ${Object.keys(ar).join(", ")}`);
476
+ console.error(` Or provide command: nv-sim run generic --command "python sim.py"`);
477
+ process.exit(1);
478
+ }
479
+ const adapterOpts = {};
480
+ if (args.includes("--command")) {
481
+ adapterOpts.command = args[args.indexOf("--command") + 1];
482
+ }
483
+ if (args.includes("--args")) {
484
+ adapterOpts.args = args[args.indexOf("--args") + 1];
485
+ }
486
+ const adapter = ca(simId, adapterOpts);
487
+ if (!adapter) {
488
+ console.error(` Failed to create adapter for: ${simId}`);
489
+ process.exit(1);
490
+ }
491
+ let roundCount = 0;
492
+ adapter.on("round", (round) => {
493
+ roundCount++;
494
+ const actions = round.agentActions?.length ?? 0;
495
+ const events = round.systemEvents?.length ?? 0;
496
+ console.log(` [round ${round.round}] ${actions} actions, ${events} events`);
497
+ });
498
+ adapter.on("complete", () => {
499
+ console.log(`\n Simulation complete. ${roundCount} rounds processed.`);
500
+ console.log(` Dashboard: http://localhost:${runPort}`);
501
+ if (noViewer)
502
+ process.exit(0);
503
+ });
504
+ adapter.on("error", (err) => {
505
+ console.error(` Simulator error: ${err.message}`);
506
+ });
507
+ adapter.start().then(() => {
508
+ console.log(` [2/3] Simulator started.`);
509
+ console.log(` [3/3] Streaming governed output...\n`);
510
+ }).catch((err) => {
511
+ console.error(` Failed to start simulator: ${err.message}`);
512
+ process.exit(1);
513
+ });
514
+ // Open viewer unless --no-viewer
515
+ if (!noViewer) {
516
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
517
+ (0, child_process_1.exec)(`${openCmd} ${url}`, () => { });
518
+ }
519
+ });
520
+ await new Promise(() => { }); // block forever
521
+ break;
522
+ }
523
+ case "world-from-doc": {
524
+ // Generate a world file from a plain-English document
525
+ const docPath = args[1];
526
+ if (!docPath) {
527
+ console.error("\n Usage: npx nv-sim world-from-doc <file>\n");
528
+ console.error(" Reads a document (.md, .txt, .pdf) and generates a world definition.\n");
529
+ console.error(" Examples:");
530
+ console.error(" npx nv-sim world-from-doc policy.md");
531
+ console.error(" npx nv-sim world-from-doc regulations.txt --output my-world.json");
532
+ console.error(" cat rules.txt | npx nv-sim world-from-doc -\n");
533
+ process.exit(1);
534
+ }
535
+ const fs = await Promise.resolve().then(() => __importStar(require("fs")));
536
+ const path = await Promise.resolve().then(() => __importStar(require("path")));
537
+ let docText;
538
+ if (docPath === "-") {
539
+ // Read from stdin
540
+ const chunks = [];
541
+ for await (const chunk of process.stdin) {
542
+ chunks.push(Buffer.from(chunk));
543
+ }
544
+ docText = Buffer.concat(chunks).toString("utf-8");
287
545
  }
288
546
  else {
289
- const template = scenarioCapsule_1.SCENARIO_TEMPLATES[presetId];
290
- if (!template || !template.world?.inline_definition) {
291
- console.error(` Unknown or incomplete preset: ${presetId}`);
547
+ const resolved = path.resolve(docPath);
548
+ if (!fs.existsSync(resolved)) {
549
+ console.error(`\n Error: File not found: ${resolved}\n`);
292
550
  process.exit(1);
293
551
  }
294
- const request = {
295
- scenario: template.scenario,
296
- stakeholders: template.stakeholders,
297
- assumptions: template.assumptions,
298
- constraints: template.constraints,
299
- depth: template.depth,
300
- swarm: template.swarm,
301
- };
302
- const paths = [
303
- {
304
- id: "path_primary", label: "Primary Response",
305
- description: "Most likely course of action",
306
- projected_outcome: "Moderate disruption", probability: 0.6,
307
- risk: "high", tradeoffs: ["Speed vs. thoroughness"],
308
- benefits_stakeholders: template.stakeholders.filter(s => s.disposition === "supportive").map(s => s.id),
309
- harms_stakeholders: template.stakeholders.filter(s => s.disposition === "hostile").map(s => s.id),
310
- },
311
- {
312
- id: "path_defensive", label: "Defensive Posture",
313
- description: "Conservative approach", projected_outcome: "Reduced damage",
314
- probability: 0.7, risk: "moderate", tradeoffs: ["Safety vs. opportunity"],
315
- benefits_stakeholders: template.stakeholders.filter(s => s.disposition !== "hostile").map(s => s.id),
316
- harms_stakeholders: [],
317
- },
318
- ];
319
- comparisonResult = await (0, governedSimulation_1.runGovernedComparison)(request, template.world.inline_definition, paths);
552
+ docText = fs.readFileSync(resolved, "utf-8");
320
553
  }
321
- console.log(" Starting local visualizer...\n");
322
- await startVisualizer(comparisonResult, presetId);
554
+ console.log(`\n NV-SIM World File Generator\n`);
555
+ console.log(` Reading: ${docPath === "-" ? "stdin" : docPath}`);
556
+ console.log(" " + "=".repeat(60));
557
+ // Parse the document into a world definition
558
+ const lines = docText.split("\n").map(l => l.trim()).filter(l => l.length > 0);
559
+ const worldId = args.includes("--id") ? args[args.indexOf("--id") + 1] : path.basename(docPath, path.extname(docPath)).replace(/\s+/g, "_").toLowerCase();
560
+ // Extract thesis (first paragraph or heading)
561
+ let thesis = "";
562
+ const invariants = [];
563
+ const gates = [];
564
+ const stateVars = [];
565
+ let invCount = 0;
566
+ let gateCount = 0;
567
+ for (const line of lines) {
568
+ const lower = line.toLowerCase();
569
+ // Skip markdown headers but use them for context
570
+ if (line.startsWith("#")) {
571
+ if (!thesis)
572
+ thesis = line.replace(/^#+\s*/, "");
573
+ continue;
574
+ }
575
+ // First non-header line as thesis if not set
576
+ if (!thesis && line.length > 10) {
577
+ thesis = line;
578
+ continue;
579
+ }
580
+ // Detect rules: lines starting with block/prevent/limit/require/allow etc
581
+ if (/^(block|prevent|prohibit|ban|stop|forbid|no\b|don.t|limit|cap|restrict)/i.test(lower)) {
582
+ invCount++;
583
+ invariants.push({
584
+ id: `INV-${String(invCount).padStart(3, "0")}`,
585
+ description: line.replace(/^[-*•]\s*/, ""),
586
+ enforceable: true,
587
+ });
588
+ }
589
+ else if (/^(require|must|shall|ensure|mandate)/i.test(lower)) {
590
+ invCount++;
591
+ invariants.push({
592
+ id: `INV-${String(invCount).padStart(3, "0")}`,
593
+ description: line.replace(/^[-*•]\s*/, ""),
594
+ enforceable: true,
595
+ });
596
+ }
597
+ else if (/^(allow|permit|enable)/i.test(lower)) {
598
+ invCount++;
599
+ invariants.push({
600
+ id: `INV-${String(invCount).padStart(3, "0")}`,
601
+ description: line.replace(/^[-*•]\s*/, ""),
602
+ enforceable: false,
603
+ });
604
+ }
605
+ else if (/^(warn|alert|flag|monitor|watch)/i.test(lower)) {
606
+ gateCount++;
607
+ gates.push({
608
+ id: `GATE-${String(gateCount).padStart(3, "0")}`,
609
+ label: line.replace(/^[-*•]\s*/, ""),
610
+ condition: lower,
611
+ severity: /critical|extreme|emergency/i.test(lower) ? "critical" : "warning",
612
+ });
613
+ }
614
+ else if (/^[-*•]\s*/.test(line)) {
615
+ // Bullet points are probably rules
616
+ invCount++;
617
+ invariants.push({
618
+ id: `INV-${String(invCount).padStart(3, "0")}`,
619
+ description: line.replace(/^[-*•]\s*/, ""),
620
+ enforceable: true,
621
+ });
622
+ }
623
+ }
624
+ if (!thesis)
625
+ thesis = "Governance rules from " + (docPath === "-" ? "document" : docPath);
626
+ const worldDef = {
627
+ thesis,
628
+ state_variables: stateVars,
629
+ invariants,
630
+ gates,
631
+ };
632
+ // Wrap in SavedWorld format so enforce can consume it directly
633
+ const { createSavedWorld: createWorld } = await Promise.resolve().then(() => __importStar(require("./worldStorage")));
634
+ const savedWorld = createWorld(worldId, worldDef, "document", {
635
+ description: `Generated from ${docPath === "-" ? "stdin" : docPath}`,
636
+ });
637
+ // Output
638
+ const outputPath = args.includes("--output") ? args[args.indexOf("--output") + 1] : null;
639
+ const json = JSON.stringify(savedWorld, null, 2);
640
+ if (outputPath) {
641
+ fs.writeFileSync(outputPath, json, "utf-8");
642
+ console.log(`\n Generated: ${outputPath}`);
643
+ }
644
+ else {
645
+ console.log("\n" + json);
646
+ }
647
+ console.log(`\n Summary:`);
648
+ console.log(` Thesis: ${thesis}`);
649
+ console.log(` Invariants: ${invariants.length}`);
650
+ console.log(` Gates: ${gates.length}`);
651
+ console.log(`\n This world file is saved for reuse.`);
652
+ console.log(` You don't need this step — enforce accepts .txt directly:`);
653
+ console.log(` npx nv-sim enforce trading ${docPath === "-" ? "policy.txt" : docPath}`);
654
+ if (outputPath) {
655
+ console.log(`\n But since you saved it, you can also:`);
656
+ console.log(` npx nv-sim enforce trading ${outputPath}`);
657
+ }
658
+ console.log("");
323
659
  break;
324
660
  }
325
661
  case "preset": {
@@ -350,6 +686,864 @@ async function main() {
350
686
  console.log(" Usage: npx nv-sim preset <preset_id>\n");
351
687
  break;
352
688
  }
689
+ // ============================================
690
+ // WORLD SAVE / LOAD / LIST
691
+ // ============================================
692
+ case "world:save": {
693
+ const sourcePreset = args[1];
694
+ const outputPath = args[2] || `${sourcePreset || "world"}.nv-world.json`;
695
+ const nameArg = args.find(a => a.startsWith("--name="))?.slice(7);
696
+ if (!sourcePreset) {
697
+ console.error(" Usage: npx nv-sim world:save <preset|file.json> [output.nv-world.json] [--name=My World]");
698
+ console.error(" Available presets: trading, strait_of_hormuz, gas_price_spike, ai_regulation_crisis");
699
+ process.exit(1);
700
+ }
701
+ try {
702
+ const { resolveWorld } = await Promise.resolve().then(() => __importStar(require("./worldComparison")));
703
+ const { createSavedWorld, saveWorldToFile } = await Promise.resolve().then(() => __importStar(require("./worldStorage")));
704
+ const resolved = resolveWorld(sourcePreset);
705
+ const name = nameArg || resolved.title;
706
+ const saved = createSavedWorld(name, resolved.world, sourcePreset, {
707
+ description: `Saved from CLI: ${resolved.title}`,
708
+ });
709
+ await saveWorldToFile(saved, outputPath);
710
+ console.log(`\n World saved: ${outputPath}`);
711
+ console.log(` Name: ${saved.name}`);
712
+ console.log(` Thesis: ${saved.world.thesis}`);
713
+ console.log(` Rules: ${saved.world.invariants.length} invariants, ${(saved.world.gates ?? []).length} gates`);
714
+ console.log(` ID: ${saved.id}\n`);
715
+ }
716
+ catch (err) {
717
+ console.error(` Failed to save world: ${err instanceof Error ? err.message : err}`);
718
+ process.exit(1);
719
+ }
720
+ break;
721
+ }
722
+ case "world:load": {
723
+ const filePath = args[1];
724
+ if (!filePath) {
725
+ console.error(" Usage: npx nv-sim world:load <file.nv-world.json>");
726
+ process.exit(1);
727
+ }
728
+ try {
729
+ const { loadWorldFromFile } = await Promise.resolve().then(() => __importStar(require("./worldStorage")));
730
+ const world = await loadWorldFromFile(filePath);
731
+ console.log(`\n World loaded: ${filePath}`);
732
+ console.log(` Name: ${world.name}`);
733
+ console.log(` Thesis: ${world.world.thesis}`);
734
+ console.log(` Base: ${world.basePreset}`);
735
+ console.log(` Rules: ${world.world.invariants.length} invariants, ${(world.world.gates ?? []).length} gates`);
736
+ console.log(` Created: ${world.createdAt}`);
737
+ console.log(` ID: ${world.id}\n`);
738
+ console.log(" INVARIANTS:");
739
+ for (const inv of world.world.invariants) {
740
+ console.log(` [${inv.enforceable ? "ENFORCED" : "ADVISORY"}] ${inv.description}`);
741
+ }
742
+ if (world.world.gates?.length) {
743
+ console.log("\n GATES:");
744
+ for (const gate of world.world.gates) {
745
+ console.log(` [${gate.severity.toUpperCase()}] ${gate.label}: ${gate.condition}`);
746
+ }
747
+ }
748
+ console.log();
749
+ }
750
+ catch (err) {
751
+ console.error(` Failed to load world: ${err instanceof Error ? err.message : err}`);
752
+ process.exit(1);
753
+ }
754
+ break;
755
+ }
756
+ case "world:list": {
757
+ const { getAvailableWorlds, explainWorldGovernance } = await Promise.resolve().then(() => __importStar(require("./worldComparison")));
758
+ const worlds = getAvailableWorlds();
759
+ console.log(`\n NV-SIM — Available Worlds\n`);
760
+ console.log(" " + "=".repeat(60));
761
+ for (const w of worlds) {
762
+ console.log(`\n ${w.id}`);
763
+ console.log(` ${w.title}`);
764
+ console.log(` ${w.thesis.slice(0, 80)}${w.thesis.length > 80 ? "..." : ""}`);
765
+ console.log(` ${w.invariantCount} rules, ${w.gateCount} gates`);
766
+ }
767
+ console.log(`\n Total: ${worlds.length} worlds available\n`);
768
+ console.log(" Use: npx nv-sim worlds <worldA> <worldB> to compare");
769
+ console.log(" Use: npx nv-sim world:save <preset> to save as JSON\n");
770
+ break;
771
+ }
772
+ case "world:compare": {
773
+ // Compare two saved world files
774
+ const fileA = args[1];
775
+ const fileB = args[2];
776
+ if (!fileA || !fileB) {
777
+ console.error(" Usage: npx nv-sim world:compare <worldA.json> <worldB.json>");
778
+ process.exit(1);
779
+ }
780
+ try {
781
+ const { loadWorldFromFile, saveWorldToStorage } = await Promise.resolve().then(() => __importStar(require("./worldStorage")));
782
+ const worldA = await loadWorldFromFile(fileA);
783
+ const worldB = await loadWorldFromFile(fileB);
784
+ // Temporarily register in storage so resolveWorld can find them
785
+ saveWorldToStorage(worldA);
786
+ saveWorldToStorage(worldB);
787
+ console.log(`\n NV-SIM — World File Comparison\n`);
788
+ console.log(` World A: ${worldA.name} (${fileA})`);
789
+ console.log(` World B: ${worldB.name} (${fileB})`);
790
+ console.log(" " + "=".repeat(60));
791
+ const compResult = await (0, worldComparison_1.runWorldComparison)(worldA.id, worldB.id, {
792
+ onProgress: (label, phase) => console.log(` ${label}: ${phase}...`),
793
+ });
794
+ const { generateComparisonImpact } = await Promise.resolve().then(() => __importStar(require("./worldComparison")));
795
+ const impact = generateComparisonImpact(compResult);
796
+ printWorldComparisonResults(compResult, impact);
797
+ }
798
+ catch (err) {
799
+ console.error(` Comparison failed: ${err instanceof Error ? err.message : err}`);
800
+ process.exit(1);
801
+ }
802
+ break;
803
+ }
804
+ // ============================================
805
+ // PRIME RADIANT — Full Policy Enforcement System
806
+ // ============================================
807
+ case "radiant":
808
+ case "prime": {
809
+ const presetId = (args[1] && !args[1].startsWith("--")) ? args[1] : undefined;
810
+ const mode = args.includes("--compare") ? "compare" : args.includes("--world") ? "world" : "simulate";
811
+ console.log(`\n PRIME RADIANT — Policy Enforcement System\n`);
812
+ console.log(` "govern() controls actions. governDynamics() controls outcomes."\n`);
813
+ console.log(" " + "=".repeat(60));
814
+ const { createPrimeRadiant, PRIME_RADIANT_PRESETS } = await Promise.resolve().then(() => __importStar(require("./primeRadiant")));
815
+ // Resolve preset or use default
816
+ const presetKey = presetId && (presetId in PRIME_RADIANT_PRESETS)
817
+ ? presetId
818
+ : "university_crisis";
819
+ const preset = PRIME_RADIANT_PRESETS[presetKey];
820
+ const narrativeEvents = (0, narrativeInjection_1.parseInjectArgs)(args);
821
+ console.log(` Preset: ${presetKey}`);
822
+ const radiant = createPrimeRadiant({
823
+ scenario: preset.scenario,
824
+ policyText: preset.policyText,
825
+ stakeholders: preset.stakeholders,
826
+ narrativeEvents: narrativeEvents.length > 0 ? narrativeEvents : undefined,
827
+ });
828
+ if (mode === "world") {
829
+ // MODE 1: Build world
830
+ console.log(` Mode: WORLD BUILDER\n`);
831
+ const world = radiant.buildWorld();
832
+ console.log(` Policy Health: ${world.health.healthScore}/100`);
833
+ console.log(` Total Rules: ${world.coverageReport.totalRules}`);
834
+ console.log(` Enforced: ${world.coverageReport.enforcedRules}`);
835
+ console.log(` Advisory: ${world.coverageReport.advisoryRules}`);
836
+ console.log(` Dynamics: ${world.coverageReport.dynamicsRulesDetected}`);
837
+ console.log(` Thesis: ${world.world.thesis}`);
838
+ if (world.coverageReport.uncoveredCritical.length > 0) {
839
+ console.log(`\n UNCOVERED CRITICAL VARIABLES:`);
840
+ for (const v of world.coverageReport.uncoveredCritical) {
841
+ console.log(` - ${v}`);
842
+ }
843
+ }
844
+ console.log(`\n INVARIANTS:`);
845
+ for (const inv of world.world.invariants) {
846
+ console.log(` [${inv.enforceable ? "ENFORCED" : "ADVISORY"}] ${inv.description}`);
847
+ }
848
+ if (world.world.gates && world.world.gates.length > 0) {
849
+ console.log(`\n GATES:`);
850
+ for (const gate of world.world.gates) {
851
+ console.log(` [${gate.severity.toUpperCase()}] ${gate.label}`);
852
+ }
853
+ }
854
+ }
855
+ else if (mode === "compare") {
856
+ // MODE 3: Decision Intelligence
857
+ console.log(` Mode: DECISION INTELLIGENCE\n`);
858
+ const options = preset.policyOptions;
859
+ console.log(` Comparing ${options.length} policy options...\n`);
860
+ for (const opt of options) {
861
+ console.log(` ${opt.id.padEnd(20)} ${opt.label}`);
862
+ }
863
+ console.log();
864
+ const result = await radiant.compareOptions(options);
865
+ // Print decision brief
866
+ console.log(result.decisionBrief);
867
+ // Print dynamics details
868
+ console.log(`\n DYNAMICS GOVERNANCE`);
869
+ console.log(" " + "-".repeat(50));
870
+ for (const optResult of result.optionResults) {
871
+ const d = optResult.simulation.dynamicsGovernance;
872
+ console.log(` ${optResult.optionLabel}:`);
873
+ console.log(` Interventions: ${d.totalInterventions} (propagation: ${d.propagationLimits}, amplification: ${d.amplificationDampens}, cascade: ${d.cascadeBreakers}, trust: ${d.trustBoosts}, cooling: ${d.coolingPeriods})`);
874
+ console.log(` Peak outrage: ${(d.peakOutrage * 100).toFixed(0)}% | Peak polarization: ${(d.peakPolarization * 100).toFixed(0)}% | Avg trust: ${(d.averageTrust * 100).toFixed(0)}%`);
875
+ }
876
+ // Print recommendation
877
+ console.log(`\n RECOMMENDATION: ${result.recommendation.optionLabel}`);
878
+ console.log(` Confidence: ${(result.recommendation.confidence * 100).toFixed(0)}%`);
879
+ console.log(` ${result.recommendation.rationale}`);
880
+ }
881
+ else {
882
+ // MODE 2: Governed Simulation
883
+ console.log(` Mode: GOVERNED SIMULATION\n`);
884
+ console.log(` Running dual-layer governance...`);
885
+ console.log(` Layer A: govern() — per-action, per-agent`);
886
+ console.log(` Layer B: governDynamics() — per-round, system-level\n`);
887
+ const result = await radiant.runGovernedSimulation();
888
+ const g = result.governed;
889
+ const b = result.baseline;
890
+ const d = result.delta;
891
+ // System state timeline
892
+ console.log(` STATE EVOLUTION`);
893
+ console.log(" " + "-".repeat(60));
894
+ console.log(` ${"Round".padEnd(8)} ${"Trust".padEnd(10)} ${"Outrage".padEnd(10)} ${"Polar.".padEnd(10)} ${"Cascade".padEnd(10)} ${"Trajectory"}`);
895
+ for (const snap of g.stateTimeline) {
896
+ const round = g.rounds.find(r => r.round === snap.round);
897
+ console.log(` ${String(snap.round).padEnd(8)} ${(snap.trust * 100).toFixed(0).padStart(4)}% ${(snap.outrage * 100).toFixed(0).padStart(4)}% ${(snap.polarization * 100).toFixed(0).padStart(4)}% ${(snap.cascadeRisk * 100).toFixed(0).padStart(4)}% ${round?.dynamicsTrajectory ?? "-"}`);
898
+ }
899
+ // Action governance
900
+ console.log(`\n ACTION GOVERNANCE (Layer A)`);
901
+ console.log(" " + "-".repeat(50));
902
+ const ag = g.actionGovernance;
903
+ console.log(` Evaluations: ${ag.totalEvaluations}`);
904
+ console.log(` Allowed: ${ag.allowed} | Blocked: ${ag.blocked} | Modified: ${ag.modified} | Paused: ${ag.paused}`);
905
+ console.log(` Avg reduction: ${(ag.avgReduction * 100).toFixed(0)}% | Rules fired: ${ag.rulesFired}`);
906
+ // Dynamics governance
907
+ console.log(`\n DYNAMICS GOVERNANCE (Layer B)`);
908
+ console.log(" " + "-".repeat(50));
909
+ const dg = g.dynamicsGovernance;
910
+ console.log(` Total interventions: ${dg.totalInterventions}`);
911
+ console.log(` Propagation limits: ${dg.propagationLimits}`);
912
+ console.log(` Amplification damps: ${dg.amplificationDampens}`);
913
+ console.log(` Cascade breakers: ${dg.cascadeBreakers}`);
914
+ console.log(` Trust boosts: ${dg.trustBoosts}`);
915
+ console.log(` Cooling periods: ${dg.coolingPeriods}`);
916
+ console.log(` Peak outrage: ${(dg.peakOutrage * 100).toFixed(0)}%`);
917
+ console.log(` Peak polarization: ${(dg.peakPolarization * 100).toFixed(0)}%`);
918
+ console.log(` Peak cascade risk: ${(dg.peakCascadeRisk * 100).toFixed(0)}%`);
919
+ console.log(` Average trust: ${(dg.averageTrust * 100).toFixed(0)}%`);
920
+ // Delta
921
+ console.log(`\n GOVERNANCE IMPACT (vs ungoverned baseline)`);
922
+ console.log(" " + "=".repeat(50));
923
+ console.log(` Trust: ${d.trustDelta > 0 ? "+" : ""}${(d.trustDelta * 100).toFixed(0)} points (${(b.finalState.trust * 100).toFixed(0)}% → ${(g.finalState.trust * 100).toFixed(0)}%)`);
924
+ console.log(` Polarization: ${d.polarizationDelta < 0 ? "" : "+"}${(d.polarizationDelta * 100).toFixed(0)} points (${(b.finalState.polarization * 100).toFixed(0)}% → ${(g.finalState.polarization * 100).toFixed(0)}%)`);
925
+ console.log(` Outrage: ${d.outrageDelta < 0 ? "" : "+"}${(d.outrageDelta * 100).toFixed(0)} points (${(b.finalState.outrage * 100).toFixed(0)}% → ${(g.finalState.outrage * 100).toFixed(0)}%)`);
926
+ console.log(` Cascade risk: ${d.cascadeRiskDelta < 0 ? "" : "+"}${(d.cascadeRiskDelta * 100).toFixed(0)} points (${(b.finalState.cascadeRisk * 100).toFixed(0)}% → ${(g.finalState.cascadeRisk * 100).toFixed(0)}%)`);
927
+ console.log(` System health: ${d.healthDelta > 0 ? "+" : ""}${d.healthDelta} (${b.systemHealthScore}/100 → ${g.systemHealthScore}/100)`);
928
+ console.log(` Trajectory: ${d.trajectoryShift}`);
929
+ console.log(` Effectiveness: ${(d.governanceEffectiveness * 100).toFixed(0)}%`);
930
+ // Inflection points
931
+ if (g.inflectionPoints.length > 0) {
932
+ console.log(`\n INFLECTION POINTS`);
933
+ console.log(" " + "-".repeat(50));
934
+ for (const ip of g.inflectionPoints) {
935
+ console.log(` ${ip}`);
936
+ }
937
+ }
938
+ // Narrative
939
+ console.log(`\n NARRATIVE`);
940
+ console.log(" " + "-".repeat(50));
941
+ const words = result.narrative.split(" ");
942
+ let line = " ";
943
+ for (const word of words) {
944
+ if (line.length + word.length > 72) {
945
+ console.log(line);
946
+ line = " " + word;
947
+ }
948
+ else {
949
+ line += (line.trim() ? " " : "") + word;
950
+ }
951
+ }
952
+ if (line.trim())
953
+ console.log(line);
954
+ }
955
+ console.log(`\n ${"=".repeat(60)}`);
956
+ console.log(` govern() controls actions.`);
957
+ console.log(` governDynamics() controls outcomes.`);
958
+ console.log(` ${"=".repeat(60)}`);
959
+ console.log(`\n Design rules. Run reality. See what changes.`);
960
+ console.log(` neuroverse-simulations | @neuroverseos\n`);
961
+ break;
962
+ }
963
+ // ============================================
964
+ // MIROFISH — Governed Agent Simulation
965
+ // ============================================
966
+ case "mirofish": {
967
+ console.log(`\n MIROFISH SIMULATION — Governed by NeuroVerse\n`);
968
+ console.log(` MiroFish + NeuroVerse Governance Layer`);
969
+ console.log(` MiroFish generates emergent agent behavior.`);
970
+ console.log(` NeuroVerse enforces rules, constraints, and system stability in real time.\n`);
971
+ console.log(" " + "=".repeat(60));
972
+ const { runMiroFishComparison } = await Promise.resolve().then(() => __importStar(require("../adapters/mirofish")));
973
+ const mfMode = args.includes("--compare") ? "compare" : "governed";
974
+ if (mfMode === "compare") {
975
+ console.log(` Mode: GOVERNED vs UNGOVERNED COMPARISON\n`);
976
+ console.log(` Running simulation...`);
977
+ const comparison = runMiroFishComparison();
978
+ const gov = comparison.governed;
979
+ const ungov = comparison.ungoverned;
980
+ const gs = gov.metrics.systemState;
981
+ const us = ungov.metrics.systemState;
982
+ console.log(`\n Scenario: ${comparison.scenario}`);
983
+ console.log(" " + "=".repeat(60));
984
+ // Metric comparison
985
+ console.log(`\n ${"Metric".padEnd(30)} ${"Governed".padEnd(15)} ${"Ungoverned".padEnd(15)} ${"Delta"}`);
986
+ console.log(" " + "-".repeat(75));
987
+ const mfMetrics = [
988
+ ["Trust", gs.trust, us.trust],
989
+ ["Polarization", gs.polarization, us.polarization],
990
+ ["Outrage", gs.outrage, us.outrage],
991
+ ["Cascade Risk", gs.cascadeRisk, us.cascadeRisk],
992
+ ["Amplification", gs.amplification, us.amplification],
993
+ ];
994
+ for (const [label, gv, uv] of mfMetrics) {
995
+ const delta = gv - uv;
996
+ const deltaStr = `${delta > 0 ? "+" : ""}${(delta * 100).toFixed(0)}`;
997
+ console.log(` ${label.padEnd(30)} ${(gv * 100).toFixed(0).padStart(5)}% ${(uv * 100).toFixed(0).padStart(5)}% ${deltaStr.padStart(5)}`);
998
+ }
999
+ console.log(`\n ${"".padEnd(30)} ${"Governed".padEnd(15)} ${"Ungoverned"}`);
1000
+ console.log(" " + "-".repeat(60));
1001
+ console.log(` ${"Verdict".padEnd(30)} ${gov.verdict.padEnd(15)} ${ungov.verdict}`);
1002
+ console.log(` ${"Actions Blocked".padEnd(30)} ${String(gov.totalBlocked).padEnd(15)} ${0}`);
1003
+ console.log(` ${"Actions Modified".padEnd(30)} ${String(gov.totalModified).padEnd(15)} ${0}`);
1004
+ console.log(` ${"Trajectory".padEnd(30)} ${gov.metrics.trajectory.padEnd(15)} ${ungov.metrics.trajectory}`);
1005
+ // State timeline
1006
+ console.log(`\n STATE EVOLUTION (Trust)`);
1007
+ console.log(" " + "-".repeat(60));
1008
+ console.log(` ${"Round".padEnd(8)} ${"Governed".padEnd(15)} ${"Ungoverned".padEnd(15)} ${"Gap"}`);
1009
+ for (let i = 0; i < gov.timeline.length; i++) {
1010
+ const gt = gov.timeline[i].systemState.trust;
1011
+ const ut = ungov.timeline[i].systemState.trust;
1012
+ const gap = gt - ut;
1013
+ console.log(` ${String(i + 1).padEnd(8)} ${(gt * 100).toFixed(0).padStart(5)}% ${(ut * 100).toFixed(0).padStart(5)}% ${(gap > 0 ? "+" : "") + (gap * 100).toFixed(0).padStart(4)}`);
1014
+ }
1015
+ // Science state comparison
1016
+ const govSci = gov.metrics.scienceState;
1017
+ const ungovSci = ungov.metrics.scienceState;
1018
+ console.log(`\n SYSTEM ASSESSMENT`);
1019
+ console.log(" " + "-".repeat(60));
1020
+ console.log(` Governed: ${govSci.overallAssessment}`);
1021
+ console.log(` Ungoverned: ${ungovSci.overallAssessment}`);
1022
+ console.log(`\n This simulation was generated by MiroFish.`);
1023
+ console.log(` NeuroVerse governance altered the system trajectory in real time.`);
1024
+ }
1025
+ else {
1026
+ // Single governed run
1027
+ console.log(` Mode: GOVERNED SIMULATION\n`);
1028
+ const { createMiroFishWrapper, generateDemoSimulation } = await Promise.resolve().then(() => __importStar(require("../adapters/mirofish")));
1029
+ const demo = generateDemoSimulation();
1030
+ const wrapper = createMiroFishWrapper({ enabled: true });
1031
+ console.log(` Scenario: ${demo.scenario}\n`);
1032
+ for (let i = 0; i < demo.rounds.length; i++) {
1033
+ const round = demo.rounds[i];
1034
+ let blocked = 0;
1035
+ let modified = 0;
1036
+ const approved = [];
1037
+ for (const action of round) {
1038
+ const result = wrapper.interceptAction(action);
1039
+ if (result.wasBlocked)
1040
+ blocked++;
1041
+ else if (result.wasModified)
1042
+ modified++;
1043
+ if (!result.wasBlocked)
1044
+ approved.push(result.governed ?? action);
1045
+ }
1046
+ const cycleResult = wrapper.completeCycle({ cycle: i + 1, actions: approved });
1047
+ const m = wrapper.getMetrics();
1048
+ const flags = blocked > 0 ? ` [${blocked} blocked]` : "";
1049
+ console.log(` Round ${String(i + 1).padEnd(3)} | trust: ${(m.systemState.trust * 100).toFixed(0).padStart(3)}% | cascade: ${(m.systemState.cascadeRisk * 100).toFixed(0).padStart(3)}% | ${cycleResult.trajectory}${flags}`);
1050
+ }
1051
+ const final = wrapper.getMetrics();
1052
+ console.log(`\n FINAL STATE`);
1053
+ console.log(" " + "=".repeat(50));
1054
+ console.log(` Trust: ${(final.systemState.trust * 100).toFixed(0)}%`);
1055
+ console.log(` Cascade Risk: ${(final.systemState.cascadeRisk * 100).toFixed(0)}%`);
1056
+ console.log(` Blocked: ${final.actionStats.blocked}`);
1057
+ console.log(` Modified: ${final.actionStats.modified}`);
1058
+ console.log(` Assessment: ${final.scienceState.overallAssessment}`);
1059
+ }
1060
+ console.log(`\n ${"=".repeat(60)}`);
1061
+ console.log(` Governed MiroFish Simulation | NeuroVerse Governance Layer`);
1062
+ console.log(` ${"=".repeat(60)}`);
1063
+ console.log(`\n neuroverse-simulations | @neuroverseos\n`);
1064
+ break;
1065
+ }
1066
+ // ============================================
1067
+ // SCIENCE — Governed Scientific Discovery
1068
+ // ============================================
1069
+ case "science": {
1070
+ console.log(`\n SCIENCECLAW SIMULATION — Governed by NeuroVerse\n`);
1071
+ console.log(` Powered by ScienceClaw | Governed by NeuroVerse`);
1072
+ console.log(` ScienceClaw generates autonomous scientific discovery.`);
1073
+ console.log(` NeuroVerse enforces reproducibility, provenance, and research integrity.\n`);
1074
+ console.log(" " + "=".repeat(60));
1075
+ const { createScienceClawAdapter, generateDemoInvestigation } = await Promise.resolve().then(() => __importStar(require("../adapters/scienceclaw")));
1076
+ const { SCIENCE_METRICS, interpretScienceState } = await Promise.resolve().then(() => __importStar(require("./metrics/science.metrics")));
1077
+ const demo = generateDemoInvestigation();
1078
+ const mode = args.includes("--compare") ? "compare" : "simulate";
1079
+ if (mode === "compare") {
1080
+ // Governed vs ungoverned comparison
1081
+ console.log(` Mode: GOVERNED vs UNGOVERNED COMPARISON\n`);
1082
+ // Run governed
1083
+ console.log(` Running GOVERNED investigation...`);
1084
+ const governed = createScienceClawAdapter();
1085
+ const govResult = governed.runGovernedInvestigation(demo.artifactCycles, demo.matchesByCycle);
1086
+ // Run ungoverned (empty policy)
1087
+ console.log(` Running UNGOVERNED investigation...`);
1088
+ const ungoverned = createScienceClawAdapter({ policyText: "" });
1089
+ const ungovResult = ungoverned.runGovernedInvestigation(demo.artifactCycles, demo.matchesByCycle);
1090
+ // Print comparison
1091
+ console.log(`\n COMPARISON: Governed vs Ungoverned Discovery`);
1092
+ console.log(" " + "=".repeat(60));
1093
+ console.log(` ${"Metric".padEnd(30)} ${"Governed".padEnd(15)} ${"Ungoverned".padEnd(15)} ${"Delta"}`);
1094
+ console.log(" " + "-".repeat(75));
1095
+ const metrics = [
1096
+ ["Trust (Reproducibility)", govResult.finalState.trust, ungovResult.finalState.trust],
1097
+ ["Polarization (Fragmentation)", govResult.finalState.polarization, ungovResult.finalState.polarization],
1098
+ ["Outrage (Publication Pressure)", govResult.finalState.outrage, ungovResult.finalState.outrage],
1099
+ ["Cascade Risk (False Positives)", govResult.finalState.cascadeRisk, ungovResult.finalState.cascadeRisk],
1100
+ ["Amplification (Citation Echo)", govResult.finalState.amplification, ungovResult.finalState.amplification],
1101
+ ];
1102
+ for (const [label, gov, ungov] of metrics) {
1103
+ const delta = gov - ungov;
1104
+ const deltaStr = `${delta > 0 ? "+" : ""}${(delta * 100).toFixed(0)}`;
1105
+ console.log(` ${label.padEnd(30)} ${(gov * 100).toFixed(0).padStart(5)}% ${(ungov * 100).toFixed(0).padStart(5)}% ${deltaStr.padStart(5)}`);
1106
+ }
1107
+ console.log(`\n ${"".padEnd(30)} ${"Governed".padEnd(15)} ${"Ungoverned"}`);
1108
+ console.log(" " + "-".repeat(60));
1109
+ console.log(` ${"Verdict".padEnd(30)} ${govResult.verdict.padEnd(15)} ${ungovResult.verdict}`);
1110
+ console.log(` ${"Cycles".padEnd(30)} ${String(govResult.totalCycles).padEnd(15)} ${ungovResult.totalCycles}`);
1111
+ console.log(` ${"Artifacts Blocked".padEnd(30)} ${String(govResult.actionGovernance.blocked).padEnd(15)} ${ungovResult.actionGovernance.blocked}`);
1112
+ console.log(` ${"Interventions".padEnd(30)} ${String(govResult.dynamicsGovernance.totalInterventions).padEnd(15)} ${ungovResult.dynamicsGovernance.totalInterventions}`);
1113
+ // Science state interpretation — side by side
1114
+ const sciState = govResult.finalScienceState;
1115
+ const ungovSciState = ungovResult.finalScienceState;
1116
+ console.log(`\n SCIENCE STATE`);
1117
+ console.log(" " + "-".repeat(70));
1118
+ console.log(` ${"".padEnd(30)} ${"Governed".padEnd(20)} ${"Ungoverned"}`);
1119
+ console.log(" " + "-".repeat(70));
1120
+ console.log(` ${"Hypothesis Fragmentation".padEnd(30)} ${sciState.hypothesisFragmentation.padEnd(20)} ${ungovSciState.hypothesisFragmentation}`);
1121
+ console.log(` ${"Publication Pressure".padEnd(30)} ${sciState.publicationPressure.padEnd(20)} ${ungovSciState.publicationPressure}`);
1122
+ console.log(` ${"Reproducibility".padEnd(30)} ${sciState.reproducibilityConfidence.padEnd(20)} ${ungovSciState.reproducibilityConfidence}`);
1123
+ console.log(` ${"Citation Health".padEnd(30)} ${sciState.citationHealth.padEnd(20)} ${ungovSciState.citationHealth}`);
1124
+ console.log(` ${"False Positive Risk".padEnd(30)} ${sciState.falsePositiveRisk.padEnd(20)} ${ungovSciState.falsePositiveRisk}`);
1125
+ console.log(` ${"Discovery Velocity".padEnd(30)} ${sciState.discoveryVelocity.padEnd(20)} ${ungovSciState.discoveryVelocity}`);
1126
+ console.log(`\n Governed: ${sciState.overallAssessment}`);
1127
+ console.log(` Ungoverned: ${ungovSciState.overallAssessment}`);
1128
+ console.log(`\n This simulation was generated by ScienceClaw.`);
1129
+ console.log(` NeuroVerse governance altered the research trajectory in real time.`);
1130
+ }
1131
+ else {
1132
+ // Single governed investigation
1133
+ console.log(` Mode: GOVERNED INVESTIGATION\n`);
1134
+ console.log(` Running governed investigation (${demo.artifactCycles.length} artifact cycles)...\n`);
1135
+ const adapter = createScienceClawAdapter({
1136
+ onCycleComplete: (result) => {
1137
+ const state = result.scienceState;
1138
+ const flags = result.riskFlags.length > 0 ? ` [${result.riskFlags.length} flags]` : "";
1139
+ console.log(` Cycle ${String(result.cycle).padEnd(3)} | approved: ${result.artifactsApproved} blocked: ${result.artifactsBlocked} | ${result.dynamics.trajectory}${flags}`);
1140
+ },
1141
+ onConvergence: (cycle, assessment) => {
1142
+ console.log(`\n >>> CONVERGENCE at cycle ${cycle}: ${assessment}`);
1143
+ },
1144
+ onHalt: (reason, cycle) => {
1145
+ console.log(`\n >>> HALTED at cycle ${cycle}: ${reason}`);
1146
+ },
1147
+ });
1148
+ const result = adapter.runGovernedInvestigation(demo.artifactCycles, demo.matchesByCycle);
1149
+ // State timeline
1150
+ console.log(`\n STATE EVOLUTION`);
1151
+ console.log(" " + "-".repeat(70));
1152
+ console.log(` ${"Cycle".padEnd(8)} ${"Trust".padEnd(10)} ${"Fragment".padEnd(10)} ${"Pressure".padEnd(10)} ${"Cascade".padEnd(10)} ${"Trajectory"}`);
1153
+ for (const snap of result.stateTimeline) {
1154
+ const round = result.cycles.find(c => c.cycle === snap.round);
1155
+ console.log(` ${String(snap.round).padEnd(8)} ${(snap.trust * 100).toFixed(0).padStart(4)}% ${(snap.polarization * 100).toFixed(0).padStart(4)}% ${(snap.outrage * 100).toFixed(0).padStart(4)}% ${(snap.cascadeRisk * 100).toFixed(0).padStart(4)}% ${round?.dynamics.trajectory ?? "-"}`);
1156
+ }
1157
+ // Summary
1158
+ console.log(`\n INVESTIGATION SUMMARY`);
1159
+ console.log(" " + "=".repeat(50));
1160
+ console.log(` Verdict: ${result.verdict.toUpperCase()}`);
1161
+ console.log(` Cycles: ${result.totalCycles}`);
1162
+ console.log(` Artifacts: ${result.actionGovernance.totalEvaluations} evaluated`);
1163
+ console.log(` Approved: ${result.actionGovernance.approved}`);
1164
+ console.log(` Blocked: ${result.actionGovernance.blocked}`);
1165
+ console.log(` Modified: ${result.actionGovernance.modified}`);
1166
+ console.log(` Interventions: ${result.dynamicsGovernance.totalInterventions}`);
1167
+ console.log(` Propagation: ${result.dynamicsGovernance.propagationLimits}`);
1168
+ console.log(` Amplification: ${result.dynamicsGovernance.amplificationDampens}`);
1169
+ console.log(` Cascade Break: ${result.dynamicsGovernance.cascadeBreakers}`);
1170
+ console.log(` Trust Boost: ${result.dynamicsGovernance.trustBoosts}`);
1171
+ console.log(` Cooling: ${result.dynamicsGovernance.coolingPeriods}`);
1172
+ // Final science state
1173
+ console.log(`\n FINAL SCIENCE STATE`);
1174
+ console.log(" " + "-".repeat(50));
1175
+ const sciState = result.finalScienceState;
1176
+ console.log(` ${sciState.overallAssessment}`);
1177
+ console.log(` Reproducibility: ${sciState.reproducibilityConfidence}`);
1178
+ console.log(` Citation Health: ${sciState.citationHealth}`);
1179
+ console.log(` False Positives: ${sciState.falsePositiveRisk}`);
1180
+ }
1181
+ console.log(`\n ${"=".repeat(60)}`);
1182
+ console.log(` Powered by ScienceClaw. Governed by NeuroVerse.`);
1183
+ console.log(` ${"=".repeat(60)}`);
1184
+ console.log(`\n neuroverse-simulations | @neuroverseos\n`);
1185
+ break;
1186
+ }
1187
+ case "enforce": {
1188
+ // ==============================================
1189
+ // POLICY ENFORCEMENT — Iterative Governance Lab
1190
+ //
1191
+ // "Design rules. Run reality. See what changes."
1192
+ //
1193
+ // Same scenario, different rules, measurable divergence.
1194
+ // ==============================================
1195
+ const { createEnforcementSession, formatEnforcementReport, exportEnforcementReportJSON, formatGuidedNextSteps, formatPolicyDiagnostics, formatRuleImpactAttribution, detectSystemArchetype, createSavedExperiment, formatMultiPatchComparison, loadExperimentLibrary, formatExperimentLineage, formatSimilarExperiments, formatBestKnownStrategy, governedDetectArchetype, governedSynthesizeStrategy, governedFindSimilar } = await Promise.resolve().then(() => __importStar(require("./policyEnforcement")));
1196
+ const { createSavedWorld, forkWorld, loadWorldFromFile } = await Promise.resolve().then(() => __importStar(require("./worldStorage")));
1197
+ const { parseRulesFromText, validatePolicy, policyToWorld, applyQuickFix } = await Promise.resolve().then(() => __importStar(require("./policyEngine")));
1198
+ const presetArg = (args[1] && !args[1].startsWith("--")) ? args[1] : "trading";
1199
+ const outputJson = args.includes("--json");
1200
+ const outputFile = args.find(a => a.startsWith("--output="))?.split("=")[1];
1201
+ // Support multiple --patch flags: --patch POL-001 --patch POL-002
1202
+ const patchDiagIds = [];
1203
+ for (let i = 0; i < args.length; i++) {
1204
+ if (args[i] === "--patch" && args[i + 1] && !args[i + 1].startsWith("--")) {
1205
+ patchDiagIds.push(args[i + 1]);
1206
+ }
1207
+ else if (args[i].startsWith("--patch=")) {
1208
+ patchDiagIds.push(args[i].split("=")[1]);
1209
+ }
1210
+ }
1211
+ const patchDiagId = patchDiagIds[0];
1212
+ const compareMode = args.includes("--compare");
1213
+ const saveExperiment = args.includes("--save");
1214
+ const saveLabel = args.find(a => a.startsWith("--save="))?.split("=")[1];
1215
+ // Accept .json world files AND .txt/.md policy documents
1216
+ const inputFiles = args.filter(a => (a.endsWith(".json") || a.endsWith(".txt") || a.endsWith(".md")) && a !== outputFile);
1217
+ const worldFiles = inputFiles.filter(a => a.endsWith(".json"));
1218
+ const policyDocFiles = inputFiles.filter(a => a.endsWith(".txt") || a.endsWith(".md"));
1219
+ console.log(`\n POLICY ENFORCEMENT SYSTEM`);
1220
+ console.log(` ${"=".repeat(60)}`);
1221
+ console.log(` Design rules. Run reality. See what changes.\n`);
1222
+ console.log(` NeuroVerse is a control layer. You define the rules.`);
1223
+ console.log(` The simulation shows what happens when those rules are enforced.\n`);
1224
+ // Resolve the base scenario
1225
+ let baseRequest;
1226
+ let basePaths;
1227
+ let baseWorldDef;
1228
+ let scenarioTitle;
1229
+ if (presetArg === "trading" || presetArg === "flash_crash") {
1230
+ baseRequest = governedSimulation_1.TRADING_DEMO.scenario;
1231
+ basePaths = [...governedSimulation_1.TRADING_DEMO.paths];
1232
+ baseWorldDef = governedSimulation_1.TRADING_DEMO.world;
1233
+ scenarioTitle = "Flash Crash — Algorithmic Cascade";
1234
+ }
1235
+ else {
1236
+ const template = scenarioCapsule_1.SCENARIO_TEMPLATES[presetArg];
1237
+ if (!template || !template.world?.inline_definition) {
1238
+ console.error(` Unknown preset: ${presetArg}`);
1239
+ console.error(` Available: trading, ${Object.keys(scenarioCapsule_1.SCENARIO_TEMPLATES).join(", ")}`);
1240
+ process.exit(1);
1241
+ }
1242
+ baseRequest = {
1243
+ scenario: template.scenario,
1244
+ stakeholders: template.stakeholders,
1245
+ assumptions: template.assumptions,
1246
+ constraints: template.constraints,
1247
+ depth: "full",
1248
+ swarm: { enabled: true, rounds: 5, reaction_model: "mixed" },
1249
+ };
1250
+ basePaths = [
1251
+ {
1252
+ id: "path_primary",
1253
+ label: "Primary Response",
1254
+ description: "The most likely course of action given the scenario",
1255
+ projected_outcome: "Moderate disruption with cascading effects",
1256
+ probability: 0.6,
1257
+ risk: "high",
1258
+ tradeoffs: ["Speed vs. thoroughness", "Short-term vs. long-term"],
1259
+ benefits_stakeholders: template.stakeholders.filter((s) => s.disposition === "supportive").map((s) => s.id),
1260
+ harms_stakeholders: template.stakeholders.filter((s) => s.disposition === "hostile").map((s) => s.id),
1261
+ },
1262
+ {
1263
+ id: "path_defensive",
1264
+ label: "Defensive Posture",
1265
+ description: "Conservative approach prioritizing stability over opportunity",
1266
+ projected_outcome: "Reduced damage but slower recovery",
1267
+ probability: 0.7,
1268
+ risk: "moderate",
1269
+ tradeoffs: ["Safety vs. opportunity cost", "Coordination cost vs. independence"],
1270
+ benefits_stakeholders: template.stakeholders.filter((s) => s.disposition !== "hostile").map((s) => s.id),
1271
+ harms_stakeholders: [],
1272
+ },
1273
+ ];
1274
+ baseWorldDef = template.world.inline_definition;
1275
+ scenarioTitle = template.title;
1276
+ }
1277
+ console.log(` Scenario: ${scenarioTitle}`);
1278
+ // Create session
1279
+ const session = createEnforcementSession(scenarioTitle, baseRequest, basePaths);
1280
+ // Convert policy documents (.txt/.md) to world files
1281
+ const convertedWorlds = [];
1282
+ if (policyDocFiles.length > 0) {
1283
+ console.log(` Converting ${policyDocFiles.length} policy document(s) to world files...\n`);
1284
+ for (const docFile of policyDocFiles) {
1285
+ try {
1286
+ let docText = fs.readFileSync(docFile, "utf-8");
1287
+ // --patch: auto-apply a quick fix before converting
1288
+ if (patchDiagId) {
1289
+ const preParsed = parseRulesFromText(docText);
1290
+ const preHealth = validatePolicy(preParsed);
1291
+ const targetDiag = preHealth.diagnostics.find(d => d.id === patchDiagId);
1292
+ if (targetDiag?.quickFix) {
1293
+ console.log(` Applying fix ${patchDiagId}: ${targetDiag.quickFix.label}`);
1294
+ docText = applyQuickFix(docText, targetDiag.quickFix);
1295
+ console.log(` Patched. Running with fixed rules.\n`);
1296
+ }
1297
+ else if (targetDiag) {
1298
+ console.log(` ${patchDiagId} found but has no quick fix available.\n`);
1299
+ }
1300
+ else {
1301
+ console.log(` ${patchDiagId} not found in diagnostics for ${docFile}.\n`);
1302
+ }
1303
+ }
1304
+ const parsed = parseRulesFromText(docText);
1305
+ const health = validatePolicy(parsed);
1306
+ const worldDef = policyToWorld(parsed);
1307
+ const docName = docFile.replace(/^.*[\\/]/, "").replace(/\.\w+$/, "");
1308
+ const world = createSavedWorld(docName, worldDef, presetArg, { description: `Generated from ${docFile}` });
1309
+ console.log(` ${docFile} → "${docName}"`);
1310
+ console.log(` World: ${worldDef.invariants.length} invariants, ${worldDef.gates.length} gates`);
1311
+ // GitHub-style policy review — show diagnostics with quick fixes
1312
+ console.log(formatPolicyDiagnostics(health, parsed, docFile));
1313
+ // If errors would block simulation, warn but continue
1314
+ if (!health.canSimulate) {
1315
+ console.error(` ✗ ${docFile} has blocking errors — skipping.`);
1316
+ console.error(` Fix the issues above and run again.\n`);
1317
+ continue;
1318
+ }
1319
+ convertedWorlds.push(world);
1320
+ }
1321
+ catch (e) {
1322
+ console.error(` Failed to convert ${docFile}: ${e.message}`);
1323
+ }
1324
+ }
1325
+ }
1326
+ // Build ordered list of worlds to test: JSON files first, then converted docs
1327
+ const worldsToRun = [];
1328
+ for (const jsonFile of worldFiles) {
1329
+ try {
1330
+ const w = await loadWorldFromFile(jsonFile);
1331
+ worldsToRun.push({ label: jsonFile, world: w });
1332
+ }
1333
+ catch (e) {
1334
+ console.error(` Failed to load ${jsonFile}: ${e.message}`);
1335
+ }
1336
+ }
1337
+ for (const cw of convertedWorlds) {
1338
+ worldsToRun.push({ label: cw.name, world: cw });
1339
+ }
1340
+ // If user provided files (JSON or docs), run them. Otherwise, run progressive rule sets.
1341
+ if (worldsToRun.length > 0) {
1342
+ console.log(` Running ${worldsToRun.length} world(s)...\n`);
1343
+ for (let i = 0; i < worldsToRun.length; i++) {
1344
+ try {
1345
+ const world = worldsToRun[i].world;
1346
+ console.log(` Run ${i + 1}: "${world.name}" (${world.world.invariants.length} rules, ${(world.world.gates ?? []).length} gates)`);
1347
+ const run = await session.run(world);
1348
+ console.log(` → Stability: ${(run.result.governed.metrics.stabilityScore * 100).toFixed(0)}% Collapse: ${(run.result.governed.metrics.collapseProbability * 100).toFixed(0)}% Effectiveness: ${(run.result.comparison.governanceEffectiveness * 100).toFixed(0)}%`);
1349
+ }
1350
+ catch (e) {
1351
+ console.error(` Failed to run "${worldsToRun[i].label}": ${e.message}`);
1352
+ }
1353
+ }
1354
+ }
1355
+ else {
1356
+ // Progressive enforcement: no rules → light rules → full rules
1357
+ console.log(` Running progressive enforcement (3 iterations)...\n`);
1358
+ // Run 1: No rules (baseline world)
1359
+ const noRulesWorld = createSavedWorld("No Rules", { thesis: baseWorldDef.thesis, state_variables: baseWorldDef.state_variables ?? [], invariants: [], gates: [] }, presetArg, { description: "Baseline — no governance rules applied" });
1360
+ console.log(` Run 1: "No Rules" (0 invariants, 0 gates)`);
1361
+ const run1 = await session.run(noRulesWorld);
1362
+ console.log(` → Stability: ${(run1.result.governed.metrics.stabilityScore * 100).toFixed(0)}% Collapse: ${(run1.result.governed.metrics.collapseProbability * 100).toFixed(0)}% Effectiveness: ${(run1.result.comparison.governanceEffectiveness * 100).toFixed(0)}%`);
1363
+ // Run 2: Light rules (pick first 2 invariants)
1364
+ const lightInvariants = baseWorldDef.invariants.slice(0, 2);
1365
+ const lightWorld = createSavedWorld("Light Governance", { thesis: baseWorldDef.thesis, state_variables: baseWorldDef.state_variables ?? [], invariants: lightInvariants, gates: [] }, presetArg, { description: "Partial rules — testing minimal governance" });
1366
+ console.log(` Run 2: "Light Governance" (${lightInvariants.length} invariants, 0 gates)`);
1367
+ const run2 = await session.run(lightWorld);
1368
+ console.log(` → Stability: ${(run2.result.governed.metrics.stabilityScore * 100).toFixed(0)}% Collapse: ${(run2.result.governed.metrics.collapseProbability * 100).toFixed(0)}% Effectiveness: ${(run2.result.comparison.governanceEffectiveness * 100).toFixed(0)}%`);
1369
+ // Run 3: Full rules
1370
+ const fullWorld = createSavedWorld("Full Governance", baseWorldDef, presetArg, { description: "All rules active — full governance enforcement" });
1371
+ console.log(` Run 3: "Full Governance" (${baseWorldDef.invariants.length} invariants, ${(baseWorldDef.gates ?? []).length} gates)`);
1372
+ const run3 = await session.run(fullWorld);
1373
+ console.log(` → Stability: ${(run3.result.governed.metrics.stabilityScore * 100).toFixed(0)}% Collapse: ${(run3.result.governed.metrics.collapseProbability * 100).toFixed(0)}% Effectiveness: ${(run3.result.comparison.governanceEffectiveness * 100).toFixed(0)}%`);
1374
+ }
1375
+ // Generate report
1376
+ const report = session.generateReport();
1377
+ if (outputJson || outputFile) {
1378
+ const json = exportEnforcementReportJSON(report);
1379
+ if (outputFile) {
1380
+ fs.writeFileSync(outputFile, json, "utf-8");
1381
+ console.log(`\n Report saved to: ${outputFile}`);
1382
+ }
1383
+ else {
1384
+ console.log(json);
1385
+ }
1386
+ }
1387
+ else {
1388
+ console.log(formatEnforcementReport(report));
1389
+ // Behavioral analysis — what agents did differently under governance
1390
+ for (const run of session.runs) {
1391
+ console.log(`\n ── Behavioral Analysis: Run ${run.iteration} (${run.world.name}) ──`);
1392
+ console.log((0, behavioralAnalysis_1.formatBehavioralAnalysis)(run.result.behavioralAnalysis));
1393
+ }
1394
+ // Rule impact attribution — show which rules shaped the outcome
1395
+ const attribution = formatRuleImpactAttribution(report);
1396
+ if (attribution) {
1397
+ console.log(attribution);
1398
+ }
1399
+ // System archetype insight — governed AI detects system patterns
1400
+ const archetypeGov = await governedDetectArchetype(report);
1401
+ const archetypeInsight = archetypeGov.result;
1402
+ if (archetypeInsight) {
1403
+ console.log(archetypeInsight);
1404
+ // Show governance trace for archetype detection
1405
+ console.log(` ┌─ Governed AI ────────────────────────────────┐`);
1406
+ console.log(` │ Actor: ${archetypeGov.actor.padEnd(40)}│`);
1407
+ console.log(` │ Action: ${"detect_archetype".padEnd(39)}│`);
1408
+ console.log(` │ Constraints: ${String(archetypeGov.trace.constraints.length).padEnd(34)}│`);
1409
+ console.log(` │ Passed: ${String(archetypeGov.trace.passed).padEnd(39)}│`);
1410
+ console.log(` └──────────────────────────────────────────────┘`);
1411
+ }
1412
+ // Load experiment library for knowledge features
1413
+ const experimentLibrary = loadExperimentLibrary();
1414
+ const bestRun = report.runs[report.divergence.bestIteration - 1];
1415
+ const currentMetrics = bestRun
1416
+ ? { stability: bestRun.metrics.stabilityScore, effectiveness: bestRun.comparison.governanceEffectiveness }
1417
+ : { stability: 0, effectiveness: 0 };
1418
+ // Find similar experiments + best known strategy — ALL governed
1419
+ if (experimentLibrary.length > 0 && bestRun) {
1420
+ const runArchetype = archetypeInsight
1421
+ ? (archetypeInsight.match(/behaves like: (.+)/)?.[1] ?? null)
1422
+ : null;
1423
+ // Governed similarity search
1424
+ const similarGov = await governedFindSimilar(runArchetype, currentMetrics, experimentLibrary);
1425
+ if (similarGov.result) {
1426
+ console.log(similarGov.result);
1427
+ console.log(` ┌─ Governed AI ────────────────────────────────┐`);
1428
+ console.log(` │ Actor: ${similarGov.actor.padEnd(40)}│`);
1429
+ console.log(` │ Action: ${"compare_experiments".padEnd(39)}│`);
1430
+ console.log(` │ Constraints: ${String(similarGov.trace.constraints.length).padEnd(34)}│`);
1431
+ console.log(` │ Passed: ${String(similarGov.trace.passed).padEnd(39)}│`);
1432
+ console.log(` └──────────────────────────────────────────────┘`);
1433
+ }
1434
+ // Governed strategy synthesis
1435
+ const strategyGov = await governedSynthesizeStrategy(runArchetype, currentMetrics, experimentLibrary);
1436
+ if (strategyGov.result) {
1437
+ console.log(strategyGov.result);
1438
+ console.log(` ┌─ Governed AI ────────────────────────────────┐`);
1439
+ console.log(` │ Actor: ${strategyGov.actor.padEnd(40)}│`);
1440
+ console.log(` │ Action: ${"synthesize_strategy".padEnd(39)}│`);
1441
+ console.log(` │ Constraints: ${String(strategyGov.trace.constraints.length).padEnd(34)}│`);
1442
+ console.log(` │ Passed: ${String(strategyGov.trace.passed).padEnd(39)}│`);
1443
+ console.log(` └──────────────────────────────────────────────┘`);
1444
+ }
1445
+ }
1446
+ // --compare: show side-by-side before/after when used with --patch
1447
+ if (compareMode && patchDiagId && report.runs.length >= 1) {
1448
+ const run = report.runs[report.runs.length - 1];
1449
+ const baseStab = report.runs.length > 1 ? report.runs[0].metrics.stabilityScore : 0.5;
1450
+ const baseEff = report.runs.length > 1 ? report.runs[0].comparison.governanceEffectiveness : 0;
1451
+ const baseCollapse = report.runs.length > 1 ? report.runs[0].metrics.collapseProbability : 0.15;
1452
+ // Multi-patch comparison
1453
+ if (patchDiagIds.length > 1) {
1454
+ // Build comparison data from available runs
1455
+ const patchResults = patchDiagIds.map((pid, idx) => {
1456
+ const patchRun = idx < report.runs.length ? report.runs[idx] : run;
1457
+ return {
1458
+ patchId: pid,
1459
+ stability: patchRun.metrics.stabilityScore,
1460
+ effectiveness: patchRun.comparison.governanceEffectiveness,
1461
+ collapse: patchRun.metrics.collapseProbability,
1462
+ };
1463
+ });
1464
+ console.log(formatMultiPatchComparison(patchResults, {
1465
+ stability: baseStab,
1466
+ effectiveness: baseEff,
1467
+ collapse: baseCollapse,
1468
+ }));
1469
+ }
1470
+ else {
1471
+ // Single patch comparison
1472
+ console.log(" BEFORE vs AFTER (--compare)");
1473
+ console.log(" " + "-".repeat(70));
1474
+ console.log(` Patch applied: ${patchDiagId}`);
1475
+ console.log("");
1476
+ console.log(" Metric Before (baseline) After (patched)");
1477
+ console.log(" " + "─".repeat(60));
1478
+ console.log(` Stability ${(baseStab * 100).toFixed(0).padStart(3)}% ${(run.metrics.stabilityScore * 100).toFixed(0).padStart(3)}%`);
1479
+ console.log(` Effectiveness ${(baseEff * 100).toFixed(0).padStart(3)}% ${(run.comparison.governanceEffectiveness * 100).toFixed(0).padStart(3)}%`);
1480
+ console.log(` Collapse risk ${(baseCollapse * 100).toFixed(0).padStart(3)}% ${(run.metrics.collapseProbability * 100).toFixed(0).padStart(3)}%`);
1481
+ const stabDelta = run.metrics.stabilityScore - baseStab;
1482
+ const effDelta = run.comparison.governanceEffectiveness - baseEff;
1483
+ console.log("");
1484
+ console.log(` Net change: ${stabDelta >= 0 ? "+" : ""}${(stabDelta * 100).toFixed(0)} stability, ${effDelta >= 0 ? "+" : ""}${(effDelta * 100).toFixed(0)} effectiveness`);
1485
+ if (stabDelta > 0 && effDelta > 0) {
1486
+ console.log(" The fix improved both dimensions. Apply it permanently.");
1487
+ }
1488
+ else if (stabDelta > 0 && effDelta <= 0) {
1489
+ console.log(" Stability improved but effectiveness didn't. The fix may be too conservative.");
1490
+ }
1491
+ else if (stabDelta <= 0 && effDelta > 0) {
1492
+ console.log(" Effectiveness improved but stability dropped. The fix may need guardrails.");
1493
+ }
1494
+ console.log("");
1495
+ }
1496
+ }
1497
+ // --save: persist this experiment with lineage tracking
1498
+ if (saveExperiment || saveLabel) {
1499
+ const rulesFile = policyDocFiles.length > 0 ? policyDocFiles[0] : null;
1500
+ // Find parent experiment — most recent experiment for same scenario
1501
+ const parentExp = experimentLibrary.length > 0
1502
+ ? experimentLibrary.filter(e => e.scenario === report.scenario).pop() ?? null
1503
+ : null;
1504
+ const experiment = createSavedExperiment(report, rulesFile, patchDiagIds, parentExp);
1505
+ const savePath = saveLabel
1506
+ ? `experiments/${saveLabel}.json`
1507
+ : `experiments/${experiment.id}.json`;
1508
+ try {
1509
+ fs.mkdirSync("experiments", { recursive: true });
1510
+ fs.writeFileSync(savePath, JSON.stringify(experiment, null, 2));
1511
+ console.log("");
1512
+ console.log(" EXPERIMENT SAVED");
1513
+ console.log(" " + "-".repeat(70));
1514
+ console.log(` Saved to: ${savePath}`);
1515
+ console.log(` ID: ${experiment.id}`);
1516
+ console.log(` Scenario: ${experiment.scenario}`);
1517
+ console.log(` Generation: ${experiment.lineage.generation}`);
1518
+ console.log(` Stability: ${(experiment.metrics.stability * 100).toFixed(0)}% Effectiveness: ${(experiment.metrics.effectiveness * 100).toFixed(0)}%`);
1519
+ if (experiment.archetype) {
1520
+ console.log(` Archetype: ${experiment.archetype}`);
1521
+ }
1522
+ if (experiment.lineage.parentId) {
1523
+ const imp = experiment.lineage.improvementFromParent;
1524
+ console.log(` Derived from: ${experiment.lineage.parentId}`);
1525
+ console.log(` Improvement: ${imp.stability >= 0 ? "+" : ""}${(imp.stability * 100).toFixed(0)} stability, ${imp.effectiveness >= 0 ? "+" : ""}${(imp.effectiveness * 100).toFixed(0)} effectiveness`);
1526
+ if (experiment.lineage.patchesFromParent.length > 0) {
1527
+ console.log(` Patches applied: ${experiment.lineage.patchesFromParent.join(", ")}`);
1528
+ }
1529
+ }
1530
+ // Show lineage chain
1531
+ const updatedLibrary = [...experimentLibrary, experiment];
1532
+ console.log(formatExperimentLineage(experiment, updatedLibrary));
1533
+ console.log(" Compare experiments later:");
1534
+ console.log(` npx nv-sim enforce trading --compare-experiments ${savePath}`);
1535
+ console.log("");
1536
+ }
1537
+ catch {
1538
+ console.log(`\n Could not save experiment to ${savePath}`);
1539
+ }
1540
+ }
1541
+ // Show guided next steps (skip for JSON output)
1542
+ const usedCustomFiles = inputFiles.length > 0;
1543
+ console.log(await formatGuidedNextSteps(report, usedCustomFiles));
1544
+ }
1545
+ break;
1546
+ }
353
1547
  case "help":
354
1548
  case "--help":
355
1549
  case "-h":
@@ -362,13 +1556,44 @@ async function main() {
362
1556
 
363
1557
  COMMANDS:
364
1558
 
365
- compare [preset] Run governed simulation comparison (the killer demo)
366
- chaos [preset] Stress test — find the breaking point (the viral command)
367
- visualize [preset] Launch local simulation viewer in browser
368
- analyze <file> Analyze a simulation from file or stdin
369
- preset <id> Run reasoning on a preset scenario
370
- presets List available preset scenarios
371
- help Show this help message
1559
+ compare [preset] Baseline vs governed comparison (the killer demo)
1560
+ --inject event@round[,event@round,...]
1561
+ scenario <id> Run a named stress scenario
1562
+ --compare to test across multiple worlds
1563
+ scenarios List all available scenarios
1564
+ worlds <a> <b> Same agents, different rule environments
1565
+ enforce [preset] Policy enforcement lab — iterative rule testing
1566
+ Run same scenario with different rules, see divergence
1567
+ --json to output JSON report
1568
+ --output=file.json to save report
1569
+ Pass world files: nv-sim enforce trading a.json b.json
1570
+ chaos [preset] Stress test — find the breaking point
1571
+ visualize Interactive scenario control platform
1572
+ serve | govern Start local governance runtime
1573
+ --port N (default: 3456)
1574
+ run <simulator> Run external simulator with governance
1575
+ e.g., nv-sim run mirofish
1576
+ radiant [preset] Prime Radiant — full policy enforcement system
1577
+ Dual-layer: govern() + governDynamics()
1578
+ --world MODE 1: build & validate world
1579
+ --compare MODE 3: decision intelligence (N options)
1580
+ (default) MODE 2: governed simulation with comparison
1581
+ mirofish MiroFish agent simulation with governance
1582
+ --compare Governed vs ungoverned comparison
1583
+ science Science mode — governed autonomous discovery
1584
+ --compare Governed vs ungoverned comparison
1585
+ world-from-doc <file> Generate world rules from a document
1586
+ --output file.json --id my-world
1587
+ analyze <file> Analyze a simulation from file or stdin
1588
+ presets List available preset scenarios
1589
+
1590
+ WORLD MANAGEMENT:
1591
+
1592
+ world:save <preset> [output.json] Save a world as JSON file
1593
+ --name="My World Name"
1594
+ world:load <file.json> Inspect a saved world file
1595
+ world:list List all available worlds
1596
+ world:compare <a.json> <b.json> Compare two saved world files
372
1597
 
373
1598
  PRESETS:
374
1599
 
@@ -379,15 +1604,51 @@ async function main() {
379
1604
 
380
1605
  EXAMPLES:
381
1606
 
382
- npx nv-sim compare # Run the trading demo
383
- npx nv-sim chaos # Stress test (100 runs)
384
- npx nv-sim chaos trading --runs 500 # 500 randomized runs
385
- npx nv-sim visualize # Open local viewer
386
- npx nv-sim compare strait_of_hormuz # Run geopolitical demo
1607
+ npx nv-sim compare # Baseline vs governed
1608
+ npx nv-sim worlds trading strait_of_hormuz # Compare two rule environments
1609
+ npx nv-sim chaos --runs 500 # Stress test (500 runs)
1610
+ npx nv-sim visualize # Live viewer in browser
1611
+ npx nv-sim serve # Start local governance runtime
1612
+ npx nv-sim run mirofish # Run MiroFish with governance
1613
+ npx nv-sim compare strait_of_hormuz # Geopolitical demo
1614
+ npx nv-sim compare --inject tanker_explosion@3 # With narrative shock
1615
+ npx nv-sim compare --inject rate_cut@3,sanctions@5
1616
+ npx nv-sim scenario taiwan_crisis # Named scenario
1617
+ npx nv-sim scenario bank_run --compare # Cross-world comparison
1618
+ npx nv-sim scenarios # List all scenarios
387
1619
  npx nv-sim analyze simulation.json
388
- npx nv-sim preset strait_of_hormuz
1620
+ npx nv-sim world-from-doc policy.md # Generate world from document
1621
+ npx nv-sim world-from-doc rules.txt --output world.json
1622
+ npx nv-sim world:save trading my-world.json # Save world to file
1623
+ npx nv-sim world:load my-world.json # Inspect a saved world
1624
+ npx nv-sim world:compare a.json b.json # Compare two world files
389
1625
 
390
- Simulate the future. Govern the outcomes.
1626
+ POLICY ENFORCEMENT:
1627
+
1628
+ npx nv-sim enforce # Progressive: no rules → light → full
1629
+ npx nv-sim enforce strait_of_hormuz # Same, with geopolitical preset
1630
+ npx nv-sim enforce trading a.json b.json # Compare custom world files
1631
+ npx nv-sim enforce --output=report.json # Save report as JSON
1632
+
1633
+ PRIME RADIANT:
1634
+
1635
+ npx nv-sim radiant # Full governed simulation
1636
+ npx nv-sim radiant university_crisis # University crisis preset
1637
+ npx nv-sim radiant financial_crisis --compare # Decision intelligence mode
1638
+ npx nv-sim radiant geopolitical_crisis --world # World builder mode
1639
+ npx nv-sim radiant scientific_discovery # Science discovery preset
1640
+
1641
+ MIROFISH MODE:
1642
+
1643
+ npx nv-sim mirofish # Governed agent simulation
1644
+ npx nv-sim mirofish --compare # Governed vs ungoverned comparison
1645
+
1646
+ SCIENCE MODE:
1647
+
1648
+ npx nv-sim science # Governed investigation demo
1649
+ npx nv-sim science --compare # Governed vs ungoverned comparison
1650
+
1651
+ Design rules. Run reality. See what changes.
391
1652
  `);
392
1653
  break;
393
1654
  }
@@ -480,12 +1741,103 @@ function printComparisonResults(result, worldId) {
480
1741
  console.log(` World viability: ${gs.finalViability}${gs.worldCollapsed ? " (COLLAPSED)" : ""}`);
481
1742
  console.log(` Agents: ${result.baseline.swarm.rounds[0]?.reactions.length ?? 0}`);
482
1743
  console.log(` Rounds: ${result.baseline.swarm.rounds.length}`);
1744
+ // Narrative impacts
1745
+ if (result.narrativeImpacts && result.narrativeImpacts.length > 0) {
1746
+ console.log(`\n NARRATIVE IMPACTS`);
1747
+ console.log(" " + "-".repeat(50));
1748
+ for (const ni of result.narrativeImpacts) {
1749
+ console.log(` EVENT: "${ni.event.headline}" (${ni.event.severity})`);
1750
+ for (const shift of ni.agentShifts) {
1751
+ const arrow = shift.impactShift > 0 ? "\u2191" : shift.impactShift < 0 ? "\u2193" : "=";
1752
+ console.log(` ${arrow} ${shift.stakeholder_id}: impact ${shift.impactShift > 0 ? "+" : ""}${shift.impactShift.toFixed(3)}, confidence ${shift.confidenceShift > 0 ? "+" : ""}${shift.confidenceShift.toFixed(3)}`);
1753
+ }
1754
+ for (const effect of ni.systemEffects) {
1755
+ console.log(` > ${effect}`);
1756
+ }
1757
+ }
1758
+ }
1759
+ // Behavioral analysis — what agents did differently
1760
+ console.log((0, behavioralAnalysis_1.formatBehavioralAnalysis)(result.behavioralAnalysis));
483
1761
  // Key message
484
1762
  console.log(`\n ${"=".repeat(60)}`);
485
1763
  console.log(` NeuroVerse doesn't just stop bad actions.`);
486
1764
  console.log(` It changes the structure of the system itself.`);
487
1765
  console.log(` ${"=".repeat(60)}`);
488
- console.log(`\n Simulate the future. Govern the outcomes.`);
1766
+ console.log(`\n Design rules. Run reality. See what changes.`);
1767
+ console.log(` neuroverse-simulations | @neuroverseos\n`);
1768
+ }
1769
+ // ============================================
1770
+ // WORLD COMPARISON OUTPUT
1771
+ // ============================================
1772
+ function printWorldComparisonResults(r, impact) {
1773
+ const a = r.worldA;
1774
+ const b = r.worldB;
1775
+ const d = r.delta;
1776
+ console.log(`\n WORLD A — ${a.id}`);
1777
+ console.log(" " + "=".repeat(50));
1778
+ console.log(` Thesis: ${a.thesis}`);
1779
+ console.log(` Invariants: ${a.invariantCount} | Gates: ${a.gateCount}`);
1780
+ console.log(` Trajectory: ${a.trajectory.toUpperCase()}`);
1781
+ console.log(` Stability: ${(a.metrics.stabilityScore * 100).toFixed(0)}%`);
1782
+ console.log(` Collapse probability: ${(a.metrics.collapseProbability * 100).toFixed(0)}%`);
1783
+ console.log(` Peak volatility: ${(a.metrics.maxVolatility * 100).toFixed(0)}%`);
1784
+ console.log(` Coalition risks: ${a.metrics.coalitionRisks}`);
1785
+ console.log(`\n WORLD B — ${b.id}`);
1786
+ console.log(" " + "=".repeat(50));
1787
+ console.log(` Thesis: ${b.thesis}`);
1788
+ console.log(` Invariants: ${b.invariantCount} | Gates: ${b.gateCount}`);
1789
+ console.log(` Trajectory: ${b.trajectory.toUpperCase()}`);
1790
+ console.log(` Stability: ${(b.metrics.stabilityScore * 100).toFixed(0)}%`);
1791
+ console.log(` Collapse probability: ${(b.metrics.collapseProbability * 100).toFixed(0)}%`);
1792
+ console.log(` Peak volatility: ${(b.metrics.maxVolatility * 100).toFixed(0)}%`);
1793
+ console.log(` Coalition risks: ${b.metrics.coalitionRisks}`);
1794
+ console.log(`\n DELTA`);
1795
+ console.log(" " + "=".repeat(50));
1796
+ const stabArrow = d.stabilityDiff > 0 ? "A more stable" : d.stabilityDiff < 0 ? "B more stable" : "equal";
1797
+ console.log(` Stability: ${d.stabilityDiff > 0 ? "+" : ""}${d.stabilityDiff.toFixed(1)} pp (${stabArrow})`);
1798
+ console.log(` Volatility: ${d.volatilityDiff > 0 ? "+" : ""}${d.volatilityDiff.toFixed(1)} pp`);
1799
+ console.log(` Coalition risk: ${d.coalitionRiskDiff > 0 ? "+" : ""}${d.coalitionRiskDiff}`);
1800
+ console.log(` Collapse: ${d.collapseDiff > 0 ? "+" : ""}${d.collapseDiff.toFixed(1)} pp`);
1801
+ console.log(`\n GOVERNANCE ENGINE`);
1802
+ console.log(" " + "-".repeat(50));
1803
+ console.log(` World A: ${a.governanceStats.engineLoaded ? "@neuroverseos/governance" : "heuristic"} — ${a.governanceStats.totalEvaluations} evals (${a.governanceStats.verdicts.allow} allow, ${a.governanceStats.verdicts.block} block, ${a.governanceStats.verdicts.pause} pause)`);
1804
+ console.log(` World B: ${b.governanceStats.engineLoaded ? "@neuroverseos/governance" : "heuristic"} — ${b.governanceStats.totalEvaluations} evals (${b.governanceStats.verdicts.allow} allow, ${b.governanceStats.verdicts.block} block, ${b.governanceStats.verdicts.pause} pause)`);
1805
+ // Impact analysis (when available)
1806
+ if (impact) {
1807
+ console.log(`\n IMPACT ANALYSIS`);
1808
+ console.log(" " + "=".repeat(50));
1809
+ console.log(` Stability delta: ${impact.stabilityDelta}`);
1810
+ console.log(` Cascade risk delta: ${impact.cascadeRiskDelta}`);
1811
+ console.log(` Volatility delta: ${impact.volatilityDelta}`);
1812
+ console.log(` Interventions A: ${impact.totalInterventionsA} (${impact.blockedActionsA} blocked)`);
1813
+ console.log(` Interventions B: ${impact.totalInterventionsB} (${impact.blockedActionsB} blocked)`);
1814
+ console.log(`\n KEY FINDINGS:`);
1815
+ for (const finding of impact.keyFindings) {
1816
+ console.log(` • ${finding}`);
1817
+ }
1818
+ console.log(`\n Powered by ${impact.poweredBy}`);
1819
+ }
1820
+ // Narrative
1821
+ console.log(`\n ANALYSIS`);
1822
+ console.log(" " + "=".repeat(50));
1823
+ const words = d.narrative.split(" ");
1824
+ let line = " ";
1825
+ for (const word of words) {
1826
+ if (line.length + word.length > 72) {
1827
+ console.log(line);
1828
+ line = " " + word;
1829
+ }
1830
+ else {
1831
+ line += (line.trim() ? " " : "") + word;
1832
+ }
1833
+ }
1834
+ if (line.trim())
1835
+ console.log(line);
1836
+ console.log(`\n ${"=".repeat(60)}`);
1837
+ console.log(` Same agents. Different rules.`);
1838
+ console.log(` The world shapes the outcome.`);
1839
+ console.log(` ${"=".repeat(60)}`);
1840
+ console.log(`\n Design rules. Run reality. See what changes.`);
489
1841
  console.log(` neuroverse-simulations | @neuroverseos\n`);
490
1842
  }
491
1843
  // ============================================
@@ -551,252 +1903,9 @@ function printChaosResults(r) {
551
1903
  }
552
1904
  console.log(` Tested with nv-sim chaos (${r.runsCompleted} runs, ${(r.durationMs / 1000).toFixed(1)}s)`);
553
1905
  console.log(` ${"=".repeat(60)}`);
554
- console.log(`\n Simulate the future. Govern the outcomes.`);
1906
+ console.log(`\n Design rules. Run reality. See what changes.`);
555
1907
  console.log(` neuroverse-simulations | @neuroverseos\n`);
556
1908
  }
557
- // ============================================
558
- // LOCAL VISUALIZER
559
- // ============================================
560
- async function startVisualizer(data, scenarioId) {
561
- const html = buildVisualizerHtml(data, scenarioId);
562
- const server = http.createServer((_req, res) => {
563
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
564
- res.end(html);
565
- });
566
- const port = 3456;
567
- server.listen(port, () => {
568
- const url = `http://localhost:${port}`;
569
- console.log(` Visualizer running at ${url}\n`);
570
- console.log(" Press Ctrl+C to stop.\n");
571
- // Try to open browser
572
- const { exec } = require("child_process");
573
- const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
574
- exec(`${openCmd} ${url}`, () => { });
575
- });
576
- }
577
- function buildVisualizerHtml(data, scenarioId) {
578
- const rounds = data.baseline.swarm.rounds;
579
- const governedRounds = data.governed.swarm.rounds;
580
- // Build per-round data for charts
581
- const roundData = rounds.map((r, i) => {
582
- const gr = governedRounds[i];
583
- const blAvg = r.reactions.reduce((s, rx) => s + rx.impact, 0) / r.reactions.length;
584
- const gvAvg = gr ? gr.reactions.reduce((s, rx) => s + rx.impact, 0) / gr.reactions.length : 0;
585
- const blVol = Math.max(...r.reactions.map(rx => Math.abs(rx.impact)));
586
- const gvVol = gr ? Math.max(...gr.reactions.map(rx => Math.abs(rx.impact))) : 0;
587
- return { round: i, blAvg, gvAvg, blVol, gvVol };
588
- });
589
- // Agent data
590
- const agents = rounds[0]?.reactions.map(r => r.stakeholder_id) ?? [];
591
- const agentTimelines = agents.map(agentId => ({
592
- id: agentId,
593
- baseline: rounds.map(r => {
594
- const rx = r.reactions.find(a => a.stakeholder_id === agentId);
595
- return rx ? rx.impact : 0;
596
- }),
597
- governed: governedRounds.map(r => {
598
- const rx = r.reactions.find(a => a.stakeholder_id === agentId);
599
- return rx ? rx.impact : 0;
600
- }),
601
- }));
602
- // Governance interventions per round
603
- const dynamics = governedRounds.map(r => r.emergent_dynamics ?? []);
604
- const jsonData = JSON.stringify({ roundData, agentTimelines, dynamics, scenarioId, data });
605
- return `<!DOCTYPE html>
606
- <html lang="en">
607
- <head>
608
- <meta charset="UTF-8">
609
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
610
- <title>NV-SIM Visualizer — ${scenarioId}</title>
611
- <style>
612
- * { margin: 0; padding: 0; box-sizing: border-box; }
613
- body { font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace; background: #0a0a0a; color: #e0e0e0; padding: 24px; }
614
- h1 { font-size: 18px; color: #fff; margin-bottom: 4px; }
615
- .subtitle { color: #888; font-size: 13px; margin-bottom: 24px; }
616
- .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px; }
617
- .card { background: #141414; border: 1px solid #222; border-radius: 8px; padding: 16px; }
618
- .card h2 { font-size: 13px; color: #888; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 12px; }
619
- .metric { display: flex; justify-content: space-between; padding: 4px 0; font-size: 13px; }
620
- .metric .label { color: #999; }
621
- .metric .value { color: #fff; font-weight: 600; }
622
- .metric .value.good { color: #4ade80; }
623
- .metric .value.bad { color: #f87171; }
624
- .metric .value.neutral { color: #fbbf24; }
625
- canvas { width: 100% !important; height: 200px !important; }
626
- .full-width { grid-column: 1 / -1; }
627
- .agent-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-bottom: 1px solid #1a1a1a; font-size: 12px; }
628
- .agent-name { width: 160px; color: #999; flex-shrink: 0; }
629
- .bar-container { flex: 1; display: flex; gap: 2px; align-items: center; }
630
- .bar { height: 20px; border-radius: 3px; min-width: 1px; transition: width 0.3s; }
631
- .bar.baseline { background: #ef4444; opacity: 0.6; }
632
- .bar.governed { background: #4ade80; opacity: 0.8; }
633
- .dynamics-list { font-size: 12px; color: #999; }
634
- .dynamics-list .round-label { color: #fbbf24; font-weight: 600; }
635
- .dynamics-list p { padding: 3px 0; }
636
- .tag { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; margin: 2px; }
637
- .tag.allow { background: #052e16; color: #4ade80; }
638
- .tag.block { background: #2d0606; color: #f87171; }
639
- .tag.pause { background: #2d2006; color: #fbbf24; }
640
- .footer { text-align: center; color: #444; font-size: 12px; padding: 24px 0; }
641
- .sparkline { display: flex; align-items: flex-end; gap: 1px; height: 40px; }
642
- .spark-bar { flex: 1; border-radius: 2px 2px 0 0; min-width: 3px; }
643
- </style>
644
- </head>
645
- <body>
646
- <h1>NV-SIM Visualizer</h1>
647
- <div class="subtitle">${scenarioId} — Baseline vs Governed Comparison</div>
648
-
649
- <div class="grid">
650
- <div class="card">
651
- <h2>Simulation A — Baseline (No Governance)</h2>
652
- <div id="baseline-metrics"></div>
653
- </div>
654
- <div class="card">
655
- <h2>Simulation B — Governed (World Rules)</h2>
656
- <div id="governed-metrics"></div>
657
- </div>
658
-
659
- <div class="card">
660
- <h2>Impact Timeline — Average Impact Per Round</h2>
661
- <canvas id="impactChart"></canvas>
662
- </div>
663
- <div class="card">
664
- <h2>Volatility Timeline — Peak Volatility Per Round</h2>
665
- <canvas id="volatilityChart"></canvas>
666
- </div>
667
-
668
- <div class="card full-width">
669
- <h2>Agent Behavior — Baseline vs Governed (Final Round)</h2>
670
- <div id="agent-bars"></div>
671
- </div>
672
-
673
- <div class="card">
674
- <h2>Governance Interventions</h2>
675
- <div id="dynamics" class="dynamics-list"></div>
676
- </div>
677
- <div class="card">
678
- <h2>Governance Engine Stats</h2>
679
- <div id="engine-stats"></div>
680
- </div>
681
- </div>
682
-
683
- <div class="footer">
684
- nv-sim visualize — local simulation inspector<br>
685
- neuroverse-simulations | @neuroverseos
686
- </div>
687
-
688
- <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js"><\/script>
689
- <script>
690
- const SIM = ${jsonData};
691
-
692
- // Metrics
693
- function renderMetrics(containerId, metrics, trajectory) {
694
- const el = document.getElementById(containerId);
695
- const rows = [
696
- ['Trajectory', trajectory.toUpperCase(), trajectory === 'converging' || trajectory === 'stabilizing' ? 'good' : trajectory === 'escalating' ? 'bad' : 'neutral'],
697
- ['Collapse Probability', (metrics.collapseProbability * 100).toFixed(0) + '%', metrics.collapseProbability < 0.2 ? 'good' : metrics.collapseProbability > 0.5 ? 'bad' : 'neutral'],
698
- ['Stability', (metrics.stabilityScore * 100).toFixed(0) + '%', metrics.stabilityScore > 0.7 ? 'good' : metrics.stabilityScore < 0.4 ? 'bad' : 'neutral'],
699
- ['Peak Volatility', (metrics.maxVolatility * 100).toFixed(0) + '%', metrics.maxVolatility < 0.4 ? 'good' : metrics.maxVolatility > 0.7 ? 'bad' : 'neutral'],
700
- ['Coalition Risks', metrics.coalitionRisks, metrics.coalitionRisks === 0 ? 'good' : 'bad'],
701
- ['Polarization Events', metrics.polarizationEvents, metrics.polarizationEvents === 0 ? 'good' : 'neutral'],
702
- ];
703
- el.innerHTML = rows.map(([label, value, cls]) =>
704
- '<div class="metric"><span class="label">' + label + '</span><span class="value ' + cls + '">' + value + '</span></div>'
705
- ).join('');
706
- }
707
-
708
- renderMetrics('baseline-metrics', SIM.data.baseline.metrics, SIM.data.baseline.swarm.trajectory);
709
- renderMetrics('governed-metrics', SIM.data.governed.metrics, SIM.data.governed.swarm.trajectory);
710
-
711
- // Charts
712
- const labels = SIM.roundData.map(d => 'R' + d.round);
713
-
714
- if (typeof Chart !== 'undefined') {
715
- new Chart(document.getElementById('impactChart'), {
716
- type: 'line',
717
- data: {
718
- labels,
719
- datasets: [
720
- { label: 'Baseline', data: SIM.roundData.map(d => d.blAvg), borderColor: '#ef4444', backgroundColor: 'rgba(239,68,68,0.1)', fill: true, tension: 0.3 },
721
- { label: 'Governed', data: SIM.roundData.map(d => d.gvAvg), borderColor: '#4ade80', backgroundColor: 'rgba(74,222,128,0.1)', fill: true, tension: 0.3 },
722
- ]
723
- },
724
- options: {
725
- responsive: true,
726
- plugins: { legend: { labels: { color: '#888', font: { family: 'monospace' } } } },
727
- scales: {
728
- x: { ticks: { color: '#666' }, grid: { color: '#1a1a1a' } },
729
- y: { ticks: { color: '#666' }, grid: { color: '#1a1a1a' }, min: -1, max: 1 }
730
- }
731
- }
732
- });
733
-
734
- new Chart(document.getElementById('volatilityChart'), {
735
- type: 'bar',
736
- data: {
737
- labels,
738
- datasets: [
739
- { label: 'Baseline', data: SIM.roundData.map(d => d.blVol), backgroundColor: 'rgba(239,68,68,0.6)' },
740
- { label: 'Governed', data: SIM.roundData.map(d => d.gvVol), backgroundColor: 'rgba(74,222,128,0.6)' },
741
- ]
742
- },
743
- options: {
744
- responsive: true,
745
- plugins: { legend: { labels: { color: '#888', font: { family: 'monospace' } } } },
746
- scales: {
747
- x: { ticks: { color: '#666' }, grid: { color: '#1a1a1a' } },
748
- y: { ticks: { color: '#666' }, grid: { color: '#1a1a1a' }, min: 0, max: 1 }
749
- }
750
- }
751
- });
752
- }
753
-
754
- // Agent bars (final round comparison)
755
- const agentBars = document.getElementById('agent-bars');
756
- const lastRoundIdx = SIM.agentTimelines[0]?.baseline.length - 1 || 0;
757
- agentBars.innerHTML = SIM.agentTimelines.map(agent => {
758
- const blImpact = agent.baseline[lastRoundIdx] || 0;
759
- const gvImpact = agent.governed[lastRoundIdx] || 0;
760
- const blWidth = Math.abs(blImpact) * 100;
761
- const gvWidth = Math.abs(gvImpact) * 100;
762
- const blColor = blImpact >= 0 ? '#4ade80' : '#ef4444';
763
- const gvColor = gvImpact >= 0 ? '#4ade80' : '#3b82f6';
764
- return '<div class="agent-row">' +
765
- '<span class="agent-name">' + agent.id + '</span>' +
766
- '<div class="bar-container">' +
767
- '<div class="bar" style="width:' + blWidth + '%;background:' + blColor + ';opacity:0.4" title="Baseline: ' + blImpact.toFixed(2) + '"></div>' +
768
- '</div>' +
769
- '<div class="bar-container">' +
770
- '<div class="bar" style="width:' + gvWidth + '%;background:' + gvColor + '" title="Governed: ' + gvImpact.toFixed(2) + '"></div>' +
771
- '</div>' +
772
- '</div>';
773
- }).join('');
774
-
775
- // Dynamics
776
- const dynamicsEl = document.getElementById('dynamics');
777
- dynamicsEl.innerHTML = SIM.dynamics.map((roundDyn, i) =>
778
- roundDyn.length > 0
779
- ? '<p><span class="round-label">Round ' + i + ':</span> ' + roundDyn.join('; ') + '</p>'
780
- : ''
781
- ).filter(Boolean).join('') || '<p>No interventions triggered.</p>';
782
-
783
- // Engine stats
784
- const gs = SIM.data.governanceStats;
785
- const engineEl = document.getElementById('engine-stats');
786
- engineEl.innerHTML = [
787
- ['Engine', gs.engineLoaded ? '@neuroverseos/governance' : 'heuristic fallback', gs.engineLoaded ? 'good' : 'neutral'],
788
- ['Guard Evaluations', gs.totalEvaluations, ''],
789
- ['Verdicts', '<span class="tag allow">' + gs.verdicts.allow + ' allow</span><span class="tag block">' + gs.verdicts.block + ' block</span><span class="tag pause">' + gs.verdicts.pause + ' pause</span>', ''],
790
- ['Rules Fired', gs.rulesFired, ''],
791
- ['World Viability', gs.finalViability + (gs.worldCollapsed ? ' (COLLAPSED)' : ''), gs.worldCollapsed ? 'bad' : 'good'],
792
- ['Invariants Checked', gs.invariantsChecked, ''],
793
- ].map(([label, value, cls]) =>
794
- '<div class="metric"><span class="label">' + label + '</span><span class="value ' + cls + '">' + value + '</span></div>'
795
- ).join('');
796
- <\/script>
797
- </body>
798
- </html>`;
799
- }
800
1909
  main().catch((err) => {
801
1910
  console.error("Error:", err.message);
802
1911
  process.exit(1);