@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.
- package/README.md +562 -68
- package/dist/adapters/mirofish.js +461 -0
- package/dist/adapters/scienceclaw.js +750 -0
- package/dist/assets/index-B64NuIXu.css +1 -0
- package/dist/assets/index-DbzSnYxr.js +532 -0
- package/dist/assets/mirotir-logo-DUexumBH.svg +185 -0
- package/dist/assets/reportEngine-DKWTrP6-.js +1 -0
- package/dist/components/ConstraintsPanel.js +11 -0
- package/dist/components/StakeholderBuilder.js +32 -0
- package/dist/components/ui/badge.js +24 -0
- package/dist/components/ui/button.js +70 -0
- package/dist/components/ui/card.js +57 -0
- package/dist/components/ui/input.js +44 -0
- package/dist/components/ui/label.js +45 -0
- package/dist/components/ui/select.js +70 -0
- package/dist/engine/aiProvider.js +681 -0
- package/dist/engine/auditTrace.js +352 -0
- package/dist/engine/behavioralAnalysis.js +605 -0
- package/dist/engine/cli.js +1408 -299
- package/dist/engine/dynamicsGovernance.js +588 -0
- package/dist/engine/fullGovernedLoop.js +367 -0
- package/dist/engine/governance.js +8 -3
- package/dist/engine/governedSimulation.js +114 -17
- package/dist/engine/index.js +56 -1
- package/dist/engine/liveAdapter.js +342 -0
- package/dist/engine/liveVisualizer.js +4284 -0
- package/dist/engine/metrics/science.metrics.js +335 -0
- package/dist/engine/narrativeInjection.js +360 -0
- package/dist/engine/policyEnforcement.js +1611 -0
- package/dist/engine/policyEngine.js +799 -0
- package/dist/engine/primeRadiant.js +540 -0
- package/dist/engine/reasoningEngine.js +57 -3
- package/dist/engine/reportEngine.js +97 -0
- package/dist/engine/scenarioCapsule.js +56 -0
- package/dist/engine/scenarioComparison.js +463 -0
- package/dist/engine/scenarioLibrary.js +248 -0
- package/dist/engine/swarmSimulation.js +54 -1
- package/dist/engine/worldComparison.js +358 -0
- package/dist/engine/worldStorage.js +232 -0
- package/dist/favicon.ico +0 -0
- package/dist/index.html +23 -0
- package/dist/lib/reasoningEngine.js +290 -0
- package/dist/lib/simulationAdapter.js +686 -0
- package/dist/lib/swarmParser.js +291 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/utils.js +8 -0
- package/dist/placeholder.svg +1 -0
- package/dist/robots.txt +14 -0
- package/dist/runtime/govern.js +473 -0
- package/dist/runtime/index.js +75 -0
- package/dist/runtime/types.js +11 -0
- package/package.json +17 -12
- 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
|
+
}
|