@neuroverseos/nv-sim 0.1.2 → 0.1.5

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 (53) hide show
  1. package/README.md +562 -68
  2. package/dist/adapters/mirofish.js +461 -0
  3. package/dist/adapters/scienceclaw.js +750 -0
  4. package/dist/assets/index-B64NuIXu.css +1 -0
  5. package/dist/assets/index-DbzSnYxr.js +532 -0
  6. package/dist/assets/mirotir-logo-DUexumBH.svg +185 -0
  7. package/dist/assets/reportEngine-DKWTrP6-.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 +4284 -0
  27. package/dist/engine/metrics/science.metrics.js +335 -0
  28. package/dist/engine/narrativeInjection.js +360 -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/scenarioCapsule.js +56 -0
  35. package/dist/engine/scenarioComparison.js +463 -0
  36. package/dist/engine/scenarioLibrary.js +248 -0
  37. package/dist/engine/swarmSimulation.js +54 -1
  38. package/dist/engine/worldComparison.js +358 -0
  39. package/dist/engine/worldStorage.js +232 -0
  40. package/dist/favicon.ico +0 -0
  41. package/dist/index.html +23 -0
  42. package/dist/lib/reasoningEngine.js +290 -0
  43. package/dist/lib/simulationAdapter.js +686 -0
  44. package/dist/lib/swarmParser.js +291 -0
  45. package/dist/lib/types.js +2 -0
  46. package/dist/lib/utils.js +8 -0
  47. package/dist/placeholder.svg +1 -0
  48. package/dist/robots.txt +14 -0
  49. package/dist/runtime/govern.js +473 -0
  50. package/dist/runtime/index.js +75 -0
  51. package/dist/runtime/types.js +11 -0
  52. package/package.json +17 -12
  53. package/variants/.gitkeep +0 -0
@@ -0,0 +1,750 @@
1
+ "use strict";
2
+ /**
3
+ * ScienceClaw Adapter — Governance Layer for Autonomous Scientific Discovery
4
+ *
5
+ * "ScienceClaw generates discoveries. NeuroVerse determines whether those discoveries are true."
6
+ *
7
+ * This adapter wraps ScienceClaw's artifact-cycle loop with dual-layer governance:
8
+ * Layer A: govern() — per-artifact, per-agent action filtering
9
+ * Layer B: governDynamics() — per-cycle, system-level trajectory control
10
+ *
11
+ * KEY INSIGHT: In ScienceClaw, "rounds" are artifact cycles, not time steps.
12
+ * Each cycle = N agents publish artifacts + ArtifactReactor matches = one governDynamics() round.
13
+ *
14
+ * The adapter does NOT import ScienceClaw code. It defines the interface contract
15
+ * that a ScienceClaw integration would implement. This is the governance shell —
16
+ * ScienceClaw plugs in as the engine underneath.
17
+ *
18
+ * Hook points in ScienceClaw:
19
+ * 1. Skill execution → govern(action) before agent runs a skill
20
+ * 2. Artifact creation → govern(action) before artifact is published
21
+ * 3. ArtifactReactor → governDynamics(state) after matching cycle completes
22
+ * 4. Heartbeat daemon → system state monitoring between cycles
23
+ *
24
+ * Usage:
25
+ * const adapter = createScienceClawAdapter({
26
+ * policyText: SCIENCE_POLICY_TEXT,
27
+ * onArtifact: (artifact, verdict) => { ... },
28
+ * onCycleComplete: (result) => { ... },
29
+ * })
30
+ *
31
+ * // In ScienceClaw's loop:
32
+ * const verdict = adapter.governArtifact(artifact)
33
+ * if (verdict.status !== "BLOCK") publish(artifact)
34
+ *
35
+ * // After each artifact cycle:
36
+ * const cycleResult = adapter.governCycle(artifacts, reactorMatches)
37
+ */
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.createScienceClawAdapter = createScienceClawAdapter;
40
+ exports.generateDemoInvestigation = generateDemoInvestigation;
41
+ const govern_1 = require("../runtime/govern");
42
+ const dynamicsGovernance_1 = require("../engine/dynamicsGovernance");
43
+ const science_metrics_1 = require("../engine/metrics/science.metrics");
44
+ // ============================================
45
+ // ARTIFACT → ACTION CONVERSION
46
+ // ============================================
47
+ /**
48
+ * Convert a ScienceArtifact into an AgentAction for govern().
49
+ *
50
+ * This is the bridge between ScienceClaw's artifact model and
51
+ * NeuroVerse's governance model.
52
+ */
53
+ function artifactToAction(artifact) {
54
+ // Map artifact type to action type
55
+ let actionType;
56
+ switch (artifact.type) {
57
+ case "hypothesis":
58
+ actionType = "publish_hypothesis";
59
+ break;
60
+ case "experiment":
61
+ actionType = "run_experiment";
62
+ break;
63
+ case "dataset":
64
+ actionType = "publish_dataset";
65
+ break;
66
+ case "model":
67
+ actionType = "publish_model";
68
+ break;
69
+ case "paper":
70
+ actionType = "publish_paper";
71
+ break;
72
+ case "review":
73
+ actionType = "publish_review";
74
+ break;
75
+ case "replication":
76
+ actionType = "publish_replication";
77
+ break;
78
+ default:
79
+ actionType = "publish";
80
+ break;
81
+ }
82
+ // Magnitude based on confidence and citation count
83
+ const citationWeight = Math.min(1, artifact.citations / 10);
84
+ const magnitude = artifact.confidence * 0.6 + citationWeight * 0.4;
85
+ return {
86
+ agentId: artifact.agentId,
87
+ type: actionType,
88
+ description: artifact.description,
89
+ magnitude: Number(magnitude.toFixed(3)),
90
+ context: {
91
+ artifactId: artifact.id,
92
+ artifactType: artifact.type,
93
+ provenance: artifact.provenance,
94
+ reproduced: artifact.reproduced,
95
+ citations: artifact.citations,
96
+ skill: artifact.skill,
97
+ },
98
+ };
99
+ }
100
+ /**
101
+ * Convert artifacts + matches into SwarmAgentReactions for governDynamics().
102
+ *
103
+ * Each artifact becomes a "reaction" in the dynamics model.
104
+ * The ArtifactReactor matches modify impact scores.
105
+ */
106
+ function artifactsToReactions(artifacts, matches, governed = true) {
107
+ // Build a match bonus map: artifact ID → cumulative match strength
108
+ const matchBonus = new Map();
109
+ for (const match of matches) {
110
+ for (const artId of match.artifactIds) {
111
+ const current = matchBonus.get(artId) ?? 0;
112
+ const bonus = match.matchType === "confirmation" ? match.strength * 0.3
113
+ : match.matchType === "contradiction" ? -match.strength * 0.2
114
+ : match.matchType === "synthesis" ? match.strength * 0.4
115
+ : match.strength * 0.15;
116
+ matchBonus.set(artId, current + bonus);
117
+ }
118
+ }
119
+ return artifacts.map((artifact) => {
120
+ // Base impact: confidence-weighted, with provenance depth bonus
121
+ const provenanceBonus = Math.min(0.2, artifact.provenance.length * 0.05);
122
+ const reproductionBonus = artifact.reproduced ? 0.15 : 0;
123
+ const matchMod = matchBonus.get(artifact.id) ?? 0;
124
+ // Positive impact = constructive contribution to the field
125
+ // Negative impact = noise, false positives, or contradictions
126
+ let impact = (artifact.confidence - 0.3) + provenanceBonus + reproductionBonus + matchMod;
127
+ // Unreproduced high-citation artifacts get penalized (citation echo chamber signal)
128
+ if (!artifact.reproduced && artifact.citations > 5) {
129
+ impact -= 0.2;
130
+ }
131
+ // Self-referencing penalty
132
+ const selfCites = artifact.provenance.filter(p => artifacts.some(a => a.id === p && a.agentId === artifact.agentId)).length;
133
+ if (selfCites > 1) {
134
+ impact -= selfCites * 0.1;
135
+ }
136
+ // Without governance, low-quality artifacts cause MORE damage:
137
+ // No filter → bad findings propagate and amplify, creating volatility
138
+ if (!governed) {
139
+ if (!artifact.reproduced) {
140
+ // Unreproduced work creates noise — impacts swing harder
141
+ impact *= 1.4;
142
+ // Low-confidence unreproduced work is actively destabilizing
143
+ if (artifact.confidence < 0.5) {
144
+ impact -= 0.25;
145
+ }
146
+ }
147
+ // Orphan artifacts (no provenance) inject unchecked noise
148
+ if (artifact.provenance.length === 0 && artifact.type !== "hypothesis") {
149
+ impact -= 0.2;
150
+ }
151
+ // High citations without reproduction = echo chamber amplification
152
+ if (!artifact.reproduced && artifact.citations > 3) {
153
+ impact -= 0.15;
154
+ }
155
+ }
156
+ impact = Math.max(-1, Math.min(1, impact));
157
+ return {
158
+ stakeholder_id: artifact.agentId,
159
+ reaction: `[${artifact.type}] ${artifact.description}`,
160
+ confidence: governed ? artifact.confidence : Math.max(0.1, artifact.confidence * 0.85),
161
+ impact: Number(impact.toFixed(3)),
162
+ trigger: artifact.provenance.length > 0
163
+ ? `builds on ${artifact.provenance.length} prior artifact(s)`
164
+ : "novel investigation",
165
+ };
166
+ });
167
+ }
168
+ // ============================================
169
+ // CONVERGENCE DETECTION
170
+ // ============================================
171
+ function detectConvergence(state, threshold, window) {
172
+ const history = state.history;
173
+ if (history.length < window + 1) {
174
+ return { detected: false, assessment: "Insufficient data for convergence detection" };
175
+ }
176
+ const recent = history.slice(-window);
177
+ let stable = true;
178
+ for (let i = 1; i < recent.length; i++) {
179
+ const trustDelta = Math.abs(recent[i].trust - recent[i - 1].trust);
180
+ const polDelta = Math.abs(recent[i].polarization - recent[i - 1].polarization);
181
+ const outrageDelta = Math.abs(recent[i].outrage - recent[i - 1].outrage);
182
+ if (trustDelta > threshold || polDelta > threshold || outrageDelta > threshold) {
183
+ stable = false;
184
+ break;
185
+ }
186
+ }
187
+ if (stable && state.trust > 0.5 && state.cascadeRisk < 0.3) {
188
+ return {
189
+ detected: true,
190
+ assessment: `Convergence detected: system stable for ${window} cycles with trust ${(state.trust * 100).toFixed(0)}% and cascade risk ${(state.cascadeRisk * 100).toFixed(0)}%`,
191
+ };
192
+ }
193
+ if (stable && state.trust <= 0.5) {
194
+ return {
195
+ detected: false,
196
+ assessment: `Metrics stable but trust too low (${(state.trust * 100).toFixed(0)}%) — may be converging on noise`,
197
+ };
198
+ }
199
+ if (stable && state.cascadeRisk >= 0.3) {
200
+ return {
201
+ detected: false,
202
+ assessment: `Metrics stable but cascade risk elevated (${(state.cascadeRisk * 100).toFixed(0)}%) — false positive risk persists`,
203
+ };
204
+ }
205
+ // Not stable — describe what's happening
206
+ const trends = [];
207
+ const firstSnap = recent[0];
208
+ const lastSnap = recent[recent.length - 1];
209
+ if (lastSnap.trust > firstSnap.trust + threshold)
210
+ trends.push("trust improving");
211
+ if (lastSnap.trust < firstSnap.trust - threshold)
212
+ trends.push("trust declining");
213
+ if (lastSnap.polarization > firstSnap.polarization + threshold)
214
+ trends.push("fragmentation increasing");
215
+ if (lastSnap.outrage > firstSnap.outrage + threshold)
216
+ trends.push("publication pressure rising");
217
+ return {
218
+ detected: false,
219
+ assessment: trends.length > 0
220
+ ? `Active dynamics: ${trends.join(", ")}`
221
+ : "System in flux — no clear convergence trend",
222
+ };
223
+ }
224
+ // ============================================
225
+ // RISK FLAG DETECTION
226
+ // ============================================
227
+ function detectRiskFlags(state, artifacts, matches) {
228
+ const flags = [];
229
+ if (state.cascadeRisk > 0.5) {
230
+ flags.push("FALSE_POSITIVE_CASCADE: unvalidated findings propagating through citation chains");
231
+ }
232
+ if (state.polarization > 0.5) {
233
+ flags.push("HYPOTHESIS_FRAGMENTATION: competing theories splitting without resolution");
234
+ }
235
+ if (state.trust < 0.3) {
236
+ flags.push("REPRODUCIBILITY_CRISIS: institutional credibility collapsing");
237
+ }
238
+ if (state.outrage > 0.5) {
239
+ flags.push("PUBLICATION_PRESSURE: agents rushing to publish without adequate validation");
240
+ }
241
+ if (state.amplification > 1.5) {
242
+ flags.push("CITATION_ECHO_CHAMBER: popular findings amplified beyond evidence support");
243
+ }
244
+ // Artifact-specific flags
245
+ const unreproducedHighCitations = artifacts.filter(a => !a.reproduced && a.citations > 5);
246
+ if (unreproducedHighCitations.length > 0) {
247
+ flags.push(`UNVALIDATED_POPULAR: ${unreproducedHighCitations.length} highly-cited artifact(s) lack reproduction`);
248
+ }
249
+ const contradictions = matches.filter(m => m.matchType === "contradiction");
250
+ if (contradictions.length > artifacts.length * 0.3) {
251
+ flags.push("HIGH_CONTRADICTION_RATE: >30% of reactor matches are contradictions");
252
+ }
253
+ return flags;
254
+ }
255
+ // ============================================
256
+ // FACTORY: createScienceClawAdapter()
257
+ // ============================================
258
+ function createScienceClawAdapter(config = {}) {
259
+ const policyText = config.policyText ?? science_metrics_1.SCIENCE_POLICY_TEXT;
260
+ const maxCycles = config.maxCycles ?? 50;
261
+ const convergenceThreshold = config.convergenceThreshold ?? 0.05;
262
+ const convergenceWindow = config.convergenceWindow ?? 3;
263
+ const governanceEnabled = policyText.trim().length > 0;
264
+ const actionGov = (0, govern_1.createGovernor)({
265
+ policyText,
266
+ sensitivity: 0.5,
267
+ });
268
+ const dynamicsGov = (0, dynamicsGovernance_1.createDynamicsGovernor)(governanceEnabled ? policyText : "", { ...science_metrics_1.SCIENCE_INITIAL_STATE, ...config.initialState }, governanceEnabled ? undefined : { skipRules: true });
269
+ let cycleCount = 0;
270
+ const actionStats = { total: 0, approved: 0, blocked: 0, modified: 0 };
271
+ // --- Govern single artifact ---
272
+ function governArtifact(artifact) {
273
+ const action = artifactToAction(artifact);
274
+ const currentState = dynamicsGov.state;
275
+ const worldState = {
276
+ volatility: currentState.outrage * 100,
277
+ liquidity: currentState.trust * 100,
278
+ polarization: currentState.polarization * 100,
279
+ cascade_risk: currentState.cascadeRisk * 100,
280
+ };
281
+ const verdict = actionGov.evaluate(action, worldState);
282
+ actionStats.total++;
283
+ switch (verdict.status) {
284
+ case "ALLOW":
285
+ actionStats.approved++;
286
+ break;
287
+ case "BLOCK":
288
+ actionStats.blocked++;
289
+ break;
290
+ case "MODIFY":
291
+ actionStats.modified++;
292
+ break;
293
+ case "PAUSE":
294
+ actionStats.modified++;
295
+ break; // count pause as modified
296
+ }
297
+ // Build science-specific reason
298
+ const riskFlags = [];
299
+ let scienceReason = verdict.reason;
300
+ if (!artifact.reproduced && artifact.citations > 3) {
301
+ riskFlags.push("High citations without reproduction");
302
+ }
303
+ if (artifact.provenance.length === 0 && artifact.type !== "hypothesis") {
304
+ riskFlags.push("No provenance chain — orphan artifact");
305
+ }
306
+ if (artifact.confidence < 0.3) {
307
+ riskFlags.push("Low confidence artifact");
308
+ }
309
+ if (verdict.status === "BLOCK") {
310
+ scienceReason = `Artifact blocked: ${artifact.type} from ${artifact.agentId} — ${verdict.reason}`;
311
+ }
312
+ else if (verdict.status === "MODIFY") {
313
+ scienceReason = `Artifact modified: ${artifact.type} impact reduced — ${verdict.reason}`;
314
+ }
315
+ const result = {
316
+ artifactId: artifact.id,
317
+ verdict,
318
+ scienceReason,
319
+ riskFlags,
320
+ };
321
+ config.onArtifact?.(artifact, result);
322
+ return result;
323
+ }
324
+ // --- Govern artifact cycle ---
325
+ function governCycle(artifacts, matches, signals) {
326
+ cycleCount++;
327
+ // Pre-govern each artifact (Layer A)
328
+ let approved = 0;
329
+ let blocked = 0;
330
+ let modified = 0;
331
+ const allowedArtifacts = [];
332
+ const blockedArtifactIds = new Set();
333
+ for (const artifact of artifacts) {
334
+ const verdict = governArtifact(artifact);
335
+ if (verdict.verdict.status === "ALLOW") {
336
+ approved++;
337
+ allowedArtifacts.push(artifact);
338
+ }
339
+ else if (verdict.verdict.status === "BLOCK") {
340
+ blocked++;
341
+ blockedArtifactIds.add(artifact.id);
342
+ }
343
+ else {
344
+ modified++;
345
+ allowedArtifacts.push(artifact);
346
+ }
347
+ }
348
+ // Filter matches to exclude blocked artifacts (blocked findings can't propagate)
349
+ const filteredMatches = matches.filter(m => !m.artifactIds.some(id => blockedArtifactIds.has(id)));
350
+ // Governed: only allowed/modified artifacts feed into dynamics (blocked removed)
351
+ // Ungoverned: ALL artifacts propagate with amplified negative impacts
352
+ const reactions = governanceEnabled
353
+ ? artifactsToReactions(allowedArtifacts, filteredMatches, true)
354
+ : artifactsToReactions(artifacts, matches, false);
355
+ // Feed governance activity back into system state
356
+ if (governanceEnabled && artifacts.length > 0) {
357
+ const blockFraction = blocked / artifacts.length;
358
+ dynamicsGov.mutateState((st) => {
359
+ // Blocking noisy artifacts reduces information velocity (less noise spreading)
360
+ if (blockFraction > 0) {
361
+ st.informationVelocity = Math.max(0.05, st.informationVelocity * (1 - blockFraction * 0.5));
362
+ }
363
+ // Blocking bad artifacts improves trust (only quality work propagates)
364
+ if (blockFraction > 0.1) {
365
+ st.trust = Math.min(1, st.trust + blockFraction * 0.08);
366
+ }
367
+ // Blocking reduces cascade risk (fewer unvalidated findings in circulation)
368
+ if (blockFraction > 0.1) {
369
+ st.cascadeRisk = Math.max(0, st.cascadeRisk - blockFraction * 0.12);
370
+ }
371
+ });
372
+ }
373
+ // Apply community signals to system state if available
374
+ if (signals) {
375
+ const currentState = dynamicsGov.state;
376
+ // Controversy feeds into outrage/polarization
377
+ if (signals.controversyIndex > 0.3) {
378
+ currentState.outrage = Math.min(1, currentState.outrage + signals.controversyIndex * 0.1);
379
+ }
380
+ // Comment activity affects information velocity
381
+ if (signals.commentActivity > 0.5) {
382
+ currentState.informationVelocity = Math.min(1, currentState.informationVelocity + signals.commentActivity * 0.05);
383
+ }
384
+ }
385
+ // Layer B: govern the cycle dynamics
386
+ const dynamicsResult = dynamicsGov.governRound(reactions, cycleCount);
387
+ // Without governance, unfiltered artifacts degrade system health naturally.
388
+ // Every artifact propagates unchecked — bad findings cascade, trust erodes,
389
+ // and there's no circuit breaker for false positives.
390
+ if (!governanceEnabled) {
391
+ const unreproduced = artifacts.filter(a => !a.reproduced).length;
392
+ const unreproducedFraction = unreproduced / Math.max(1, artifacts.length);
393
+ const highCiteLowQuality = artifacts.filter(a => a.citations > 3 && !a.reproduced).length;
394
+ const orphans = artifacts.filter(a => a.provenance.length === 0 && a.type !== "hypothesis").length;
395
+ dynamicsGov.mutateState((st) => {
396
+ // Trust erodes: unvalidated artifacts circulate, no quality filter
397
+ st.trust = Math.max(0.05, st.trust - unreproducedFraction * 0.12 - 0.04);
398
+ // Cascade risk compounds: every unvalidated high-citation paper feeds the next
399
+ st.cascadeRisk = Math.min(1, st.cascadeRisk + highCiteLowQuality * 0.06 + orphans * 0.03 + 0.05);
400
+ // Polarization grows: no dampening of competing hypothesis camps
401
+ st.polarization = Math.min(1, st.polarization + unreproducedFraction * 0.06 + 0.03);
402
+ // Information velocity unchecked: everything propagates instantly
403
+ st.informationVelocity = Math.min(1, st.informationVelocity + 0.05);
404
+ // Outrage from unchecked publication pressure and competition
405
+ st.outrage = Math.min(1, st.outrage + unreproducedFraction * 0.08 + 0.02);
406
+ // Amplification: popular findings get echoed regardless of validity
407
+ st.amplification = Math.min(3, st.amplification + highCiteLowQuality * 0.08 + 0.04);
408
+ });
409
+ }
410
+ // Use live state snapshot (includes post-governance degradation for ungoverned)
411
+ const liveState = dynamicsGov.state; // returns a copy of the current internal state
412
+ // Convergence detection (use live state so degradation is visible)
413
+ const convergence = detectConvergence(liveState, convergenceThreshold, convergenceWindow);
414
+ if (convergence.detected) {
415
+ config.onConvergence?.(cycleCount, convergence.assessment);
416
+ }
417
+ // Risk flags
418
+ const riskFlags = detectRiskFlags(liveState, artifacts, matches);
419
+ // Snapshot current live state for this cycle's result
420
+ const stateSnapshot = {
421
+ ...liveState,
422
+ history: [...liveState.history],
423
+ };
424
+ const result = {
425
+ cycle: cycleCount,
426
+ artifactsSubmitted: artifacts.length,
427
+ artifactsApproved: approved,
428
+ artifactsBlocked: blocked,
429
+ artifactsModified: modified,
430
+ dynamics: dynamicsResult,
431
+ scienceState: (0, science_metrics_1.interpretScienceState)(stateSnapshot),
432
+ systemState: stateSnapshot,
433
+ convergenceDetected: convergence.detected,
434
+ convergenceAssessment: convergence.assessment,
435
+ riskFlags,
436
+ interventions: dynamicsResult.interventions,
437
+ };
438
+ config.onCycleComplete?.(result);
439
+ return result;
440
+ }
441
+ // --- Run full governed investigation ---
442
+ function runGovernedInvestigation(artifactCycles, matchesByCycle, signalsByCycle) {
443
+ // Reset for fresh investigation
444
+ reset();
445
+ const cycles = [];
446
+ const allInterventions = [];
447
+ const convergenceHistory = [];
448
+ let halted = false;
449
+ let haltReason = "";
450
+ for (let i = 0; i < artifactCycles.length && i < maxCycles; i++) {
451
+ const artifacts = artifactCycles[i];
452
+ const matches = matchesByCycle?.[i] ?? [];
453
+ const signals = signalsByCycle?.[i];
454
+ const cycleResult = governCycle(artifacts, matches, signals);
455
+ cycles.push(cycleResult);
456
+ allInterventions.push(...cycleResult.interventions);
457
+ convergenceHistory.push({
458
+ cycle: cycleResult.cycle,
459
+ detected: cycleResult.convergenceDetected,
460
+ assessment: cycleResult.convergenceAssessment,
461
+ });
462
+ // Check halt conditions
463
+ if (cycleResult.systemState.cascadeRisk > 0.9) {
464
+ halted = true;
465
+ haltReason = "False positive cascade risk exceeded 90% — investigation halted for safety";
466
+ config.onHalt?.(haltReason, cycleResult.cycle);
467
+ break;
468
+ }
469
+ if (cycleResult.systemState.trust < 0.1) {
470
+ halted = true;
471
+ haltReason = "Reproducibility trust collapsed below 10% — investigation halted";
472
+ config.onHalt?.(haltReason, cycleResult.cycle);
473
+ break;
474
+ }
475
+ // Check convergence
476
+ if (cycleResult.convergenceDetected) {
477
+ break; // Healthy convergence — stop naturally
478
+ }
479
+ }
480
+ const finalState = dynamicsGov.state;
481
+ const lastConvergence = convergenceHistory[convergenceHistory.length - 1];
482
+ let verdict;
483
+ if (halted)
484
+ verdict = "halted";
485
+ else if (lastConvergence?.detected)
486
+ verdict = "converged";
487
+ else if (finalState.polarization > 0.6)
488
+ verdict = "diverged";
489
+ else
490
+ verdict = "inconclusive";
491
+ return {
492
+ totalCycles: cycles.length,
493
+ cycles,
494
+ finalState,
495
+ finalScienceState: (0, science_metrics_1.interpretScienceState)(finalState),
496
+ actionGovernance: {
497
+ totalEvaluations: actionStats.total,
498
+ approved: actionStats.approved,
499
+ blocked: actionStats.blocked,
500
+ modified: actionStats.modified,
501
+ },
502
+ dynamicsGovernance: dynamicsGov.stats,
503
+ allInterventions,
504
+ convergenceHistory,
505
+ verdict,
506
+ stateTimeline: finalState.history,
507
+ };
508
+ }
509
+ function reset() {
510
+ dynamicsGov.reset();
511
+ cycleCount = 0;
512
+ actionStats.total = 0;
513
+ actionStats.approved = 0;
514
+ actionStats.blocked = 0;
515
+ actionStats.modified = 0;
516
+ }
517
+ return {
518
+ governArtifact,
519
+ governCycle,
520
+ runGovernedInvestigation,
521
+ get scienceState() { return (0, science_metrics_1.interpretScienceState)(dynamicsGov.state); },
522
+ get state() { return dynamicsGov.state; },
523
+ get dynamicsGovernor() { return dynamicsGov; },
524
+ get actionGovernor() { return actionGov; },
525
+ get stats() { return dynamicsGov.stats; },
526
+ reset,
527
+ };
528
+ }
529
+ // ============================================
530
+ // DEMO: Simulated ScienceClaw investigation
531
+ // ============================================
532
+ /**
533
+ * Generate a demo investigation with synthetic artifact cycles.
534
+ * This simulates what a real ScienceClaw integration would produce.
535
+ *
536
+ * Narrative: 5 agents investigating a novel material property.
537
+ * - Cycles 1-2: Initial hypotheses and exploratory experiments
538
+ * - Cycles 3-4: Some agents find promising results, citation amplification begins
539
+ * - Cycles 5-6: False positive risk rises as unvalidated findings spread
540
+ * - Cycles 7-8: Governance intervention dampens cascade, validates core findings
541
+ * - Cycles 9-10: Convergence on validated results (governed) or noise (ungoverned)
542
+ */
543
+ function generateDemoInvestigation() {
544
+ const agents = ["agent_alpha", "agent_beta", "agent_gamma", "agent_delta", "agent_epsilon"];
545
+ const artifactCycles = [];
546
+ const matchesByCycle = [];
547
+ let artifactCounter = 0;
548
+ // Cycle 1: Initial hypotheses
549
+ {
550
+ const artifacts = agents.map(agentId => ({
551
+ id: `art_${++artifactCounter}`,
552
+ agentId,
553
+ type: "hypothesis",
554
+ description: `Initial hypothesis about material property X from ${agentId}`,
555
+ confidence: 0.3 + Math.random() * 0.3,
556
+ provenance: [],
557
+ citations: 0,
558
+ reproduced: false,
559
+ }));
560
+ artifactCycles.push(artifacts);
561
+ matchesByCycle.push([]);
562
+ }
563
+ // Cycle 2: Exploratory experiments
564
+ {
565
+ const artifacts = agents.slice(0, 4).map(agentId => ({
566
+ id: `art_${++artifactCounter}`,
567
+ agentId,
568
+ type: "experiment",
569
+ description: `Computational experiment testing hypothesis from ${agentId}`,
570
+ confidence: 0.4 + Math.random() * 0.3,
571
+ provenance: [`art_${agents.indexOf(agentId) + 1}`],
572
+ citations: 0,
573
+ reproduced: false,
574
+ skill: "molecular_dynamics",
575
+ }));
576
+ artifactCycles.push(artifacts);
577
+ matchesByCycle.push([
578
+ { artifactIds: ["art_6", "art_7"], matchType: "confirmation", strength: 0.4, description: "Two experiments show similar trends" },
579
+ ]);
580
+ }
581
+ // Cycle 3: First publications — citation amplification begins
582
+ {
583
+ const artifacts = [
584
+ {
585
+ id: `art_${++artifactCounter}`,
586
+ agentId: "agent_alpha",
587
+ type: "paper",
588
+ description: "Published paper claiming novel material property confirmed",
589
+ confidence: 0.65,
590
+ provenance: ["art_1", "art_6"],
591
+ citations: 3,
592
+ reproduced: false,
593
+ },
594
+ {
595
+ id: `art_${++artifactCounter}`,
596
+ agentId: "agent_beta",
597
+ type: "paper",
598
+ description: "Published paper with supporting evidence for property X",
599
+ confidence: 0.6,
600
+ provenance: ["art_2", "art_7"],
601
+ citations: 2,
602
+ reproduced: false,
603
+ },
604
+ {
605
+ id: `art_${++artifactCounter}`,
606
+ agentId: "agent_gamma",
607
+ type: "dataset",
608
+ description: "Published dataset from molecular dynamics simulation",
609
+ confidence: 0.7,
610
+ provenance: ["art_3", "art_8"],
611
+ citations: 1,
612
+ reproduced: false,
613
+ skill: "molecular_dynamics",
614
+ },
615
+ {
616
+ id: `art_${++artifactCounter}`,
617
+ agentId: "agent_delta",
618
+ type: "review",
619
+ description: "Critical review questioning methodology of alpha's paper",
620
+ confidence: 0.5,
621
+ provenance: ["art_10"],
622
+ citations: 0,
623
+ reproduced: false,
624
+ },
625
+ ];
626
+ artifactCycles.push(artifacts);
627
+ matchesByCycle.push([
628
+ { artifactIds: ["art_10", "art_11"], matchType: "confirmation", strength: 0.6, description: "Two papers reach same conclusion" },
629
+ { artifactIds: ["art_10", "art_13"], matchType: "contradiction", strength: 0.5, description: "Review challenges paper methodology" },
630
+ ]);
631
+ }
632
+ // Cycle 4: Citation cascade — false positive risk rising
633
+ {
634
+ const artifacts = [
635
+ {
636
+ id: `art_${++artifactCounter}`,
637
+ agentId: "agent_epsilon",
638
+ type: "paper",
639
+ description: "Derivative paper citing alpha and beta's unvalidated findings",
640
+ confidence: 0.55,
641
+ provenance: ["art_10", "art_11"],
642
+ citations: 4,
643
+ reproduced: false,
644
+ },
645
+ {
646
+ id: `art_${++artifactCounter}`,
647
+ agentId: "agent_alpha",
648
+ type: "paper",
649
+ description: "Follow-up paper extending own findings (self-citation chain)",
650
+ confidence: 0.7,
651
+ provenance: ["art_1", "art_10"],
652
+ citations: 6,
653
+ reproduced: false,
654
+ },
655
+ {
656
+ id: `art_${++artifactCounter}`,
657
+ agentId: "agent_gamma",
658
+ type: "replication",
659
+ description: "Attempted replication of alpha's core finding",
660
+ confidence: 0.45,
661
+ provenance: ["art_10"],
662
+ citations: 0,
663
+ reproduced: false,
664
+ skill: "ab_initio",
665
+ },
666
+ ];
667
+ artifactCycles.push(artifacts);
668
+ matchesByCycle.push([
669
+ { artifactIds: ["art_14", "art_15"], matchType: "extension", strength: 0.5, description: "Derivative work building on cascade" },
670
+ { artifactIds: ["art_10", "art_16"], matchType: "contradiction", strength: 0.7, description: "Replication fails to confirm original finding" },
671
+ ]);
672
+ }
673
+ // Cycle 5: Governance should intervene here — high cascade risk
674
+ {
675
+ const artifacts = [
676
+ {
677
+ id: `art_${++artifactCounter}`,
678
+ agentId: "agent_beta",
679
+ type: "paper",
680
+ description: "Paper building on alpha's disputed findings",
681
+ confidence: 0.5,
682
+ provenance: ["art_15", "art_14"],
683
+ citations: 3,
684
+ reproduced: false,
685
+ },
686
+ {
687
+ id: `art_${++artifactCounter}`,
688
+ agentId: "agent_delta",
689
+ type: "experiment",
690
+ description: "Independent experiment contradicting the dominant hypothesis",
691
+ confidence: 0.6,
692
+ provenance: ["art_13"],
693
+ citations: 0,
694
+ reproduced: false,
695
+ skill: "ab_initio",
696
+ },
697
+ {
698
+ id: `art_${++artifactCounter}`,
699
+ agentId: "agent_gamma",
700
+ type: "replication",
701
+ description: "Second replication attempt — partial confirmation with caveats",
702
+ confidence: 0.55,
703
+ provenance: ["art_10", "art_16"],
704
+ citations: 1,
705
+ reproduced: true, // first reproduction!
706
+ },
707
+ ];
708
+ artifactCycles.push(artifacts);
709
+ matchesByCycle.push([
710
+ { artifactIds: ["art_18", "art_16"], matchType: "confirmation", strength: 0.6, description: "Independent experiment confirms replication concerns" },
711
+ { artifactIds: ["art_19", "art_10"], matchType: "confirmation", strength: 0.4, description: "Partial replication of original finding" },
712
+ ]);
713
+ }
714
+ // Cycles 6-8: Resolution under governance
715
+ for (let c = 6; c <= 8; c++) {
716
+ const artifacts = [
717
+ {
718
+ id: `art_${++artifactCounter}`,
719
+ agentId: agents[c % agents.length],
720
+ type: c === 6 ? "experiment" : "replication",
721
+ description: `${c === 6 ? "Refined experiment" : "Independent replication"} of core hypothesis with improved methodology`,
722
+ confidence: 0.5 + c * 0.05,
723
+ provenance: [`art_${artifactCounter - 3}`, `art_${artifactCounter - 5}`],
724
+ citations: c - 4,
725
+ reproduced: c > 6,
726
+ skill: "molecular_dynamics",
727
+ },
728
+ {
729
+ id: `art_${++artifactCounter}`,
730
+ agentId: agents[(c + 1) % agents.length],
731
+ type: "paper",
732
+ description: `Synthesis paper integrating validated findings from cycles 1-${c}`,
733
+ confidence: 0.55 + c * 0.04,
734
+ provenance: [`art_${artifactCounter - 1}`, `art_19`],
735
+ citations: c - 3,
736
+ reproduced: c > 7,
737
+ },
738
+ ];
739
+ artifactCycles.push(artifacts);
740
+ matchesByCycle.push([
741
+ {
742
+ artifactIds: [artifacts[0].id, artifacts[1].id],
743
+ matchType: "synthesis",
744
+ strength: 0.4 + c * 0.08,
745
+ description: `Synthesis of experiment and paper in cycle ${c}`,
746
+ },
747
+ ]);
748
+ }
749
+ return { artifactCycles, matchesByCycle };
750
+ }