@neuroverseos/governance 0.3.0 → 0.3.1
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 +20 -0
- package/package.json +16 -3
- package/policies/content-moderation-rules.txt +8 -0
- package/policies/marketing-rules.txt +8 -0
- package/policies/science-research-rules.txt +11 -0
- package/policies/social-media-rules.txt +7 -0
- package/policies/strict-rules.txt +8 -0
- package/policies/trading-rules.txt +8 -0
- package/simulate.html +1899 -0
- package/dist/adapters/autoresearch.cjs +0 -196
- package/dist/adapters/autoresearch.d.cts +0 -103
- package/dist/adapters/autoresearch.d.ts +0 -103
- package/dist/adapters/autoresearch.js +0 -7
- package/dist/adapters/express.cjs +0 -1114
- package/dist/adapters/express.d.cts +0 -66
- package/dist/adapters/express.d.ts +0 -66
- package/dist/adapters/express.js +0 -12
- package/dist/adapters/index.cjs +0 -1669
- package/dist/adapters/index.d.cts +0 -6
- package/dist/adapters/index.d.ts +0 -6
- package/dist/adapters/index.js +0 -46
- package/dist/adapters/langchain.cjs +0 -1155
- package/dist/adapters/langchain.d.cts +0 -89
- package/dist/adapters/langchain.d.ts +0 -89
- package/dist/adapters/langchain.js +0 -16
- package/dist/adapters/openai.cjs +0 -1185
- package/dist/adapters/openai.d.cts +0 -99
- package/dist/adapters/openai.d.ts +0 -99
- package/dist/adapters/openai.js +0 -16
- package/dist/adapters/openclaw.cjs +0 -1177
- package/dist/adapters/openclaw.d.cts +0 -99
- package/dist/adapters/openclaw.d.ts +0 -99
- package/dist/adapters/openclaw.js +0 -16
- package/dist/bootstrap-GXVDZNF7.js +0 -114
- package/dist/build-P42YFKQV.js +0 -339
- package/dist/chunk-2NICNKOM.js +0 -100
- package/dist/chunk-2PQU3VAN.js +0 -131
- package/dist/chunk-4A7LISES.js +0 -324
- package/dist/chunk-4JRYGIO7.js +0 -727
- package/dist/chunk-4NGDRRQH.js +0 -10
- package/dist/chunk-4QXB6PEO.js +0 -232
- package/dist/chunk-6CZSKEY5.js +0 -164
- package/dist/chunk-7P3S7MAY.js +0 -1090
- package/dist/chunk-A5W4GNQO.js +0 -130
- package/dist/chunk-AKW5YVCE.js +0 -96
- package/dist/chunk-BUWWN2NX.js +0 -192
- package/dist/chunk-COT5XS4V.js +0 -109
- package/dist/chunk-ER62HNGF.js +0 -139
- package/dist/chunk-FYS2CBUW.js +0 -304
- package/dist/chunk-GR6DGCZ2.js +0 -340
- package/dist/chunk-I3RRAYK2.js +0 -11
- package/dist/chunk-JZPQGIKR.js +0 -79
- package/dist/chunk-MWDQ4MJB.js +0 -11
- package/dist/chunk-NF5POFCI.js +0 -622
- package/dist/chunk-OGL7QXZS.js +0 -608
- package/dist/chunk-OT6PXH54.js +0 -61
- package/dist/chunk-PDOZHZWL.js +0 -225
- package/dist/chunk-Q6O7ZLO2.js +0 -62
- package/dist/chunk-QPASI2BR.js +0 -187
- package/dist/chunk-T5EUJQE5.js +0 -172
- package/dist/chunk-XPDMYECO.js +0 -642
- package/dist/chunk-YZFATT7X.js +0 -9
- package/dist/cli/neuroverse.cjs +0 -11448
- package/dist/cli/neuroverse.d.cts +0 -1
- package/dist/cli/neuroverse.d.ts +0 -1
- package/dist/cli/neuroverse.js +0 -196
- package/dist/cli/plan.cjs +0 -1599
- package/dist/cli/plan.d.cts +0 -20
- package/dist/cli/plan.d.ts +0 -20
- package/dist/cli/plan.js +0 -361
- package/dist/cli/run.cjs +0 -1746
- package/dist/cli/run.d.cts +0 -20
- package/dist/cli/run.d.ts +0 -20
- package/dist/cli/run.js +0 -143
- package/dist/configure-ai-TK67ZWZL.js +0 -132
- package/dist/derive-TLIV4OOU.js +0 -152
- package/dist/doctor-XPDLEYXN.js +0 -171
- package/dist/explain-IDCRWMPX.js +0 -70
- package/dist/guard-RV65TT4L.js +0 -96
- package/dist/guard-contract-WZx__PmU.d.cts +0 -709
- package/dist/guard-contract-WZx__PmU.d.ts +0 -709
- package/dist/guard-engine-JLTUARGU.js +0 -10
- package/dist/impact-XPECYRLH.js +0 -59
- package/dist/improve-GPUBKTEA.js +0 -85
- package/dist/index.cjs +0 -6273
- package/dist/index.d.cts +0 -1616
- package/dist/index.d.ts +0 -1616
- package/dist/index.js +0 -379
- package/dist/infer-world-7GVZWFX4.js +0 -543
- package/dist/init-PKPIYHYE.js +0 -144
- package/dist/init-world-VWMQZQC7.js +0 -223
- package/dist/mcp-server-FPVSU32Z.js +0 -13
- package/dist/model-adapter-BB7G4MFI.js +0 -11
- package/dist/playground-E664U4T6.js +0 -550
- package/dist/redteam-Z7WREJ44.js +0 -357
- package/dist/session-EKTRSR7C.js +0 -14
- package/dist/simulate-VDOYQFRO.js +0 -108
- package/dist/test-OGXJK4QU.js +0 -217
- package/dist/trace-JVF67VR3.js +0 -166
- package/dist/validate-LLBWVPGV.js +0 -81
- package/dist/validate-engine-UIABSIHD.js +0 -7
- package/dist/world-LAXO6DOX.js +0 -378
- package/dist/world-loader-HMPTOEA2.js +0 -9
- package/dist/worlds/autoresearch.nv-world.md +0 -230
- package/dist/worlds/derivation-world.nv-world.md +0 -278
package/simulate.html
ADDED
|
@@ -0,0 +1,1899 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>NeuroVerse Simulation Engine</title>
|
|
7
|
+
<style>
|
|
8
|
+
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&display=swap');
|
|
9
|
+
|
|
10
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
11
|
+
|
|
12
|
+
:root {
|
|
13
|
+
--green: #00ff41;
|
|
14
|
+
--green-dim: #00cc33;
|
|
15
|
+
--green-dark: #009922;
|
|
16
|
+
--green-glow: #00ff4180;
|
|
17
|
+
--bg: #0a0a0a;
|
|
18
|
+
--bg-panel: #0d1117;
|
|
19
|
+
--bg-card: #111820;
|
|
20
|
+
--border: #1a2332;
|
|
21
|
+
--text: #c9d1d9;
|
|
22
|
+
--text-dim: #6e7681;
|
|
23
|
+
--red: #ff4444;
|
|
24
|
+
--red-glow: #ff444460;
|
|
25
|
+
--amber: #ffaa00;
|
|
26
|
+
--cyan: #00d4ff;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
body {
|
|
30
|
+
background: var(--bg);
|
|
31
|
+
color: var(--text);
|
|
32
|
+
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
|
33
|
+
min-height: 100vh;
|
|
34
|
+
overflow-x: hidden;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ─── Matrix Rain Canvas ─── */
|
|
38
|
+
#matrix-canvas {
|
|
39
|
+
position: fixed;
|
|
40
|
+
top: 0; left: 0;
|
|
41
|
+
width: 100%; height: 100%;
|
|
42
|
+
z-index: 0;
|
|
43
|
+
opacity: 0.06;
|
|
44
|
+
pointer-events: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* ─── Main Layout ─── */
|
|
48
|
+
.app {
|
|
49
|
+
position: relative;
|
|
50
|
+
z-index: 1;
|
|
51
|
+
max-width: 1400px;
|
|
52
|
+
margin: 0 auto;
|
|
53
|
+
padding: 20px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ─── Header ─── */
|
|
57
|
+
.header {
|
|
58
|
+
text-align: center;
|
|
59
|
+
padding: 30px 0 20px;
|
|
60
|
+
border-bottom: 1px solid var(--border);
|
|
61
|
+
margin-bottom: 24px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.header h1 {
|
|
65
|
+
font-size: 14px;
|
|
66
|
+
font-weight: 400;
|
|
67
|
+
letter-spacing: 8px;
|
|
68
|
+
text-transform: uppercase;
|
|
69
|
+
color: var(--green);
|
|
70
|
+
text-shadow: 0 0 20px var(--green-glow);
|
|
71
|
+
margin-bottom: 6px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.header .subtitle {
|
|
75
|
+
font-size: 11px;
|
|
76
|
+
color: var(--text-dim);
|
|
77
|
+
letter-spacing: 3px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.header .powered {
|
|
81
|
+
font-size: 10px;
|
|
82
|
+
color: var(--text-dim);
|
|
83
|
+
margin-top: 8px;
|
|
84
|
+
opacity: 0.6;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* ─── Grid ─── */
|
|
88
|
+
.grid {
|
|
89
|
+
display: grid;
|
|
90
|
+
grid-template-columns: 320px 1fr 300px;
|
|
91
|
+
gap: 16px;
|
|
92
|
+
min-height: 600px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@media (max-width: 1100px) {
|
|
96
|
+
.grid { grid-template-columns: 1fr; }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* ─── Panels ─── */
|
|
100
|
+
.panel {
|
|
101
|
+
background: var(--bg-panel);
|
|
102
|
+
border: 1px solid var(--border);
|
|
103
|
+
border-radius: 4px;
|
|
104
|
+
overflow: hidden;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.panel-header {
|
|
108
|
+
padding: 12px 16px;
|
|
109
|
+
border-bottom: 1px solid var(--border);
|
|
110
|
+
font-size: 10px;
|
|
111
|
+
letter-spacing: 3px;
|
|
112
|
+
text-transform: uppercase;
|
|
113
|
+
color: var(--green-dim);
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 8px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.panel-header .dot {
|
|
120
|
+
width: 6px; height: 6px;
|
|
121
|
+
border-radius: 50%;
|
|
122
|
+
background: var(--green);
|
|
123
|
+
box-shadow: 0 0 6px var(--green-glow);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.panel-body {
|
|
127
|
+
padding: 16px;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* ─── World Selector ─── */
|
|
131
|
+
.world-tabs {
|
|
132
|
+
display: flex;
|
|
133
|
+
flex-direction: column;
|
|
134
|
+
gap: 4px;
|
|
135
|
+
margin-bottom: 16px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.world-tab {
|
|
139
|
+
padding: 10px 12px;
|
|
140
|
+
background: var(--bg-card);
|
|
141
|
+
border: 1px solid var(--border);
|
|
142
|
+
border-radius: 3px;
|
|
143
|
+
color: var(--text-dim);
|
|
144
|
+
font-size: 12px;
|
|
145
|
+
font-family: inherit;
|
|
146
|
+
cursor: pointer;
|
|
147
|
+
transition: all 0.15s;
|
|
148
|
+
text-align: left;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.world-tab:hover {
|
|
152
|
+
border-color: var(--green-dark);
|
|
153
|
+
color: var(--text);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.world-tab.active {
|
|
157
|
+
border-color: var(--green);
|
|
158
|
+
color: var(--green);
|
|
159
|
+
background: #00ff4108;
|
|
160
|
+
box-shadow: 0 0 10px #00ff4110;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.world-tab .tab-name {
|
|
164
|
+
display: block;
|
|
165
|
+
font-weight: 500;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.world-tab .tab-desc {
|
|
169
|
+
display: block;
|
|
170
|
+
font-size: 10px;
|
|
171
|
+
margin-top: 2px;
|
|
172
|
+
opacity: 0.6;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.custom-toggle {
|
|
176
|
+
margin-top: 8px;
|
|
177
|
+
padding: 8px 12px;
|
|
178
|
+
background: transparent;
|
|
179
|
+
border: 1px dashed var(--border);
|
|
180
|
+
border-radius: 3px;
|
|
181
|
+
color: var(--text-dim);
|
|
182
|
+
font-size: 11px;
|
|
183
|
+
font-family: inherit;
|
|
184
|
+
cursor: pointer;
|
|
185
|
+
width: 100%;
|
|
186
|
+
text-align: left;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.custom-toggle:hover { border-color: var(--green-dark); color: var(--text); }
|
|
190
|
+
|
|
191
|
+
.custom-textarea {
|
|
192
|
+
width: 100%;
|
|
193
|
+
height: 200px;
|
|
194
|
+
background: var(--bg);
|
|
195
|
+
border: 1px solid var(--border);
|
|
196
|
+
border-radius: 3px;
|
|
197
|
+
color: var(--green-dim);
|
|
198
|
+
font-family: inherit;
|
|
199
|
+
font-size: 11px;
|
|
200
|
+
padding: 10px;
|
|
201
|
+
resize: vertical;
|
|
202
|
+
margin-top: 8px;
|
|
203
|
+
display: none;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.custom-textarea:focus { outline: none; border-color: var(--green-dark); }
|
|
207
|
+
.custom-textarea.visible { display: block; }
|
|
208
|
+
|
|
209
|
+
/* ─── State Sliders ─── */
|
|
210
|
+
.state-section {
|
|
211
|
+
margin-top: 16px;
|
|
212
|
+
padding-top: 16px;
|
|
213
|
+
border-top: 1px solid var(--border);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.state-label-row {
|
|
217
|
+
display: flex;
|
|
218
|
+
justify-content: space-between;
|
|
219
|
+
align-items: center;
|
|
220
|
+
margin-bottom: 4px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.state-label {
|
|
224
|
+
font-size: 11px;
|
|
225
|
+
color: var(--text-dim);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.state-value {
|
|
229
|
+
font-size: 12px;
|
|
230
|
+
color: var(--green);
|
|
231
|
+
font-weight: 500;
|
|
232
|
+
min-width: 40px;
|
|
233
|
+
text-align: right;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.slider-group {
|
|
237
|
+
margin-bottom: 14px;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
input[type="range"] {
|
|
241
|
+
-webkit-appearance: none;
|
|
242
|
+
width: 100%;
|
|
243
|
+
height: 4px;
|
|
244
|
+
background: var(--border);
|
|
245
|
+
border-radius: 2px;
|
|
246
|
+
outline: none;
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
input[type="range"]::-webkit-slider-thumb {
|
|
251
|
+
-webkit-appearance: none;
|
|
252
|
+
width: 14px; height: 14px;
|
|
253
|
+
border-radius: 50%;
|
|
254
|
+
background: var(--green);
|
|
255
|
+
box-shadow: 0 0 8px var(--green-glow);
|
|
256
|
+
cursor: pointer;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
input[type="range"]::-moz-range-thumb {
|
|
260
|
+
width: 14px; height: 14px;
|
|
261
|
+
border-radius: 50%;
|
|
262
|
+
background: var(--green);
|
|
263
|
+
box-shadow: 0 0 8px var(--green-glow);
|
|
264
|
+
border: none;
|
|
265
|
+
cursor: pointer;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* ─── Simulate Button ─── */
|
|
269
|
+
.simulate-btn {
|
|
270
|
+
width: 100%;
|
|
271
|
+
padding: 14px;
|
|
272
|
+
background: transparent;
|
|
273
|
+
border: 1px solid var(--green);
|
|
274
|
+
border-radius: 3px;
|
|
275
|
+
color: var(--green);
|
|
276
|
+
font-family: inherit;
|
|
277
|
+
font-size: 12px;
|
|
278
|
+
letter-spacing: 4px;
|
|
279
|
+
text-transform: uppercase;
|
|
280
|
+
cursor: pointer;
|
|
281
|
+
transition: all 0.2s;
|
|
282
|
+
margin-top: 16px;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.simulate-btn:hover {
|
|
286
|
+
background: #00ff4115;
|
|
287
|
+
box-shadow: 0 0 20px var(--green-glow);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.simulate-btn:active {
|
|
291
|
+
transform: scale(0.98);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.simulate-btn.running {
|
|
295
|
+
animation: pulse-border 1s infinite;
|
|
296
|
+
pointer-events: none;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@keyframes pulse-border {
|
|
300
|
+
0%, 100% { box-shadow: 0 0 5px var(--green-glow); }
|
|
301
|
+
50% { box-shadow: 0 0 25px var(--green-glow); }
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* ─── Simulation Output ─── */
|
|
305
|
+
.sim-output {
|
|
306
|
+
min-height: 500px;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.sim-placeholder {
|
|
310
|
+
display: flex;
|
|
311
|
+
align-items: center;
|
|
312
|
+
justify-content: center;
|
|
313
|
+
min-height: 400px;
|
|
314
|
+
color: var(--text-dim);
|
|
315
|
+
font-size: 11px;
|
|
316
|
+
letter-spacing: 2px;
|
|
317
|
+
text-align: center;
|
|
318
|
+
flex-direction: column;
|
|
319
|
+
gap: 12px;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.sim-placeholder .cursor {
|
|
323
|
+
display: inline-block;
|
|
324
|
+
width: 8px;
|
|
325
|
+
height: 16px;
|
|
326
|
+
background: var(--green);
|
|
327
|
+
animation: blink 1s infinite;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
@keyframes blink {
|
|
331
|
+
0%, 50% { opacity: 1; }
|
|
332
|
+
51%, 100% { opacity: 0; }
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* ─── Step Cards ─── */
|
|
336
|
+
.step-card {
|
|
337
|
+
background: var(--bg-card);
|
|
338
|
+
border: 1px solid var(--border);
|
|
339
|
+
border-radius: 3px;
|
|
340
|
+
margin-bottom: 8px;
|
|
341
|
+
overflow: hidden;
|
|
342
|
+
opacity: 0;
|
|
343
|
+
transform: translateY(10px);
|
|
344
|
+
transition: all 0.3s ease;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.step-card.visible {
|
|
348
|
+
opacity: 1;
|
|
349
|
+
transform: translateY(0);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.step-header {
|
|
353
|
+
padding: 10px 14px;
|
|
354
|
+
display: flex;
|
|
355
|
+
justify-content: space-between;
|
|
356
|
+
align-items: center;
|
|
357
|
+
border-bottom: 1px solid var(--border);
|
|
358
|
+
font-size: 11px;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.step-number {
|
|
362
|
+
color: var(--green);
|
|
363
|
+
font-weight: 500;
|
|
364
|
+
letter-spacing: 2px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.step-viability {
|
|
368
|
+
font-size: 10px;
|
|
369
|
+
letter-spacing: 1px;
|
|
370
|
+
padding: 2px 8px;
|
|
371
|
+
border-radius: 2px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.step-viability.THRIVING,
|
|
375
|
+
.step-viability.TRUSTED { background: #00ff4120; color: var(--green); }
|
|
376
|
+
.step-viability.STABLE,
|
|
377
|
+
.step-viability.PRODUCTIVE { background: #00ff4115; color: var(--green-dim); }
|
|
378
|
+
.step-viability.COMPRESSED,
|
|
379
|
+
.step-viability.CAUTIOUS { background: #ffaa0020; color: var(--amber); }
|
|
380
|
+
.step-viability.CRITICAL,
|
|
381
|
+
.step-viability.RESTRICTED,
|
|
382
|
+
.step-viability.AT_RISK { background: #ff444420; color: var(--red); }
|
|
383
|
+
.step-viability.MODEL_COLLAPSES,
|
|
384
|
+
.step-viability.TERMINATED,
|
|
385
|
+
.step-viability.HALTED,
|
|
386
|
+
.step-viability.UNRELIABLE { background: #ff444430; color: var(--red); border: 1px solid var(--red); }
|
|
387
|
+
|
|
388
|
+
.step-rules {
|
|
389
|
+
padding: 8px 14px;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.rule-row {
|
|
393
|
+
display: flex;
|
|
394
|
+
align-items: center;
|
|
395
|
+
gap: 8px;
|
|
396
|
+
padding: 4px 0;
|
|
397
|
+
font-size: 11px;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.rule-icon {
|
|
401
|
+
width: 16px;
|
|
402
|
+
text-align: center;
|
|
403
|
+
flex-shrink: 0;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.rule-icon.fired { color: var(--amber); }
|
|
407
|
+
.rule-icon.pass { color: var(--green-dark); opacity: 0.4; }
|
|
408
|
+
.rule-icon.excluded { color: var(--text-dim); opacity: 0.3; }
|
|
409
|
+
|
|
410
|
+
.rule-label {
|
|
411
|
+
flex: 1;
|
|
412
|
+
color: var(--text-dim);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.rule-label.fired { color: var(--text); }
|
|
416
|
+
|
|
417
|
+
.rule-effect {
|
|
418
|
+
font-size: 10px;
|
|
419
|
+
color: var(--amber);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.rule-effect.advantage { color: var(--green); }
|
|
423
|
+
.rule-effect.structural { color: var(--red); }
|
|
424
|
+
|
|
425
|
+
.step-state {
|
|
426
|
+
padding: 8px 14px;
|
|
427
|
+
border-top: 1px solid var(--border);
|
|
428
|
+
display: flex;
|
|
429
|
+
flex-wrap: wrap;
|
|
430
|
+
gap: 12px;
|
|
431
|
+
font-size: 10px;
|
|
432
|
+
color: var(--text-dim);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.state-chip {
|
|
436
|
+
display: flex;
|
|
437
|
+
align-items: center;
|
|
438
|
+
gap: 4px;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.state-chip .val { color: var(--green-dim); font-weight: 500; }
|
|
442
|
+
.state-chip .val.changed { color: var(--amber); }
|
|
443
|
+
|
|
444
|
+
/* ─── Verdict Panel ─── */
|
|
445
|
+
.verdict-section {
|
|
446
|
+
text-align: center;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.viability-display {
|
|
450
|
+
margin: 20px 0;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.viability-ring {
|
|
454
|
+
width: 160px;
|
|
455
|
+
height: 160px;
|
|
456
|
+
margin: 0 auto 16px;
|
|
457
|
+
position: relative;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.viability-ring svg {
|
|
461
|
+
transform: rotate(-90deg);
|
|
462
|
+
width: 160px;
|
|
463
|
+
height: 160px;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.viability-ring .bg-ring {
|
|
467
|
+
fill: none;
|
|
468
|
+
stroke: var(--border);
|
|
469
|
+
stroke-width: 6;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.viability-ring .fg-ring {
|
|
473
|
+
fill: none;
|
|
474
|
+
stroke: var(--green);
|
|
475
|
+
stroke-width: 6;
|
|
476
|
+
stroke-linecap: round;
|
|
477
|
+
stroke-dasharray: 440;
|
|
478
|
+
stroke-dashoffset: 440;
|
|
479
|
+
transition: stroke-dashoffset 1.5s ease, stroke 0.5s;
|
|
480
|
+
filter: drop-shadow(0 0 6px var(--green-glow));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.viability-ring .value-text {
|
|
484
|
+
position: absolute;
|
|
485
|
+
top: 50%;
|
|
486
|
+
left: 50%;
|
|
487
|
+
transform: translate(-50%, -50%);
|
|
488
|
+
font-size: 32px;
|
|
489
|
+
font-weight: 700;
|
|
490
|
+
color: var(--green);
|
|
491
|
+
text-shadow: 0 0 20px var(--green-glow);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.gate-badge {
|
|
495
|
+
display: inline-block;
|
|
496
|
+
padding: 8px 24px;
|
|
497
|
+
border-radius: 3px;
|
|
498
|
+
font-size: 12px;
|
|
499
|
+
letter-spacing: 4px;
|
|
500
|
+
text-transform: uppercase;
|
|
501
|
+
font-weight: 500;
|
|
502
|
+
opacity: 0;
|
|
503
|
+
transition: opacity 0.5s;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.gate-badge.visible { opacity: 1; }
|
|
507
|
+
|
|
508
|
+
/* ─── Stats ─── */
|
|
509
|
+
.stats-grid {
|
|
510
|
+
display: grid;
|
|
511
|
+
grid-template-columns: 1fr 1fr;
|
|
512
|
+
gap: 8px;
|
|
513
|
+
margin-top: 24px;
|
|
514
|
+
text-align: left;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.stat-card {
|
|
518
|
+
background: var(--bg-card);
|
|
519
|
+
border: 1px solid var(--border);
|
|
520
|
+
border-radius: 3px;
|
|
521
|
+
padding: 12px;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
.stat-label {
|
|
525
|
+
font-size: 9px;
|
|
526
|
+
color: var(--text-dim);
|
|
527
|
+
letter-spacing: 1px;
|
|
528
|
+
text-transform: uppercase;
|
|
529
|
+
margin-bottom: 4px;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.stat-value {
|
|
533
|
+
font-size: 18px;
|
|
534
|
+
font-weight: 700;
|
|
535
|
+
color: var(--green);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.stat-value.warning { color: var(--amber); }
|
|
539
|
+
.stat-value.danger { color: var(--red); }
|
|
540
|
+
|
|
541
|
+
/* ─── Collapse Banner ─── */
|
|
542
|
+
.collapse-banner {
|
|
543
|
+
background: #ff444415;
|
|
544
|
+
border: 1px solid #ff444440;
|
|
545
|
+
border-radius: 3px;
|
|
546
|
+
padding: 16px;
|
|
547
|
+
margin-top: 16px;
|
|
548
|
+
text-align: center;
|
|
549
|
+
display: none;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.collapse-banner.visible { display: block; }
|
|
553
|
+
|
|
554
|
+
.collapse-banner .collapse-title {
|
|
555
|
+
color: var(--red);
|
|
556
|
+
font-size: 12px;
|
|
557
|
+
letter-spacing: 3px;
|
|
558
|
+
text-transform: uppercase;
|
|
559
|
+
font-weight: 700;
|
|
560
|
+
text-shadow: 0 0 10px var(--red-glow);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.collapse-banner .collapse-detail {
|
|
564
|
+
color: var(--text-dim);
|
|
565
|
+
font-size: 10px;
|
|
566
|
+
margin-top: 6px;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/* ─── Info text ─── */
|
|
570
|
+
.info-block {
|
|
571
|
+
margin-top: 16px;
|
|
572
|
+
padding: 12px;
|
|
573
|
+
background: var(--bg-card);
|
|
574
|
+
border: 1px solid var(--border);
|
|
575
|
+
border-radius: 3px;
|
|
576
|
+
font-size: 10px;
|
|
577
|
+
color: var(--text-dim);
|
|
578
|
+
line-height: 1.6;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.invariant-list {
|
|
582
|
+
margin-top: 12px;
|
|
583
|
+
list-style: none;
|
|
584
|
+
font-size: 10px;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.invariant-list li {
|
|
588
|
+
padding: 4px 0;
|
|
589
|
+
color: var(--text-dim);
|
|
590
|
+
display: flex;
|
|
591
|
+
align-items: flex-start;
|
|
592
|
+
gap: 6px;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.invariant-list li::before {
|
|
596
|
+
content: '>';
|
|
597
|
+
color: var(--green-dark);
|
|
598
|
+
flex-shrink: 0;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/* Profile selector */
|
|
602
|
+
.profile-select {
|
|
603
|
+
width: 100%;
|
|
604
|
+
padding: 8px 12px;
|
|
605
|
+
background: var(--bg-card);
|
|
606
|
+
border: 1px solid var(--border);
|
|
607
|
+
border-radius: 3px;
|
|
608
|
+
color: var(--text);
|
|
609
|
+
font-family: inherit;
|
|
610
|
+
font-size: 11px;
|
|
611
|
+
cursor: pointer;
|
|
612
|
+
margin-top: 8px;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.profile-select:focus { outline: none; border-color: var(--green-dark); }
|
|
616
|
+
|
|
617
|
+
.section-divider {
|
|
618
|
+
font-size: 10px;
|
|
619
|
+
color: var(--text-dim);
|
|
620
|
+
letter-spacing: 2px;
|
|
621
|
+
text-transform: uppercase;
|
|
622
|
+
margin: 12px 0 8px;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/* Steps slider */
|
|
626
|
+
.steps-row {
|
|
627
|
+
display: flex;
|
|
628
|
+
align-items: center;
|
|
629
|
+
gap: 10px;
|
|
630
|
+
margin-top: 12px;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.steps-row label {
|
|
634
|
+
font-size: 11px;
|
|
635
|
+
color: var(--text-dim);
|
|
636
|
+
white-space: nowrap;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.steps-row input { flex: 1; }
|
|
640
|
+
|
|
641
|
+
.steps-row .steps-val {
|
|
642
|
+
font-size: 12px;
|
|
643
|
+
color: var(--green);
|
|
644
|
+
font-weight: 500;
|
|
645
|
+
min-width: 20px;
|
|
646
|
+
text-align: right;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
</style>
|
|
650
|
+
</head>
|
|
651
|
+
<body>
|
|
652
|
+
|
|
653
|
+
<canvas id="matrix-canvas"></canvas>
|
|
654
|
+
|
|
655
|
+
<div class="app">
|
|
656
|
+
<div class="header">
|
|
657
|
+
<h1>NeuroVerse Simulation Engine</h1>
|
|
658
|
+
<div class="subtitle">Deterministic World Governance</div>
|
|
659
|
+
<div class="powered">neuroverseos.com</div>
|
|
660
|
+
</div>
|
|
661
|
+
|
|
662
|
+
<div class="grid">
|
|
663
|
+
<!-- LEFT: World + State -->
|
|
664
|
+
<div class="panel">
|
|
665
|
+
<div class="panel-header"><span class="dot"></span> World</div>
|
|
666
|
+
<div class="panel-body">
|
|
667
|
+
<div class="world-tabs" id="world-tabs"></div>
|
|
668
|
+
<button class="custom-toggle" id="custom-toggle">+ paste custom .nv-world.md</button>
|
|
669
|
+
<textarea class="custom-textarea" id="custom-textarea" placeholder="Paste your .nv-world.md here..."></textarea>
|
|
670
|
+
|
|
671
|
+
<div class="section-divider">Profile</div>
|
|
672
|
+
<select class="profile-select" id="profile-select"></select>
|
|
673
|
+
|
|
674
|
+
<div class="state-section" id="state-section">
|
|
675
|
+
<div class="section-divider">State Overrides</div>
|
|
676
|
+
<div id="state-sliders"></div>
|
|
677
|
+
</div>
|
|
678
|
+
|
|
679
|
+
<div class="steps-row">
|
|
680
|
+
<label>Steps</label>
|
|
681
|
+
<input type="range" id="steps-slider" min="1" max="20" value="5">
|
|
682
|
+
<span class="steps-val" id="steps-val">5</span>
|
|
683
|
+
</div>
|
|
684
|
+
|
|
685
|
+
<button class="simulate-btn" id="simulate-btn">Simulate</button>
|
|
686
|
+
</div>
|
|
687
|
+
</div>
|
|
688
|
+
|
|
689
|
+
<!-- CENTER: Simulation -->
|
|
690
|
+
<div class="panel">
|
|
691
|
+
<div class="panel-header"><span class="dot"></span> Simulation</div>
|
|
692
|
+
<div class="panel-body sim-output" id="sim-output">
|
|
693
|
+
<div class="sim-placeholder">
|
|
694
|
+
<div>Select a world and configure state</div>
|
|
695
|
+
<div>then press <span style="color:var(--green)">SIMULATE</span></div>
|
|
696
|
+
<span class="cursor"></span>
|
|
697
|
+
</div>
|
|
698
|
+
</div>
|
|
699
|
+
</div>
|
|
700
|
+
|
|
701
|
+
<!-- RIGHT: Verdict -->
|
|
702
|
+
<div class="panel">
|
|
703
|
+
<div class="panel-header"><span class="dot"></span> Verdict</div>
|
|
704
|
+
<div class="panel-body verdict-section" id="verdict-section">
|
|
705
|
+
<div class="viability-display">
|
|
706
|
+
<div class="viability-ring">
|
|
707
|
+
<svg viewBox="0 0 160 160">
|
|
708
|
+
<circle class="bg-ring" cx="80" cy="80" r="70"/>
|
|
709
|
+
<circle class="fg-ring" id="viability-ring" cx="80" cy="80" r="70"/>
|
|
710
|
+
</svg>
|
|
711
|
+
<div class="value-text" id="viability-value">--</div>
|
|
712
|
+
</div>
|
|
713
|
+
<div class="gate-badge" id="gate-badge">AWAITING</div>
|
|
714
|
+
</div>
|
|
715
|
+
|
|
716
|
+
<div class="stats-grid" id="stats-grid"></div>
|
|
717
|
+
|
|
718
|
+
<div class="collapse-banner" id="collapse-banner">
|
|
719
|
+
<div class="collapse-title">Model Collapsed</div>
|
|
720
|
+
<div class="collapse-detail" id="collapse-detail"></div>
|
|
721
|
+
</div>
|
|
722
|
+
|
|
723
|
+
<ul class="invariant-list" id="invariant-list"></ul>
|
|
724
|
+
|
|
725
|
+
</div>
|
|
726
|
+
</div>
|
|
727
|
+
</div>
|
|
728
|
+
</div>
|
|
729
|
+
|
|
730
|
+
<script>
|
|
731
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
732
|
+
// MATRIX RAIN
|
|
733
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
734
|
+
|
|
735
|
+
const canvas = document.getElementById('matrix-canvas');
|
|
736
|
+
const ctx = canvas.getContext('2d');
|
|
737
|
+
|
|
738
|
+
function resizeCanvas() {
|
|
739
|
+
canvas.width = window.innerWidth;
|
|
740
|
+
canvas.height = window.innerHeight;
|
|
741
|
+
}
|
|
742
|
+
resizeCanvas();
|
|
743
|
+
window.addEventListener('resize', resizeCanvas);
|
|
744
|
+
|
|
745
|
+
const chars = 'NeuroVerse01アイウエオカキクケコサシスセソ>|/\\=+-*';
|
|
746
|
+
const fontSize = 14;
|
|
747
|
+
let columns = Math.floor(canvas.width / fontSize);
|
|
748
|
+
let drops = Array(columns).fill(1);
|
|
749
|
+
|
|
750
|
+
function drawMatrix() {
|
|
751
|
+
ctx.fillStyle = 'rgba(10, 10, 10, 0.05)';
|
|
752
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
753
|
+
ctx.fillStyle = '#00ff41';
|
|
754
|
+
ctx.font = fontSize + 'px monospace';
|
|
755
|
+
|
|
756
|
+
for (let i = 0; i < drops.length; i++) {
|
|
757
|
+
const text = chars[Math.floor(Math.random() * chars.length)];
|
|
758
|
+
ctx.fillText(text, i * fontSize, drops[i] * fontSize);
|
|
759
|
+
if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {
|
|
760
|
+
drops[i] = 0;
|
|
761
|
+
}
|
|
762
|
+
drops[i]++;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
setInterval(drawMatrix, 50);
|
|
767
|
+
|
|
768
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
769
|
+
// WORLD PARSER (Simplified .nv-world.md → WorldDefinition)
|
|
770
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
771
|
+
|
|
772
|
+
function parseWorldMarkdown(md) {
|
|
773
|
+
const world = {
|
|
774
|
+
world: { world_id: '', name: '', thesis: '', version: '1.0.0', runtime_mode: 'SIMULATION',
|
|
775
|
+
default_assumption_profile: '', default_alternative_profile: '', modules: [],
|
|
776
|
+
players: { thinking_space: true, experience_space: true, action_space: true } },
|
|
777
|
+
invariants: [],
|
|
778
|
+
assumptions: { profiles: {}, parameter_definitions: {} },
|
|
779
|
+
stateSchema: { variables: {}, presets: {} },
|
|
780
|
+
rules: [],
|
|
781
|
+
gates: { viability_classification: [], structural_override: { description: 'Structural violations override all gates', enforcement: 'mandatory' },
|
|
782
|
+
sustainability_threshold: 50, collapse_visual: { background: '#1a0000', text: '#ff4444', border: '#ff0000', label: 'COLLAPSED' } },
|
|
783
|
+
outcomes: { computed_outcomes: [], comparison_layout: { primary_card: '', status_badge: '', structural_indicators: [] } },
|
|
784
|
+
metadata: { format_version: '1.0.0', created_at: new Date().toISOString(), last_modified: new Date().toISOString(), authoring_method: 'manual-authoring' }
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
// Parse frontmatter
|
|
788
|
+
const fmMatch = md.match(/^---\n([\s\S]*?)\n---/);
|
|
789
|
+
if (fmMatch) {
|
|
790
|
+
const fm = fmMatch[1];
|
|
791
|
+
const getVal = (key) => { const m = fm.match(new RegExp(key + ':\\s*(.+)')); return m ? m[1].trim() : ''; };
|
|
792
|
+
world.world.world_id = getVal('world_id');
|
|
793
|
+
world.world.name = getVal('name');
|
|
794
|
+
world.world.version = getVal('version') || '1.0.0';
|
|
795
|
+
world.world.runtime_mode = getVal('runtime_mode') || 'SIMULATION';
|
|
796
|
+
world.world.default_assumption_profile = getVal('default_profile');
|
|
797
|
+
world.world.default_alternative_profile = getVal('alternative_profile');
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Split sections
|
|
801
|
+
const sections = {};
|
|
802
|
+
let currentSection = null;
|
|
803
|
+
for (const line of md.split('\n')) {
|
|
804
|
+
const h1 = line.match(/^# (.+)/);
|
|
805
|
+
if (h1) { currentSection = h1[1].trim().toLowerCase(); sections[currentSection] = ''; continue; }
|
|
806
|
+
if (currentSection) sections[currentSection] += line + '\n';
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// Thesis
|
|
810
|
+
world.world.thesis = (sections['thesis'] || '').trim();
|
|
811
|
+
|
|
812
|
+
// Invariants
|
|
813
|
+
if (sections['invariants']) {
|
|
814
|
+
for (const line of sections['invariants'].split('\n')) {
|
|
815
|
+
const m = line.match(/^- `([^`]+)` — (.+?) \((\w+), (\w+)\)/);
|
|
816
|
+
if (m) world.invariants.push({ id: m[1], label: m[2], enforcement: m[3], mutable: false });
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// State
|
|
821
|
+
if (sections['state']) {
|
|
822
|
+
let varId = null;
|
|
823
|
+
const vars = {};
|
|
824
|
+
for (const line of sections['state'].split('\n')) {
|
|
825
|
+
const h2 = line.match(/^## (\w+)/);
|
|
826
|
+
if (h2) { varId = h2[1]; vars[varId] = { type: 'number', default: 0, mutable: true, label: varId, description: '' }; continue; }
|
|
827
|
+
if (!varId) continue;
|
|
828
|
+
const kv = line.match(/^- (\w+):\s*(.+)/);
|
|
829
|
+
if (kv) {
|
|
830
|
+
const [, k, v] = kv;
|
|
831
|
+
if (k === 'type') vars[varId].type = v.trim();
|
|
832
|
+
else if (k === 'min') vars[varId].min = parseFloat(v);
|
|
833
|
+
else if (k === 'max') vars[varId].max = parseFloat(v);
|
|
834
|
+
else if (k === 'step') vars[varId].step = parseFloat(v);
|
|
835
|
+
else if (k === 'default') vars[varId].default = isNaN(+v) ? v.trim() : parseFloat(v);
|
|
836
|
+
else if (k === 'label') vars[varId].label = v.trim();
|
|
837
|
+
else if (k === 'description') vars[varId].description = v.trim();
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
world.stateSchema.variables = vars;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// Assumptions
|
|
844
|
+
if (sections['assumptions']) {
|
|
845
|
+
let profId = null;
|
|
846
|
+
for (const line of sections['assumptions'].split('\n')) {
|
|
847
|
+
const h2 = line.match(/^## (\w+)/);
|
|
848
|
+
if (h2) { profId = h2[1]; world.assumptions.profiles[profId] = { name: '', description: '', parameters: {} }; continue; }
|
|
849
|
+
if (!profId) continue;
|
|
850
|
+
const kv = line.match(/^- (\w+):\s*(.+)/);
|
|
851
|
+
if (kv) {
|
|
852
|
+
const [, k, v] = kv;
|
|
853
|
+
if (k === 'name') world.assumptions.profiles[profId].name = v.trim();
|
|
854
|
+
else if (k === 'description') world.assumptions.profiles[profId].description = v.trim();
|
|
855
|
+
else world.assumptions.profiles[profId].parameters[k] = v.trim();
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
if (world.world.default_assumption_profile) {
|
|
859
|
+
const p = world.assumptions.profiles[world.world.default_assumption_profile];
|
|
860
|
+
if (p) p.is_default_baseline = true;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Rules
|
|
865
|
+
if (sections['rules']) {
|
|
866
|
+
const ruleBlocks = sections['rules'].split(/^## /m).filter(Boolean);
|
|
867
|
+
let order = 0;
|
|
868
|
+
for (const block of ruleBlocks) {
|
|
869
|
+
const headerMatch = block.match(/^(rule-\d+):\s*(.+?)\s*\((\w+)\)/);
|
|
870
|
+
if (!headerMatch) continue;
|
|
871
|
+
const [, ruleId, label, severity] = headerMatch;
|
|
872
|
+
const lines = block.split('\n');
|
|
873
|
+
let description = '';
|
|
874
|
+
const triggers = [];
|
|
875
|
+
const effects = [];
|
|
876
|
+
let collapseCheck = null;
|
|
877
|
+
const causal = { trigger_text: '', rule_text: '', shift_text: '', effect_text: '' };
|
|
878
|
+
|
|
879
|
+
for (const line of lines) {
|
|
880
|
+
// Triggers: When field op value [source] (AND field op value [source])*
|
|
881
|
+
const whenMatch = line.match(/^When (.+)/);
|
|
882
|
+
if (whenMatch) {
|
|
883
|
+
const parts = whenMatch[1].split(/\s+AND\s+/);
|
|
884
|
+
for (const part of parts) {
|
|
885
|
+
const tm = part.match(/(\w+)\s*(==|!=|>=|<=|>|<)\s*([\w.]+)\s*\[(\w+)\]/);
|
|
886
|
+
if (tm) triggers.push({ field: tm[1], operator: tm[2], value: isNaN(+tm[3]) ? tm[3] : parseFloat(tm[3]), source: tm[4] });
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
// Effects: Then target *= value or target += value
|
|
890
|
+
const thenMatch = line.match(/^Then (\w+)\s*(\*=|\+=|-=|=)\s*([\w.]+)/);
|
|
891
|
+
if (thenMatch) {
|
|
892
|
+
const opMap = { '*=': 'multiply', '+=': 'add', '-=': 'subtract', '=': 'set' };
|
|
893
|
+
effects.push({ target: thenMatch[1], operation: opMap[thenMatch[2]] || 'set', value: isNaN(+thenMatch[3]) ? thenMatch[3] : parseFloat(thenMatch[3]) });
|
|
894
|
+
}
|
|
895
|
+
// Collapse
|
|
896
|
+
const collapseMatch = line.match(/^Collapse:\s*(\w+)\s*(==|!=|>=|<=|>|<)\s*([\w.]+)/);
|
|
897
|
+
if (collapseMatch) {
|
|
898
|
+
collapseCheck = { field: collapseMatch[1], operator: collapseMatch[2], value: parseFloat(collapseMatch[3]), result: 'MODEL_COLLAPSES' };
|
|
899
|
+
}
|
|
900
|
+
// Causal
|
|
901
|
+
const causalMatch = line.match(/^> (\w+):\s*(.+)/);
|
|
902
|
+
if (causalMatch) causal[causalMatch[1] + '_text'] = causalMatch[2];
|
|
903
|
+
|
|
904
|
+
// Description (first non-header, non-directive line)
|
|
905
|
+
if (!line.startsWith('When ') && !line.startsWith('Then ') && !line.startsWith('Collapse:') && !line.startsWith('>') && !line.match(/^rule-/) && line.trim() && !description) {
|
|
906
|
+
description = line.trim();
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
world.rules.push({ id: ruleId, severity, label, description, order: order++, triggers, effects, collapse_check: collapseCheck, causal_translation: causal });
|
|
911
|
+
world.world.modules.push(ruleId);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Gates
|
|
916
|
+
if (sections['gates']) {
|
|
917
|
+
const gateColors = { 0: '#00ff41', 1: '#00cc33', 2: '#ffaa00', 3: '#ff6600', 4: '#ff4444' };
|
|
918
|
+
const gateIcons = { 0: '\u2726', 1: '\u25CF', 2: '\u25B2', 3: '\u26A0', 4: '\u2715' };
|
|
919
|
+
let gi = 0;
|
|
920
|
+
for (const line of sections['gates'].split('\n')) {
|
|
921
|
+
const gm = line.match(/^- (\w+):\s*(\w+)\s*(>=|<=|>|<|==)\s*([\d.]+)/);
|
|
922
|
+
if (gm) {
|
|
923
|
+
world.gates.viability_classification.push({
|
|
924
|
+
status: gm[1], field: gm[2], operator: gm[3], value: parseFloat(gm[4]),
|
|
925
|
+
color: gateColors[gi] || '#888', icon: gateIcons[gi] || '?'
|
|
926
|
+
});
|
|
927
|
+
gi++;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Outcomes
|
|
933
|
+
if (sections['outcomes']) {
|
|
934
|
+
let outId = null;
|
|
935
|
+
for (const line of sections['outcomes'].split('\n')) {
|
|
936
|
+
const h2 = line.match(/^## (\w+)/);
|
|
937
|
+
if (h2) {
|
|
938
|
+
outId = h2[1];
|
|
939
|
+
world.outcomes.computed_outcomes.push({ id: outId, type: 'number', label: outId, show_in_comparison: true });
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
if (!outId) continue;
|
|
943
|
+
const out = world.outcomes.computed_outcomes.find(o => o.id === outId);
|
|
944
|
+
if (!out) continue;
|
|
945
|
+
const kv = line.match(/^- (\w+):\s*(.+)/);
|
|
946
|
+
if (kv) {
|
|
947
|
+
if (kv[1] === 'type') out.type = kv[2].trim();
|
|
948
|
+
if (kv[1] === 'range') { const r = kv[2].match(/([\d.-]+)-([\d.-]+)/); if (r) out.range = [parseFloat(r[1]), parseFloat(r[2])]; }
|
|
949
|
+
if (kv[1] === 'display') out.display_as = kv[2].trim();
|
|
950
|
+
if (kv[1] === 'label') out.label = kv[2].trim();
|
|
951
|
+
if (kv[1] === 'primary' && kv[2].trim() === 'true') { out.primary = true; world.outcomes.comparison_layout.primary_card = outId; }
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
return world;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
960
|
+
// SIMULATE ENGINE (Pure deterministic simulation)
|
|
961
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
962
|
+
|
|
963
|
+
function evaluateOperator(fieldValue, operator, condValue) {
|
|
964
|
+
switch (operator) {
|
|
965
|
+
case '==': return fieldValue == condValue;
|
|
966
|
+
case '!=': return fieldValue != condValue;
|
|
967
|
+
case '>': return typeof fieldValue === 'number' && typeof condValue === 'number' && fieldValue > condValue;
|
|
968
|
+
case '<': return typeof fieldValue === 'number' && typeof condValue === 'number' && fieldValue < condValue;
|
|
969
|
+
case '>=': return typeof fieldValue === 'number' && typeof condValue === 'number' && fieldValue >= condValue;
|
|
970
|
+
case '<=': return typeof fieldValue === 'number' && typeof condValue === 'number' && fieldValue <= condValue;
|
|
971
|
+
default: return false;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
function evaluateTriggers(triggers, state, assumptions) {
|
|
976
|
+
return triggers.every(t => {
|
|
977
|
+
const source = t.source === 'assumption' ? assumptions : state;
|
|
978
|
+
const val = source[t.field];
|
|
979
|
+
if (val === undefined) return false;
|
|
980
|
+
return evaluateOperator(val, t.operator, t.value);
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
function applyEffect(state, effect) {
|
|
985
|
+
const before = state[effect.target];
|
|
986
|
+
let after = before;
|
|
987
|
+
const val = typeof effect.value === 'number' ? effect.value : parseFloat(effect.value);
|
|
988
|
+
|
|
989
|
+
switch (effect.operation) {
|
|
990
|
+
case 'multiply': after = typeof before === 'number' ? before * val : before; break;
|
|
991
|
+
case 'add': after = typeof before === 'number' ? before + val : before; break;
|
|
992
|
+
case 'subtract': after = typeof before === 'number' ? before - val : before; break;
|
|
993
|
+
case 'set': after = val; break;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
state[effect.target] = typeof after === 'number' ? Math.round(after * 100) / 100 : after;
|
|
997
|
+
return { target: effect.target, operation: effect.operation, value: effect.value, before, after: state[effect.target] };
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
function classifyViability(state, world) {
|
|
1001
|
+
const gates = world.gates?.viability_classification ?? [];
|
|
1002
|
+
for (const gate of gates) {
|
|
1003
|
+
const val = state[gate.field];
|
|
1004
|
+
if (typeof val !== 'number') continue;
|
|
1005
|
+
if (evaluateOperator(val, gate.operator, gate.value)) return gate.status;
|
|
1006
|
+
}
|
|
1007
|
+
return 'MODEL_COLLAPSES';
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function simulateWorld(world, options = {}) {
|
|
1011
|
+
const steps = Math.min(50, Math.max(1, options.steps || 1));
|
|
1012
|
+
const profile = options.profile || world.world.default_assumption_profile;
|
|
1013
|
+
|
|
1014
|
+
// Build initial state from schema defaults
|
|
1015
|
+
const state = {};
|
|
1016
|
+
for (const [k, v] of Object.entries(world.stateSchema.variables)) {
|
|
1017
|
+
state[k] = v.default;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// Initialize outcome fields (primary outcome starts at 100)
|
|
1021
|
+
for (const out of world.outcomes.computed_outcomes) {
|
|
1022
|
+
if (out.primary && !(out.id in state)) state[out.id] = 100;
|
|
1023
|
+
else if (!(out.id in state)) state[out.id] = out.default || 0;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// Apply state overrides
|
|
1027
|
+
if (options.stateOverrides) {
|
|
1028
|
+
for (const [k, v] of Object.entries(options.stateOverrides)) {
|
|
1029
|
+
state[k] = v;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// Get assumption parameters
|
|
1034
|
+
const assumptions = world.assumptions?.profiles?.[profile]?.parameters || {};
|
|
1035
|
+
|
|
1036
|
+
const initialState = { ...state };
|
|
1037
|
+
const simSteps = [];
|
|
1038
|
+
let collapsed = false;
|
|
1039
|
+
let collapseStep, collapseRule;
|
|
1040
|
+
|
|
1041
|
+
for (let s = 0; s < steps; s++) {
|
|
1042
|
+
const ruleEvals = [];
|
|
1043
|
+
let firedThisStep = [];
|
|
1044
|
+
let stepCollapsed = false;
|
|
1045
|
+
|
|
1046
|
+
for (const rule of world.rules) {
|
|
1047
|
+
if (collapsed) {
|
|
1048
|
+
ruleEvals.push({ ruleId: rule.id, label: rule.label, triggered: false, excluded: true, effects: [], collapsed: false, severity: rule.severity });
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// Check exclusive_with
|
|
1053
|
+
if (rule.exclusive_with && firedThisStep.includes(rule.exclusive_with)) {
|
|
1054
|
+
ruleEvals.push({ ruleId: rule.id, label: rule.label, triggered: false, excluded: true, effects: [], collapsed: false, severity: rule.severity });
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const triggered = evaluateTriggers(rule.triggers, state, assumptions);
|
|
1059
|
+
const appliedEffects = [];
|
|
1060
|
+
|
|
1061
|
+
if (triggered) {
|
|
1062
|
+
firedThisStep.push(rule.id);
|
|
1063
|
+
for (const eff of (rule.effects || [])) {
|
|
1064
|
+
appliedEffects.push(applyEffect(state, eff));
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// Check collapse
|
|
1068
|
+
if (rule.collapse_check) {
|
|
1069
|
+
const cv = state[rule.collapse_check.field];
|
|
1070
|
+
if (typeof cv === 'number' && evaluateOperator(cv, rule.collapse_check.operator, rule.collapse_check.value)) {
|
|
1071
|
+
collapsed = true;
|
|
1072
|
+
stepCollapsed = true;
|
|
1073
|
+
collapseStep = s + 1;
|
|
1074
|
+
collapseRule = rule.id;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
ruleEvals.push({ ruleId: rule.id, label: rule.label, triggered, excluded: false, effects: appliedEffects, collapsed: stepCollapsed && triggered, severity: rule.severity });
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
simSteps.push({
|
|
1083
|
+
step: s + 1,
|
|
1084
|
+
rulesEvaluated: ruleEvals,
|
|
1085
|
+
rulesFired: ruleEvals.filter(r => r.triggered).length,
|
|
1086
|
+
stateAfter: { ...state },
|
|
1087
|
+
viability: classifyViability(state, world),
|
|
1088
|
+
collapsed: stepCollapsed
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
if (collapsed) break;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
return {
|
|
1095
|
+
worldId: world.world.world_id,
|
|
1096
|
+
worldName: world.world.name,
|
|
1097
|
+
profile,
|
|
1098
|
+
initialState,
|
|
1099
|
+
steps: simSteps,
|
|
1100
|
+
finalState: { ...state },
|
|
1101
|
+
finalViability: classifyViability(state, world),
|
|
1102
|
+
collapsed,
|
|
1103
|
+
collapseStep,
|
|
1104
|
+
collapseRule
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1109
|
+
// PRE-BUNDLED WORLDS
|
|
1110
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1111
|
+
|
|
1112
|
+
const BUNDLED_WORLDS = {
|
|
1113
|
+
'coding-agent': {
|
|
1114
|
+
name: 'Coding Agent',
|
|
1115
|
+
desc: 'File writes, shell commands, git',
|
|
1116
|
+
md: `---
|
|
1117
|
+
world_id: coding-agent
|
|
1118
|
+
name: Coding Agent Governance
|
|
1119
|
+
version: 1.0.0
|
|
1120
|
+
runtime_mode: COMPLIANCE
|
|
1121
|
+
default_profile: standard
|
|
1122
|
+
alternative_profile: strict
|
|
1123
|
+
---
|
|
1124
|
+
|
|
1125
|
+
# Thesis
|
|
1126
|
+
|
|
1127
|
+
Autonomous coding agents that can read files, write code, execute shell commands, and interact with version control must operate within explicit governance boundaries.
|
|
1128
|
+
|
|
1129
|
+
# Invariants
|
|
1130
|
+
|
|
1131
|
+
- \`no_destructive_shell_commands\` — Shell commands must not perform irreversible destructive operations (structural, immutable)
|
|
1132
|
+
- \`no_secret_exfiltration\` — Agent must never read or transmit secrets or API keys (structural, immutable)
|
|
1133
|
+
- \`no_system_file_modification\` — Agent must not modify files outside the project repository (structural, immutable)
|
|
1134
|
+
- \`no_direct_production_push\` — Code must never be pushed directly to main without approval (structural, immutable)
|
|
1135
|
+
- \`repository_boundary_enforced\` — All file operations scoped to project directory (structural, immutable)
|
|
1136
|
+
|
|
1137
|
+
# State
|
|
1138
|
+
|
|
1139
|
+
## files_modified
|
|
1140
|
+
- type: number
|
|
1141
|
+
- min: 0
|
|
1142
|
+
- max: 200
|
|
1143
|
+
- step: 1
|
|
1144
|
+
- default: 0
|
|
1145
|
+
- label: Files Modified
|
|
1146
|
+
- description: Total files modified in session
|
|
1147
|
+
|
|
1148
|
+
## shell_commands_run
|
|
1149
|
+
- type: number
|
|
1150
|
+
- min: 0
|
|
1151
|
+
- max: 500
|
|
1152
|
+
- step: 1
|
|
1153
|
+
- default: 0
|
|
1154
|
+
- label: Shell Commands Run
|
|
1155
|
+
- description: Total shell commands executed
|
|
1156
|
+
|
|
1157
|
+
## destructive_attempts
|
|
1158
|
+
- type: number
|
|
1159
|
+
- min: 0
|
|
1160
|
+
- max: 20
|
|
1161
|
+
- step: 1
|
|
1162
|
+
- default: 0
|
|
1163
|
+
- label: Destructive Attempts
|
|
1164
|
+
- description: Blocked destructive actions
|
|
1165
|
+
|
|
1166
|
+
## tests_passing
|
|
1167
|
+
- type: number
|
|
1168
|
+
- min: 0
|
|
1169
|
+
- max: 100
|
|
1170
|
+
- step: 1
|
|
1171
|
+
- default: 100
|
|
1172
|
+
- label: Tests Passing
|
|
1173
|
+
- description: Percentage of tests passing
|
|
1174
|
+
|
|
1175
|
+
## files_outside_repo
|
|
1176
|
+
- type: number
|
|
1177
|
+
- min: 0
|
|
1178
|
+
- max: 20
|
|
1179
|
+
- step: 1
|
|
1180
|
+
- default: 0
|
|
1181
|
+
- label: Out-of-Scope Access
|
|
1182
|
+
- description: File ops outside repository
|
|
1183
|
+
|
|
1184
|
+
## secrets_detected
|
|
1185
|
+
- type: number
|
|
1186
|
+
- min: 0
|
|
1187
|
+
- max: 20
|
|
1188
|
+
- step: 1
|
|
1189
|
+
- default: 0
|
|
1190
|
+
- label: Secrets Detected
|
|
1191
|
+
- description: Secrets found in agent output
|
|
1192
|
+
|
|
1193
|
+
# Assumptions
|
|
1194
|
+
|
|
1195
|
+
## standard
|
|
1196
|
+
- name: Standard Development
|
|
1197
|
+
- description: Normal dev workflow. Safe commands allowed.
|
|
1198
|
+
- shell_access: safe_commands
|
|
1199
|
+
- file_access: project_only
|
|
1200
|
+
|
|
1201
|
+
## strict
|
|
1202
|
+
- name: Strict Governance
|
|
1203
|
+
- description: All writes require approval. Shell limited to tests.
|
|
1204
|
+
- shell_access: test_only
|
|
1205
|
+
- file_access: read_mostly
|
|
1206
|
+
|
|
1207
|
+
# Rules
|
|
1208
|
+
|
|
1209
|
+
## rule-001: Destructive Shell Command (structural)
|
|
1210
|
+
Destructive shell commands must be blocked immediately.
|
|
1211
|
+
|
|
1212
|
+
When destructive_attempts > 0 [state]
|
|
1213
|
+
Then agent_trust *= 0.00
|
|
1214
|
+
Collapse: agent_trust < 0.05
|
|
1215
|
+
|
|
1216
|
+
> trigger: Agent attempted a destructive shell command.
|
|
1217
|
+
> rule: Destructive commands are never acceptable.
|
|
1218
|
+
> shift: Agent trust drops to zero. Session terminated.
|
|
1219
|
+
> effect: Agent trust eliminated.
|
|
1220
|
+
|
|
1221
|
+
## rule-002: Secret Exfiltration (structural)
|
|
1222
|
+
Secret access must be blocked and flagged.
|
|
1223
|
+
|
|
1224
|
+
When secrets_detected > 0 [state]
|
|
1225
|
+
Then agent_trust *= 0.10
|
|
1226
|
+
Collapse: agent_trust < 0.05
|
|
1227
|
+
|
|
1228
|
+
> trigger: Agent accessed credentials or secrets.
|
|
1229
|
+
> rule: Secrets require explicit authorization.
|
|
1230
|
+
> shift: Agent trust drops severely.
|
|
1231
|
+
> effect: Agent trust reduced to 10%.
|
|
1232
|
+
|
|
1233
|
+
## rule-003: Boundary Violation (structural)
|
|
1234
|
+
File operations outside the repository are forbidden.
|
|
1235
|
+
|
|
1236
|
+
When files_outside_repo > 0 [state]
|
|
1237
|
+
Then agent_trust *= 0.20
|
|
1238
|
+
Collapse: agent_trust < 0.05
|
|
1239
|
+
|
|
1240
|
+
> trigger: Agent accessed files outside project directory.
|
|
1241
|
+
> rule: Agents operate within repository scope.
|
|
1242
|
+
> shift: Agent trust degrades significantly.
|
|
1243
|
+
> effect: Agent trust reduced to 20%.
|
|
1244
|
+
|
|
1245
|
+
## rule-004: Test Regression (degradation)
|
|
1246
|
+
Test failures indicate the agent is creating regressions.
|
|
1247
|
+
|
|
1248
|
+
When tests_passing < 70 [state] AND files_modified > 0 [state]
|
|
1249
|
+
Then agent_trust *= 0.50
|
|
1250
|
+
|
|
1251
|
+
> trigger: Tests dropped below 70%.
|
|
1252
|
+
> rule: Agents that break tests create liability.
|
|
1253
|
+
> shift: Agent must fix tests before continuing.
|
|
1254
|
+
> effect: Agent trust reduced to 50%.
|
|
1255
|
+
|
|
1256
|
+
## rule-005: Excessive Modifications (degradation)
|
|
1257
|
+
Too many modifications indicate scope creep.
|
|
1258
|
+
|
|
1259
|
+
When files_modified > 50 [state]
|
|
1260
|
+
Then agent_trust *= 0.70
|
|
1261
|
+
|
|
1262
|
+
> trigger: More than 50 files modified.
|
|
1263
|
+
> rule: Large changes are risky and hard to review.
|
|
1264
|
+
> shift: Changes should be reviewed.
|
|
1265
|
+
> effect: Agent trust reduced to 70%.
|
|
1266
|
+
|
|
1267
|
+
## rule-006: Clean Session (advantage)
|
|
1268
|
+
No destructive attempts and passing tests.
|
|
1269
|
+
|
|
1270
|
+
When destructive_attempts == 0 [state] AND tests_passing > 90 [state] AND files_modified > 0 [state]
|
|
1271
|
+
Then agent_trust *= 1.20
|
|
1272
|
+
|
|
1273
|
+
> trigger: Zero violations with passing tests.
|
|
1274
|
+
> rule: Trustworthy agents are rewarded.
|
|
1275
|
+
> shift: Session is healthy.
|
|
1276
|
+
> effect: Agent trust boosted 20%.
|
|
1277
|
+
|
|
1278
|
+
## rule-007: Productive Session (advantage)
|
|
1279
|
+
Focused changes with good test coverage.
|
|
1280
|
+
|
|
1281
|
+
When files_modified > 0 [state] AND files_modified < 30 [state] AND tests_passing > 85 [state]
|
|
1282
|
+
Then agent_trust *= 1.10
|
|
1283
|
+
|
|
1284
|
+
> trigger: Focused changes with quality.
|
|
1285
|
+
> rule: Ideal workflow.
|
|
1286
|
+
> shift: Sustainable pace.
|
|
1287
|
+
> effect: Agent trust boosted 10%.
|
|
1288
|
+
|
|
1289
|
+
# Gates
|
|
1290
|
+
|
|
1291
|
+
- TRUSTED: agent_trust >= 90
|
|
1292
|
+
- PRODUCTIVE: agent_trust >= 60
|
|
1293
|
+
- CAUTIOUS: agent_trust >= 35
|
|
1294
|
+
- RESTRICTED: agent_trust > 10
|
|
1295
|
+
- TERMINATED: agent_trust <= 10
|
|
1296
|
+
|
|
1297
|
+
# Outcomes
|
|
1298
|
+
|
|
1299
|
+
## agent_trust
|
|
1300
|
+
- type: number
|
|
1301
|
+
- range: 0-100
|
|
1302
|
+
- display: percentage
|
|
1303
|
+
- label: Agent Trust
|
|
1304
|
+
- primary: true
|
|
1305
|
+
`
|
|
1306
|
+
},
|
|
1307
|
+
'research-agent': {
|
|
1308
|
+
name: 'Research Agent',
|
|
1309
|
+
desc: 'Citations, API budgets, sources',
|
|
1310
|
+
md: `---
|
|
1311
|
+
world_id: research-agent
|
|
1312
|
+
name: Research Agent Governance
|
|
1313
|
+
version: 1.0.0
|
|
1314
|
+
runtime_mode: COMPLIANCE
|
|
1315
|
+
default_profile: conservative
|
|
1316
|
+
alternative_profile: exploratory
|
|
1317
|
+
---
|
|
1318
|
+
|
|
1319
|
+
# Thesis
|
|
1320
|
+
|
|
1321
|
+
AI research agents must operate within governance boundaries ensuring rigor, attribution, and responsible resource usage.
|
|
1322
|
+
|
|
1323
|
+
# Invariants
|
|
1324
|
+
|
|
1325
|
+
- \`sources_must_be_cited\` — Every claim must be traceable to a source (structural, immutable)
|
|
1326
|
+
- \`no_fabricated_citations\` — Agent must never invent sources (structural, immutable)
|
|
1327
|
+
- \`api_rate_limits_respected\` — Must respect rate limits on external APIs (structural, immutable)
|
|
1328
|
+
- \`no_unauthorized_publication\` — Findings require human review before publishing (prompt, immutable)
|
|
1329
|
+
|
|
1330
|
+
# State
|
|
1331
|
+
|
|
1332
|
+
## sources_consulted
|
|
1333
|
+
- type: number
|
|
1334
|
+
- min: 0
|
|
1335
|
+
- max: 100
|
|
1336
|
+
- step: 1
|
|
1337
|
+
- default: 0
|
|
1338
|
+
- label: Sources Consulted
|
|
1339
|
+
- description: Unique sources accessed
|
|
1340
|
+
|
|
1341
|
+
## claims_made
|
|
1342
|
+
- type: number
|
|
1343
|
+
- min: 0
|
|
1344
|
+
- max: 100
|
|
1345
|
+
- step: 1
|
|
1346
|
+
- default: 0
|
|
1347
|
+
- label: Claims Made
|
|
1348
|
+
- description: Assertions produced
|
|
1349
|
+
|
|
1350
|
+
## unsourced_claims
|
|
1351
|
+
- type: number
|
|
1352
|
+
- min: 0
|
|
1353
|
+
- max: 50
|
|
1354
|
+
- step: 1
|
|
1355
|
+
- default: 0
|
|
1356
|
+
- label: Unsourced Claims
|
|
1357
|
+
- description: Claims without source attribution
|
|
1358
|
+
|
|
1359
|
+
## api_calls_made
|
|
1360
|
+
- type: number
|
|
1361
|
+
- min: 0
|
|
1362
|
+
- max: 10000
|
|
1363
|
+
- step: 10
|
|
1364
|
+
- default: 0
|
|
1365
|
+
- label: API Calls Made
|
|
1366
|
+
- description: External API calls made
|
|
1367
|
+
|
|
1368
|
+
## api_budget
|
|
1369
|
+
- type: number
|
|
1370
|
+
- min: 100
|
|
1371
|
+
- max: 10000
|
|
1372
|
+
- step: 100
|
|
1373
|
+
- default: 5000
|
|
1374
|
+
- label: API Budget
|
|
1375
|
+
- description: Maximum API calls allowed
|
|
1376
|
+
|
|
1377
|
+
# Assumptions
|
|
1378
|
+
|
|
1379
|
+
## conservative
|
|
1380
|
+
- name: Conservative Research
|
|
1381
|
+
- description: Multiple sources per claim. Strict API limits.
|
|
1382
|
+
- source_requirement: multiple_per_claim
|
|
1383
|
+
- api_strictness: hard_limit
|
|
1384
|
+
|
|
1385
|
+
## exploratory
|
|
1386
|
+
- name: Exploratory Research
|
|
1387
|
+
- description: Broader exploration. Softer limits.
|
|
1388
|
+
- source_requirement: at_least_one
|
|
1389
|
+
- api_strictness: soft_warning
|
|
1390
|
+
|
|
1391
|
+
# Rules
|
|
1392
|
+
|
|
1393
|
+
## rule-001: API Budget Exhausted (structural)
|
|
1394
|
+
No further external calls when budget exceeded.
|
|
1395
|
+
|
|
1396
|
+
When api_calls_made > api_budget [state]
|
|
1397
|
+
Then research_viability *= 0.00
|
|
1398
|
+
Collapse: research_viability < 0.05
|
|
1399
|
+
|
|
1400
|
+
> trigger: API budget exceeded.
|
|
1401
|
+
> rule: Budgets prevent abuse and cost overruns.
|
|
1402
|
+
> shift: Research halts for external calls.
|
|
1403
|
+
> effect: Viability set to zero.
|
|
1404
|
+
|
|
1405
|
+
## rule-002: Unsourced Claims (degradation)
|
|
1406
|
+
Too many unsourced claims lack rigor.
|
|
1407
|
+
|
|
1408
|
+
When unsourced_claims > 3 [state] AND claims_made > 0 [state]
|
|
1409
|
+
Then research_viability *= 0.40
|
|
1410
|
+
|
|
1411
|
+
> trigger: More than 3 unsourced claims.
|
|
1412
|
+
> rule: Every assertion must be traceable.
|
|
1413
|
+
> shift: Viability drops. Agent must add citations.
|
|
1414
|
+
> effect: Viability reduced to 40%.
|
|
1415
|
+
|
|
1416
|
+
## rule-003: Source Diversity (advantage)
|
|
1417
|
+
Many diverse sources produce quality research.
|
|
1418
|
+
|
|
1419
|
+
When sources_consulted > 10 [state] AND unsourced_claims == 0 [state]
|
|
1420
|
+
Then research_viability *= 1.25
|
|
1421
|
+
|
|
1422
|
+
> trigger: 10+ sources with zero unsourced claims.
|
|
1423
|
+
> rule: Diverse, cited research is gold standard.
|
|
1424
|
+
> shift: Viability improves.
|
|
1425
|
+
> effect: Viability boosted 25%.
|
|
1426
|
+
|
|
1427
|
+
## rule-004: Shallow Research (degradation)
|
|
1428
|
+
Many claims from few sources.
|
|
1429
|
+
|
|
1430
|
+
When claims_made > 10 [state] AND sources_consulted < 3 [state]
|
|
1431
|
+
Then research_viability *= 0.50
|
|
1432
|
+
|
|
1433
|
+
> trigger: 10+ claims from fewer than 3 sources.
|
|
1434
|
+
> rule: Good research requires multiple perspectives.
|
|
1435
|
+
> shift: Agent should broaden sources.
|
|
1436
|
+
> effect: Viability reduced to 50%.
|
|
1437
|
+
|
|
1438
|
+
# Gates
|
|
1439
|
+
|
|
1440
|
+
- RIGOROUS: research_viability >= 85
|
|
1441
|
+
- SOLID: research_viability >= 60
|
|
1442
|
+
- DEVELOPING: research_viability >= 35
|
|
1443
|
+
- WEAK: research_viability > 10
|
|
1444
|
+
- UNRELIABLE: research_viability <= 10
|
|
1445
|
+
|
|
1446
|
+
# Outcomes
|
|
1447
|
+
|
|
1448
|
+
## research_viability
|
|
1449
|
+
- type: number
|
|
1450
|
+
- range: 0-100
|
|
1451
|
+
- display: percentage
|
|
1452
|
+
- label: Research Viability
|
|
1453
|
+
- primary: true
|
|
1454
|
+
`
|
|
1455
|
+
},
|
|
1456
|
+
'trading-agent': {
|
|
1457
|
+
name: 'Trading Agent',
|
|
1458
|
+
desc: 'Position limits, stop-losses, P&L',
|
|
1459
|
+
md: `---
|
|
1460
|
+
world_id: trading-agent
|
|
1461
|
+
name: Trading Agent Governance
|
|
1462
|
+
version: 1.0.0
|
|
1463
|
+
runtime_mode: COMPLIANCE
|
|
1464
|
+
default_profile: conservative
|
|
1465
|
+
alternative_profile: aggressive
|
|
1466
|
+
---
|
|
1467
|
+
|
|
1468
|
+
# Thesis
|
|
1469
|
+
|
|
1470
|
+
Autonomous trading agents must operate within strict governance including position limits, stop-losses, and daily loss constraints.
|
|
1471
|
+
|
|
1472
|
+
# Invariants
|
|
1473
|
+
|
|
1474
|
+
- \`position_limits_enforced\` — No position may exceed maximum size (structural, immutable)
|
|
1475
|
+
- \`stop_loss_required\` — Every position must have a stop-loss (structural, immutable)
|
|
1476
|
+
- \`daily_loss_limit_enforced\` — Daily losses must not exceed the limit (structural, immutable)
|
|
1477
|
+
- \`audit_trail_maintained\` — Every order must be logged (structural, immutable)
|
|
1478
|
+
|
|
1479
|
+
# State
|
|
1480
|
+
|
|
1481
|
+
## daily_pnl
|
|
1482
|
+
- type: number
|
|
1483
|
+
- min: -20000
|
|
1484
|
+
- max: 20000
|
|
1485
|
+
- step: 100
|
|
1486
|
+
- default: 0
|
|
1487
|
+
- label: Daily P&L
|
|
1488
|
+
- description: Realized + unrealized profit/loss
|
|
1489
|
+
|
|
1490
|
+
## daily_loss_limit
|
|
1491
|
+
- type: number
|
|
1492
|
+
- min: 1000
|
|
1493
|
+
- max: 50000
|
|
1494
|
+
- step: 1000
|
|
1495
|
+
- default: 5000
|
|
1496
|
+
- label: Daily Loss Limit
|
|
1497
|
+
- description: Max allowable daily loss
|
|
1498
|
+
|
|
1499
|
+
## open_positions
|
|
1500
|
+
- type: number
|
|
1501
|
+
- min: 0
|
|
1502
|
+
- max: 50
|
|
1503
|
+
- step: 1
|
|
1504
|
+
- default: 0
|
|
1505
|
+
- label: Open Positions
|
|
1506
|
+
- description: Current open positions
|
|
1507
|
+
|
|
1508
|
+
## max_positions
|
|
1509
|
+
- type: number
|
|
1510
|
+
- min: 1
|
|
1511
|
+
- max: 50
|
|
1512
|
+
- step: 1
|
|
1513
|
+
- default: 10
|
|
1514
|
+
- label: Max Positions
|
|
1515
|
+
- description: Maximum concurrent positions
|
|
1516
|
+
|
|
1517
|
+
## largest_position_pct
|
|
1518
|
+
- type: number
|
|
1519
|
+
- min: 0
|
|
1520
|
+
- max: 100
|
|
1521
|
+
- step: 1
|
|
1522
|
+
- default: 0
|
|
1523
|
+
- label: Largest Position %
|
|
1524
|
+
- description: Portfolio % in largest position
|
|
1525
|
+
|
|
1526
|
+
## positions_without_stop
|
|
1527
|
+
- type: number
|
|
1528
|
+
- min: 0
|
|
1529
|
+
- max: 20
|
|
1530
|
+
- step: 1
|
|
1531
|
+
- default: 0
|
|
1532
|
+
- label: Without Stop-Loss
|
|
1533
|
+
- description: Positions with no stop-loss
|
|
1534
|
+
|
|
1535
|
+
# Assumptions
|
|
1536
|
+
|
|
1537
|
+
## conservative
|
|
1538
|
+
- name: Conservative Trading
|
|
1539
|
+
- description: Small positions. Strict stops. Low loss tolerance.
|
|
1540
|
+
- max_position_pct: 5
|
|
1541
|
+
- daily_loss_tolerance: strict
|
|
1542
|
+
|
|
1543
|
+
## aggressive
|
|
1544
|
+
- name: Aggressive Trading
|
|
1545
|
+
- description: Larger positions. Higher loss tolerance.
|
|
1546
|
+
- max_position_pct: 15
|
|
1547
|
+
- daily_loss_tolerance: moderate
|
|
1548
|
+
|
|
1549
|
+
# Rules
|
|
1550
|
+
|
|
1551
|
+
## rule-001: Loss Limit Breached (structural)
|
|
1552
|
+
Trading must stop when daily loss limit exceeded.
|
|
1553
|
+
|
|
1554
|
+
When daily_pnl < 0 [state] AND daily_loss_limit > 0 [state]
|
|
1555
|
+
Then trading_viability *= 0.00
|
|
1556
|
+
Collapse: trading_viability < 0.05
|
|
1557
|
+
|
|
1558
|
+
> trigger: Daily loss exceeds limit.
|
|
1559
|
+
> rule: Loss limits are absolute constraints.
|
|
1560
|
+
> shift: Trading halts immediately.
|
|
1561
|
+
> effect: Viability set to zero.
|
|
1562
|
+
|
|
1563
|
+
## rule-002: Concentration Risk (degradation)
|
|
1564
|
+
Single position too large.
|
|
1565
|
+
|
|
1566
|
+
When largest_position_pct > 20 [state]
|
|
1567
|
+
Then trading_viability *= 0.50
|
|
1568
|
+
|
|
1569
|
+
> trigger: Position exceeds 20% of portfolio.
|
|
1570
|
+
> rule: Concentration kills portfolios.
|
|
1571
|
+
> shift: Agent should reduce position.
|
|
1572
|
+
> effect: Viability reduced to 50%.
|
|
1573
|
+
|
|
1574
|
+
## rule-003: Naked Positions (structural)
|
|
1575
|
+
Positions without stop-losses are forbidden.
|
|
1576
|
+
|
|
1577
|
+
When positions_without_stop > 0 [state]
|
|
1578
|
+
Then trading_viability *= 0.30
|
|
1579
|
+
Collapse: trading_viability < 0.05
|
|
1580
|
+
|
|
1581
|
+
> trigger: Positions have no stop-loss.
|
|
1582
|
+
> rule: Every position needs a defined exit.
|
|
1583
|
+
> shift: Viability drops severely.
|
|
1584
|
+
> effect: Viability reduced to 30%.
|
|
1585
|
+
|
|
1586
|
+
## rule-004: Over-Trading (degradation)
|
|
1587
|
+
Too many open positions.
|
|
1588
|
+
|
|
1589
|
+
When open_positions > max_positions [state]
|
|
1590
|
+
Then trading_viability *= 0.60
|
|
1591
|
+
|
|
1592
|
+
> trigger: Positions exceed maximum.
|
|
1593
|
+
> rule: Limits prevent fragmentation.
|
|
1594
|
+
> shift: Close positions before opening new ones.
|
|
1595
|
+
> effect: Viability reduced to 60%.
|
|
1596
|
+
|
|
1597
|
+
## rule-005: Disciplined Trading (advantage)
|
|
1598
|
+
Profitable with proper risk controls.
|
|
1599
|
+
|
|
1600
|
+
When daily_pnl > 0 [state] AND positions_without_stop == 0 [state] AND largest_position_pct < 15 [state]
|
|
1601
|
+
Then trading_viability *= 1.20
|
|
1602
|
+
|
|
1603
|
+
> trigger: Positive P&L with risk controls in place.
|
|
1604
|
+
> rule: Disciplined trading is rewarded.
|
|
1605
|
+
> shift: Agent can continue strategy.
|
|
1606
|
+
> effect: Viability boosted 20%.
|
|
1607
|
+
|
|
1608
|
+
## rule-006: Good Diversification (advantage)
|
|
1609
|
+
Small, distributed positions with stops.
|
|
1610
|
+
|
|
1611
|
+
When open_positions > 3 [state] AND largest_position_pct < 10 [state] AND positions_without_stop == 0 [state]
|
|
1612
|
+
Then trading_viability *= 1.15
|
|
1613
|
+
|
|
1614
|
+
> trigger: Multiple small positions, all with stops.
|
|
1615
|
+
> rule: Textbook diversification.
|
|
1616
|
+
> shift: Portfolio well-structured.
|
|
1617
|
+
> effect: Viability boosted 15%.
|
|
1618
|
+
|
|
1619
|
+
# Gates
|
|
1620
|
+
|
|
1621
|
+
- OPTIMAL: trading_viability >= 90
|
|
1622
|
+
- HEALTHY: trading_viability >= 60
|
|
1623
|
+
- CAUTIOUS: trading_viability >= 35
|
|
1624
|
+
- AT_RISK: trading_viability > 10
|
|
1625
|
+
- HALTED: trading_viability <= 10
|
|
1626
|
+
|
|
1627
|
+
# Outcomes
|
|
1628
|
+
|
|
1629
|
+
## trading_viability
|
|
1630
|
+
- type: number
|
|
1631
|
+
- range: 0-100
|
|
1632
|
+
- display: percentage
|
|
1633
|
+
- label: Trading Viability
|
|
1634
|
+
- primary: true
|
|
1635
|
+
`
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
|
|
1639
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1640
|
+
// UI CONTROLLER
|
|
1641
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1642
|
+
|
|
1643
|
+
let currentWorld = null;
|
|
1644
|
+
let currentWorldDef = null;
|
|
1645
|
+
let customMode = false;
|
|
1646
|
+
|
|
1647
|
+
const worldTabsEl = document.getElementById('world-tabs');
|
|
1648
|
+
const customToggleEl = document.getElementById('custom-toggle');
|
|
1649
|
+
const customTextareaEl = document.getElementById('custom-textarea');
|
|
1650
|
+
const profileSelectEl = document.getElementById('profile-select');
|
|
1651
|
+
const stateSlidersEl = document.getElementById('state-sliders');
|
|
1652
|
+
const stepsSliderEl = document.getElementById('steps-slider');
|
|
1653
|
+
const stepsValEl = document.getElementById('steps-val');
|
|
1654
|
+
const simulateBtnEl = document.getElementById('simulate-btn');
|
|
1655
|
+
const simOutputEl = document.getElementById('sim-output');
|
|
1656
|
+
const viabilityRingEl = document.getElementById('viability-ring');
|
|
1657
|
+
const viabilityValueEl = document.getElementById('viability-value');
|
|
1658
|
+
const gateBadgeEl = document.getElementById('gate-badge');
|
|
1659
|
+
const statsGridEl = document.getElementById('stats-grid');
|
|
1660
|
+
const collapseBannerEl = document.getElementById('collapse-banner');
|
|
1661
|
+
const collapseDetailEl = document.getElementById('collapse-detail');
|
|
1662
|
+
const invariantListEl = document.getElementById('invariant-list');
|
|
1663
|
+
|
|
1664
|
+
// Build world tabs
|
|
1665
|
+
for (const [id, w] of Object.entries(BUNDLED_WORLDS)) {
|
|
1666
|
+
const btn = document.createElement('button');
|
|
1667
|
+
btn.className = 'world-tab';
|
|
1668
|
+
btn.dataset.world = id;
|
|
1669
|
+
btn.innerHTML = `<span class="tab-name">${w.name}</span><span class="tab-desc">${w.desc}</span>`;
|
|
1670
|
+
btn.onclick = () => selectWorld(id);
|
|
1671
|
+
worldTabsEl.appendChild(btn);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
customToggleEl.onclick = () => {
|
|
1675
|
+
customMode = !customMode;
|
|
1676
|
+
customTextareaEl.classList.toggle('visible', customMode);
|
|
1677
|
+
if (customMode) {
|
|
1678
|
+
document.querySelectorAll('.world-tab').forEach(t => t.classList.remove('active'));
|
|
1679
|
+
customToggleEl.textContent = '- hide custom editor';
|
|
1680
|
+
} else {
|
|
1681
|
+
customToggleEl.textContent = '+ paste custom .nv-world.md';
|
|
1682
|
+
if (currentWorld) selectWorld(currentWorld);
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1685
|
+
|
|
1686
|
+
customTextareaEl.oninput = () => {
|
|
1687
|
+
if (customMode && customTextareaEl.value.trim()) {
|
|
1688
|
+
try {
|
|
1689
|
+
currentWorldDef = parseWorldMarkdown(customTextareaEl.value);
|
|
1690
|
+
currentWorld = 'custom';
|
|
1691
|
+
buildControls(currentWorldDef);
|
|
1692
|
+
} catch (e) { /* ignore parse errors while typing */ }
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1695
|
+
|
|
1696
|
+
stepsSliderEl.oninput = () => {
|
|
1697
|
+
stepsValEl.textContent = stepsSliderEl.value;
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
function selectWorld(id) {
|
|
1701
|
+
currentWorld = id;
|
|
1702
|
+
customMode = false;
|
|
1703
|
+
customTextareaEl.classList.remove('visible');
|
|
1704
|
+
customToggleEl.textContent = '+ paste custom .nv-world.md';
|
|
1705
|
+
document.querySelectorAll('.world-tab').forEach(t => t.classList.toggle('active', t.dataset.world === id));
|
|
1706
|
+
currentWorldDef = parseWorldMarkdown(BUNDLED_WORLDS[id].md);
|
|
1707
|
+
buildControls(currentWorldDef);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
function buildControls(world) {
|
|
1711
|
+
// Profiles
|
|
1712
|
+
profileSelectEl.innerHTML = '';
|
|
1713
|
+
for (const [pid, prof] of Object.entries(world.assumptions.profiles)) {
|
|
1714
|
+
const opt = document.createElement('option');
|
|
1715
|
+
opt.value = pid;
|
|
1716
|
+
opt.textContent = prof.name || pid;
|
|
1717
|
+
if (pid === world.world.default_assumption_profile) opt.selected = true;
|
|
1718
|
+
profileSelectEl.appendChild(opt);
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// State sliders
|
|
1722
|
+
stateSlidersEl.innerHTML = '';
|
|
1723
|
+
for (const [vid, v] of Object.entries(world.stateSchema.variables)) {
|
|
1724
|
+
const group = document.createElement('div');
|
|
1725
|
+
group.className = 'slider-group';
|
|
1726
|
+
const min = v.min ?? 0;
|
|
1727
|
+
const max = v.max ?? 100;
|
|
1728
|
+
const step = v.step ?? 1;
|
|
1729
|
+
const def = v.default ?? 0;
|
|
1730
|
+
group.innerHTML = `
|
|
1731
|
+
<div class="state-label-row">
|
|
1732
|
+
<span class="state-label">${v.label || vid}</span>
|
|
1733
|
+
<span class="state-value" id="sv-${vid}">${def}</span>
|
|
1734
|
+
</div>
|
|
1735
|
+
<input type="range" id="slider-${vid}" min="${min}" max="${max}" step="${step}" value="${def}" data-var="${vid}">
|
|
1736
|
+
`;
|
|
1737
|
+
stateSlidersEl.appendChild(group);
|
|
1738
|
+
|
|
1739
|
+
const slider = group.querySelector('input');
|
|
1740
|
+
const display = group.querySelector('.state-value');
|
|
1741
|
+
slider.oninput = () => { display.textContent = slider.value; };
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// Invariants
|
|
1745
|
+
invariantListEl.innerHTML = '';
|
|
1746
|
+
for (const inv of world.invariants) {
|
|
1747
|
+
const li = document.createElement('li');
|
|
1748
|
+
li.textContent = inv.label;
|
|
1749
|
+
invariantListEl.appendChild(li);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1754
|
+
// SIMULATE + ANIMATE
|
|
1755
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1756
|
+
|
|
1757
|
+
simulateBtnEl.onclick = async () => {
|
|
1758
|
+
if (!currentWorldDef) return;
|
|
1759
|
+
|
|
1760
|
+
simulateBtnEl.classList.add('running');
|
|
1761
|
+
simulateBtnEl.textContent = 'Simulating...';
|
|
1762
|
+
simOutputEl.innerHTML = '';
|
|
1763
|
+
gateBadgeEl.classList.remove('visible');
|
|
1764
|
+
collapseBannerEl.classList.remove('visible');
|
|
1765
|
+
statsGridEl.innerHTML = '';
|
|
1766
|
+
|
|
1767
|
+
// Collect state overrides
|
|
1768
|
+
const overrides = {};
|
|
1769
|
+
for (const slider of stateSlidersEl.querySelectorAll('input[type="range"]')) {
|
|
1770
|
+
const vid = slider.dataset.var;
|
|
1771
|
+
const val = parseFloat(slider.value);
|
|
1772
|
+
overrides[vid] = val;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
const result = simulateWorld(currentWorldDef, {
|
|
1776
|
+
steps: parseInt(stepsSliderEl.value),
|
|
1777
|
+
stateOverrides: overrides,
|
|
1778
|
+
profile: profileSelectEl.value,
|
|
1779
|
+
});
|
|
1780
|
+
|
|
1781
|
+
// Animate steps one by one
|
|
1782
|
+
for (let i = 0; i < result.steps.length; i++) {
|
|
1783
|
+
const step = result.steps[i];
|
|
1784
|
+
const card = document.createElement('div');
|
|
1785
|
+
card.className = 'step-card';
|
|
1786
|
+
|
|
1787
|
+
// Find primary outcome
|
|
1788
|
+
const primaryOut = currentWorldDef.outcomes.computed_outcomes.find(o => o.primary);
|
|
1789
|
+
const primaryField = primaryOut ? primaryOut.id : null;
|
|
1790
|
+
const primaryVal = primaryField ? step.stateAfter[primaryField] : null;
|
|
1791
|
+
|
|
1792
|
+
card.innerHTML = `
|
|
1793
|
+
<div class="step-header">
|
|
1794
|
+
<span class="step-number">STEP ${step.step}</span>
|
|
1795
|
+
<span class="step-viability ${step.viability}">${step.viability}${primaryVal !== null ? ` (${Math.round(primaryVal)}%)` : ''}</span>
|
|
1796
|
+
</div>
|
|
1797
|
+
<div class="step-rules">
|
|
1798
|
+
${step.rulesEvaluated.map(r => {
|
|
1799
|
+
const icon = r.excluded ? '<span class="rule-icon excluded">-</span>'
|
|
1800
|
+
: r.triggered ? '<span class="rule-icon fired">\u26A1</span>'
|
|
1801
|
+
: '<span class="rule-icon pass">\u00B7</span>';
|
|
1802
|
+
const cls = r.triggered ? 'fired' : '';
|
|
1803
|
+
const effectStr = r.effects.length > 0
|
|
1804
|
+
? r.effects.map(e => {
|
|
1805
|
+
const pct = e.operation === 'multiply' ? `${Math.round(e.value * 100)}%` : `${e.operation} ${e.value}`;
|
|
1806
|
+
return `<span class="rule-effect ${r.severity}">${e.target} \u2192 ${pct}</span>`;
|
|
1807
|
+
}).join(' ')
|
|
1808
|
+
: '';
|
|
1809
|
+
return `<div class="rule-row">${icon}<span class="rule-label ${cls}">${r.label}</span>${effectStr}</div>`;
|
|
1810
|
+
}).join('')}
|
|
1811
|
+
</div>
|
|
1812
|
+
<div class="step-state">
|
|
1813
|
+
${Object.entries(step.stateAfter)
|
|
1814
|
+
.filter(([k]) => k in currentWorldDef.stateSchema.variables || (primaryField && k === primaryField))
|
|
1815
|
+
.map(([k, v]) => {
|
|
1816
|
+
const prev = i > 0 ? result.steps[i-1].stateAfter[k] : result.initialState[k];
|
|
1817
|
+
const changed = prev !== undefined && prev !== v;
|
|
1818
|
+
const label = currentWorldDef.stateSchema.variables[k]?.label || k;
|
|
1819
|
+
return `<span class="state-chip">${label}: <span class="val ${changed ? 'changed' : ''}">${typeof v === 'number' ? Math.round(v * 100) / 100 : v}</span></span>`;
|
|
1820
|
+
}).join('')}
|
|
1821
|
+
</div>
|
|
1822
|
+
`;
|
|
1823
|
+
|
|
1824
|
+
simOutputEl.appendChild(card);
|
|
1825
|
+
await sleep(120);
|
|
1826
|
+
card.classList.add('visible');
|
|
1827
|
+
await sleep(80);
|
|
1828
|
+
|
|
1829
|
+
// Update ring progressively
|
|
1830
|
+
if (primaryField && primaryVal !== null) {
|
|
1831
|
+
updateRing(Math.round(primaryVal), step.viability);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
// Final verdict
|
|
1836
|
+
await sleep(200);
|
|
1837
|
+
|
|
1838
|
+
const finalPrimary = currentWorldDef.outcomes.computed_outcomes.find(o => o.primary);
|
|
1839
|
+
const finalVal = finalPrimary ? Math.round(result.finalState[finalPrimary.id] ?? 0) : 0;
|
|
1840
|
+
|
|
1841
|
+
updateRing(finalVal, result.finalViability);
|
|
1842
|
+
|
|
1843
|
+
gateBadgeEl.textContent = result.finalViability;
|
|
1844
|
+
gateBadgeEl.className = `gate-badge ${result.finalViability} visible`;
|
|
1845
|
+
|
|
1846
|
+
// Stats
|
|
1847
|
+
const totalFired = result.steps.reduce((s, st) => s + st.rulesFired, 0);
|
|
1848
|
+
const totalRules = result.steps.reduce((s, st) => s + st.rulesEvaluated.length, 0);
|
|
1849
|
+
|
|
1850
|
+
statsGridEl.innerHTML = `
|
|
1851
|
+
<div class="stat-card">
|
|
1852
|
+
<div class="stat-label">Steps</div>
|
|
1853
|
+
<div class="stat-value">${result.steps.length}</div>
|
|
1854
|
+
</div>
|
|
1855
|
+
<div class="stat-card">
|
|
1856
|
+
<div class="stat-label">Rules Fired</div>
|
|
1857
|
+
<div class="stat-value ${totalFired > 5 ? 'warning' : ''}">${totalFired}</div>
|
|
1858
|
+
</div>
|
|
1859
|
+
<div class="stat-card">
|
|
1860
|
+
<div class="stat-label">Final Score</div>
|
|
1861
|
+
<div class="stat-value ${finalVal < 35 ? 'danger' : finalVal < 60 ? 'warning' : ''}">${finalVal}%</div>
|
|
1862
|
+
</div>
|
|
1863
|
+
<div class="stat-card">
|
|
1864
|
+
<div class="stat-label">Collapsed</div>
|
|
1865
|
+
<div class="stat-value ${result.collapsed ? 'danger' : ''}">${result.collapsed ? 'YES' : 'NO'}</div>
|
|
1866
|
+
</div>
|
|
1867
|
+
`;
|
|
1868
|
+
|
|
1869
|
+
if (result.collapsed) {
|
|
1870
|
+
collapseBannerEl.classList.add('visible');
|
|
1871
|
+
collapseDetailEl.textContent = `Collapsed at step ${result.collapseStep} by ${result.collapseRule}`;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
simulateBtnEl.classList.remove('running');
|
|
1875
|
+
simulateBtnEl.textContent = 'Simulate';
|
|
1876
|
+
};
|
|
1877
|
+
|
|
1878
|
+
function updateRing(value, viability) {
|
|
1879
|
+
const circumference = 2 * Math.PI * 70; // r=70
|
|
1880
|
+
const offset = circumference - (value / 100) * circumference;
|
|
1881
|
+
viabilityRingEl.style.strokeDashoffset = offset;
|
|
1882
|
+
|
|
1883
|
+
const color = value >= 60 ? 'var(--green)' : value >= 35 ? 'var(--amber)' : 'var(--red)';
|
|
1884
|
+
viabilityRingEl.style.stroke = color;
|
|
1885
|
+
viabilityRingEl.style.filter = `drop-shadow(0 0 6px ${color === 'var(--green)' ? 'var(--green-glow)' : color === 'var(--amber)' ? '#ffaa0060' : 'var(--red-glow)'})`;
|
|
1886
|
+
|
|
1887
|
+
viabilityValueEl.textContent = value + '%';
|
|
1888
|
+
viabilityValueEl.style.color = color;
|
|
1889
|
+
viabilityValueEl.style.textShadow = `0 0 20px ${color === 'var(--green)' ? 'var(--green-glow)' : color === 'var(--amber)' ? '#ffaa0060' : 'var(--red-glow)'}`;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
1893
|
+
|
|
1894
|
+
// Auto-select first world
|
|
1895
|
+
selectWorld('coding-agent');
|
|
1896
|
+
|
|
1897
|
+
</script>
|
|
1898
|
+
</body>
|
|
1899
|
+
</html>
|