@neuroverseos/governance 0.8.1 → 0.9.0

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.
@@ -20,1086 +20,1101 @@ var __copyProps = (to, from, except, desc) => {
20
20
  };
21
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
22
 
23
- // src/cli/worldmodel-create.ts
24
- var worldmodel_create_exports = {};
25
- __export(worldmodel_create_exports, {
26
- askQuestions: () => askQuestions,
27
- saveWorldmodel: () => saveWorldmodel,
28
- structureWorldmodel: () => structureWorldmodel
23
+ // src/engine/worldmodel-compiler.ts
24
+ var worldmodel_compiler_exports = {};
25
+ __export(worldmodel_compiler_exports, {
26
+ compileWorldModel: () => compileWorldModel,
27
+ emitContextsConfig: () => emitContextsConfig,
28
+ emitLensSuggestions: () => emitLensSuggestions,
29
+ emitOverlapMap: () => emitOverlapMap,
30
+ emitSignalSchema: () => emitSignalSchema,
31
+ emitWorldMarkdown: () => emitWorldMarkdown
29
32
  });
30
- async function askQuestions() {
31
- const rl = (0, import_readline.createInterface)({
32
- input: process.stdin,
33
- output: process.stderr
34
- // questions to stderr so stdout stays clean
35
- });
36
- const answers = {};
37
- process.stderr.write("\n");
38
- process.stderr.write(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n");
39
- process.stderr.write(" \u2551 NeuroVerseOS \u2014 Build your thinking constitution \u2551\n");
40
- process.stderr.write(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\n");
41
- process.stderr.write("\n");
42
- process.stderr.write(" Answer these questions in your own words.\n");
43
- process.stderr.write(" There are no wrong answers \u2014 just say what you mean.\n");
44
- process.stderr.write(" The AI will structure your answers into a worldmodel.\n\n");
45
- for (const q of QUESTIONS) {
46
- const answer = await new Promise((resolve3) => {
47
- process.stderr.write(` \x1B[1m${q.question}\x1B[0m
48
- `);
49
- process.stderr.write(` \x1B[2m${q.placeholder}\x1B[0m
50
- `);
51
- rl.question(" > ", (ans) => {
52
- process.stderr.write("\n");
53
- resolve3(ans.trim());
54
- });
55
- });
56
- answers[q.id] = answer;
57
- }
58
- rl.close();
59
- return answers;
60
- }
61
- async function structureWorldmodel(answers, apiKey) {
62
- const prompt = buildStructuringPrompt(answers);
63
- const res = await fetch("https://api.anthropic.com/v1/messages", {
64
- method: "POST",
65
- headers: {
66
- "x-api-key": apiKey,
67
- "anthropic-version": "2023-06-01",
68
- "content-type": "application/json"
69
- },
70
- body: JSON.stringify({
71
- model: "claude-sonnet-4-20250514",
72
- max_tokens: 4096,
73
- system: "You are a behavioral model architect. You take conversational answers about an organization's values, purpose, and priorities, and structure them into a precise .worldmodel.md file that follows the NeuroVerseOS three-layer format. Output ONLY the markdown file content, nothing else.",
74
- messages: [{ role: "user", content: prompt }]
75
- })
76
- });
77
- if (!res.ok) {
78
- throw new Error(`AI structuring failed: ${res.status}`);
79
- }
80
- const data = await res.json();
81
- const text = data.content?.filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
82
- if (!text) throw new Error("AI returned no content");
83
- return text.replace(/^```markdown\n?/, "").replace(/\n?```$/, "").trim();
33
+ function toSnakeCase(text) {
34
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
84
35
  }
85
- function buildStructuringPrompt(answers) {
86
- return `Structure these conversational answers into a .worldmodel.md file.
87
-
88
- The file MUST follow this exact three-layer format:
89
-
90
- ---
91
- name: ${answers.name}
92
- version: 1.0.0
93
- ---
94
-
95
- # Core Model Geometry
96
-
97
- ## Mission
98
- (from the mission answer)
99
-
100
- ## Domains
101
- (2-4 domains from the domains answer, each with:)
102
- ### Domain Name
103
- #### Skills (8-10 skills per domain, inferred from the answers)
104
- #### Values (3-4 values per domain, drawn from the non-negotiables + mission)
105
-
106
- ## Overlap Effects
107
- (from the overlaps answer, formatted as: Domain A + Domain B = Emergent State)
108
-
109
- ## Center Identity
110
- (from the center answer)
111
-
112
- # Contextual Modifiers
113
-
114
- ## Authority Layers
115
- (infer 4-5 authority levels appropriate to this organization)
116
-
117
- ## Spatial Contexts
118
- (infer 4-5 contexts where behavior happens)
119
-
120
- ## Interpretation Rules
121
- (infer 3-5 rules about how context changes meaning)
122
-
123
- # Evolution Layer
124
-
125
- ## Aligned Behaviors
126
- (from the success answer, expanded to 5-8 items)
127
-
128
- ## Drift Behaviors
129
- (from the drift answer, expanded to 5-8 items)
130
-
131
- ## Signals
132
- (infer 5-7 observable signals from the answers, snake_case)
133
-
134
- ## Decision Priorities
135
- (from the priorities answer, formatted as: preferred > secondary)
136
-
137
- ## Evolution Conditions
138
- (infer 3-5 conditions for when the model should adapt)
139
-
140
- HERE ARE THE ANSWERS:
141
-
142
- Name: ${answers.name}
143
-
144
- Mission: ${answers.mission}
145
-
146
- Domains: ${answers.domains}
147
-
148
- Overlaps: ${answers.overlaps}
149
-
150
- Center identity: ${answers.center}
151
-
152
- Non-negotiables: ${answers.nonnegotiables}
153
-
154
- Success looks like: ${answers.success}
155
-
156
- Drift looks like: ${answers.drift}
157
-
158
- Priorities: ${answers.priorities}
159
-
160
- Output ONLY the .worldmodel.md content. No explanation. No commentary. Just the file.`;
36
+ function toKebabCase2(text) {
37
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
161
38
  }
162
- function saveWorldmodel(content, outputPath) {
163
- const resolved = (0, import_path.resolve)(outputPath);
164
- (0, import_fs.writeFileSync)(resolved, content, "utf-8");
165
- return resolved;
39
+ function titleCase(text) {
40
+ return text.split(/[\s_-]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
166
41
  }
167
- var import_readline, import_fs, import_path, QUESTIONS;
168
- var init_worldmodel_create = __esm({
169
- "src/cli/worldmodel-create.ts"() {
170
- "use strict";
171
- import_readline = require("readline");
172
- import_fs = require("fs");
173
- import_path = require("path");
174
- QUESTIONS = [
175
- {
176
- id: "name",
177
- question: "What should we call this model? (Your name, your org, your project)",
178
- placeholder: 'e.g., "Kirsten", "Auki", "My Startup"'
179
- },
180
- {
181
- id: "mission",
182
- question: "In one or two sentences \u2014 what does this system exist to do? Not a slogan. The real purpose.",
183
- placeholder: 'e.g., "Protect human thinking while expanding cognitive capability through AI"'
184
- },
185
- {
186
- id: "domains",
187
- question: "What are the 2-3 big areas of focus? Not departments \u2014 the major kinds of work that matter most. Separate with commas.",
188
- placeholder: 'e.g., "Safety and boundaries, Individual authority, AI as cognitive extension"'
189
- },
190
- {
191
- id: "overlaps",
192
- question: "When those areas work well TOGETHER, what does that feel like? Name a feeling for each pair.",
193
- placeholder: 'e.g., "Safety + Authority = Trust, Authority + AI = Possibility"'
194
- },
195
- {
196
- id: "center",
197
- question: "When EVERYTHING is aligned \u2014 all areas working together \u2014 what does the system become? One name or phrase.",
198
- placeholder: 'e.g., "The Sovereign Conduit", "Collective Vanguard Leader"'
199
- },
200
- {
201
- id: "nonnegotiables",
202
- question: "What's absolutely non-negotiable? What would you walk away over? List a few.",
203
- placeholder: 'e.g., "Humans retain authority over thinking. AI extends, never replaces. People can always leave."'
204
- },
205
- {
206
- id: "success",
207
- question: `What does success look like in action? What would you point at and say "that's what I mean"?`,
208
- placeholder: 'e.g., "Someone maintaining clear authorship of decisions even when AI contributed"'
209
- },
210
- {
211
- id: "drift",
212
- question: "What does drift look like? What would worry you if you saw it happening?",
213
- placeholder: 'e.g., "Decision ownership quietly shifting to AI without explicit delegation"'
214
- },
215
- {
216
- id: "priorities",
217
- question: 'When tradeoffs appear, what wins? Give a few "X over Y" pairs.',
218
- placeholder: 'e.g., "Safety over speed, sovereignty over convenience, diversity over uniformity"'
219
- }
220
- ];
221
- }
222
- });
223
-
224
- // src/cli/worldmodel.ts
225
- var worldmodel_exports = {};
226
- __export(worldmodel_exports, {
227
- main: () => main
228
- });
229
- module.exports = __toCommonJS(worldmodel_exports);
230
- var import_promises = require("fs/promises");
231
- var import_fs2 = require("fs");
232
- var import_path2 = require("path");
233
-
234
- // src/engine/worldmodel-parser.ts
235
- function splitSections(markdown) {
236
- const lines = markdown.split("\n");
237
- let frontmatter = "";
238
- let bodyStart = 0;
239
- if (lines[0]?.trim() === "---") {
240
- const endIdx = lines.indexOf("---", 1);
241
- if (endIdx > 0) {
242
- frontmatter = lines.slice(1, endIdx).join("\n");
243
- bodyStart = endIdx + 1;
244
- }
245
- }
246
- const sections = [];
247
- let currentSection = null;
248
- const contentLines = [];
249
- for (let i = bodyStart; i < lines.length; i++) {
250
- const line = lines[i];
251
- if (line.startsWith("# ")) {
252
- if (currentSection) {
253
- currentSection.content = contentLines.join("\n").trim();
254
- sections.push(currentSection);
255
- contentLines.length = 0;
256
- }
257
- currentSection = {
258
- name: line.replace(/^#\s+/, "").trim(),
259
- content: "",
260
- startLine: i + 1
261
- // 1-based
262
- };
263
- } else if (currentSection) {
264
- contentLines.push(line);
42
+ function matchSignal(behaviorText, signals) {
43
+ const lowerText = behaviorText.toLowerCase();
44
+ for (const signal of signals) {
45
+ const signalWords = signal.toLowerCase().split("_");
46
+ if (signalWords.some((w) => w.length > 3 && lowerText.includes(w))) {
47
+ return signal;
265
48
  }
266
49
  }
267
- if (currentSection) {
268
- currentSection.content = contentLines.join("\n").trim();
269
- sections.push(currentSection);
270
- }
271
- return { frontmatter, sections };
50
+ return "alignment_score";
272
51
  }
273
- function splitH2Sections(content, baseLine) {
274
- const lines = content.split("\n");
275
- const sections = [];
276
- let currentSection = null;
277
- const contentLines = [];
278
- for (let i = 0; i < lines.length; i++) {
279
- const line = lines[i];
280
- if (line.startsWith("## ")) {
281
- if (currentSection) {
282
- currentSection.content = contentLines.join("\n").trim();
283
- sections.push(currentSection);
284
- contentLines.length = 0;
285
- }
286
- currentSection = {
287
- name: line.replace(/^##\s+/, "").trim(),
288
- content: "",
289
- startLine: baseLine + i
290
- };
291
- } else if (currentSection) {
292
- contentLines.push(line);
52
+ function emitWorldMarkdown(model) {
53
+ const lines = [];
54
+ const worldId = model.frontmatter.model_id;
55
+ const worldName = model.frontmatter.name;
56
+ const version = model.frontmatter.version;
57
+ lines.push("---");
58
+ lines.push(`world_id: ${worldId}`);
59
+ lines.push(`name: ${worldName}`);
60
+ lines.push(`version: ${version}`);
61
+ lines.push("runtime_mode: COMPLIANCE");
62
+ lines.push("default_profile: aligned");
63
+ lines.push("alternative_profile: drifting");
64
+ lines.push("---");
65
+ lines.push("");
66
+ lines.push("# Thesis");
67
+ lines.push("");
68
+ lines.push(model.geometry.mission);
69
+ if (model.geometry.centerIdentity) {
70
+ lines.push(
71
+ `When all domains are aligned, the system operates as: ${model.geometry.centerIdentity}.`
72
+ );
73
+ }
74
+ lines.push("");
75
+ lines.push("# Invariants");
76
+ lines.push("");
77
+ let invariantIdx = 0;
78
+ for (const domain of model.geometry.domains) {
79
+ for (const value of domain.values) {
80
+ invariantIdx++;
81
+ const invId = `${toSnakeCase(domain.name)}_value_${String(invariantIdx).padStart(2, "0")}`;
82
+ lines.push(
83
+ `- \`${invId}\` \u2014 ${value} [${domain.name}] (structural, immutable)`
84
+ );
293
85
  }
294
86
  }
295
- if (currentSection) {
296
- currentSection.content = contentLines.join("\n").trim();
297
- sections.push(currentSection);
87
+ lines.push("");
88
+ lines.push("# State");
89
+ lines.push("");
90
+ lines.push("## alignment_score");
91
+ lines.push("- type: number");
92
+ lines.push("- min: 0");
93
+ lines.push("- max: 100");
94
+ lines.push("- step: 5");
95
+ lines.push("- default: 70");
96
+ lines.push("- label: Alignment Score");
97
+ lines.push(
98
+ "- description: Composite behavioral alignment metric derived from all signals"
99
+ );
100
+ lines.push("");
101
+ for (const signal of model.evolution.signals) {
102
+ const signalId = toSnakeCase(signal);
103
+ const signalLabel = titleCase(signal);
104
+ lines.push(`## ${signalId}`);
105
+ lines.push("- type: number");
106
+ lines.push("- min: 0");
107
+ lines.push("- max: 100");
108
+ lines.push("- step: 5");
109
+ lines.push("- default: 70");
110
+ lines.push(`- label: ${signalLabel}`);
111
+ lines.push(`- description: Behavioral signal measuring ${signal.replace(/_/g, " ")}`);
112
+ lines.push("");
298
113
  }
299
- return sections;
300
- }
301
- function splitH3Sections(content, baseLine) {
302
- const lines = content.split("\n");
303
- const sections = [];
304
- let currentSection = null;
305
- const contentLines = [];
306
- for (let i = 0; i < lines.length; i++) {
307
- const line = lines[i];
308
- if (line.startsWith("### ")) {
309
- if (currentSection) {
310
- currentSection.content = contentLines.join("\n").trim();
311
- sections.push(currentSection);
312
- contentLines.length = 0;
313
- }
314
- currentSection = {
315
- name: line.replace(/^###\s+/, "").trim(),
316
- content: "",
317
- startLine: baseLine + i
318
- };
319
- } else if (currentSection) {
320
- contentLines.push(line);
321
- }
114
+ lines.push("# Assumptions");
115
+ lines.push("");
116
+ lines.push("## aligned");
117
+ lines.push("- name: Aligned");
118
+ lines.push("- description: All behavioral signals at healthy levels");
119
+ lines.push("- pressure_level: low");
120
+ lines.push("");
121
+ lines.push("## drifting");
122
+ lines.push("- name: Drifting");
123
+ lines.push("- description: Behavioral signals under pressure with drift risk");
124
+ lines.push("- pressure_level: high");
125
+ lines.push("");
126
+ lines.push("# Rules");
127
+ lines.push("");
128
+ let ruleIdx = 0;
129
+ for (const drift of model.evolution.driftBehaviors) {
130
+ ruleIdx++;
131
+ const ruleId = `rule-${String(ruleIdx).padStart(3, "0")}`;
132
+ const matchedSignal = matchSignal(drift, model.evolution.signals);
133
+ const signalId = toSnakeCase(matchedSignal);
134
+ lines.push(`## ${ruleId}: ${drift} (degradation)`);
135
+ lines.push(`Drift behavior detected: ${drift}`);
136
+ lines.push("");
137
+ lines.push(`When ${signalId} < 50 [state]`);
138
+ lines.push("Then alignment_score *= 0.80");
139
+ lines.push("");
140
+ lines.push(`> trigger: ${signalId} drops below threshold`);
141
+ lines.push(`> rule: Drift behavior weakens alignment`);
142
+ lines.push(`> shift: Behavioral alignment decreases`);
143
+ lines.push(`> effect: Alignment score reduced by 20%`);
144
+ lines.push("");
322
145
  }
323
- if (currentSection) {
324
- currentSection.content = contentLines.join("\n").trim();
325
- sections.push(currentSection);
146
+ for (const aligned of model.evolution.alignedBehaviors) {
147
+ ruleIdx++;
148
+ const ruleId = `rule-${String(ruleIdx).padStart(3, "0")}`;
149
+ const matchedSignal = matchSignal(aligned, model.evolution.signals);
150
+ const signalId = toSnakeCase(matchedSignal);
151
+ lines.push(`## ${ruleId}: ${aligned} (advantage)`);
152
+ lines.push(`Aligned behavior reinforced: ${aligned}`);
153
+ lines.push("");
154
+ lines.push(`When ${signalId} >= 70 [state]`);
155
+ lines.push("Then alignment_score *= 1.10");
156
+ lines.push("");
157
+ lines.push(`> trigger: ${signalId} above healthy threshold`);
158
+ lines.push(`> rule: Aligned behavior strengthens system`);
159
+ lines.push(`> shift: Behavioral alignment increases`);
160
+ lines.push(`> effect: Alignment score boosted by 10%`);
161
+ lines.push("");
326
162
  }
327
- return sections;
328
- }
329
- function splitH4Sections(content, baseLine) {
330
- const lines = content.split("\n");
331
- const sections = [];
332
- let currentSection = null;
333
- const contentLines = [];
334
- for (let i = 0; i < lines.length; i++) {
335
- const line = lines[i];
336
- if (line.startsWith("#### ")) {
337
- if (currentSection) {
338
- currentSection.content = contentLines.join("\n").trim();
339
- sections.push(currentSection);
340
- contentLines.length = 0;
163
+ for (let i = 0; i < model.evolution.decisionPriorities.length; i++) {
164
+ ruleIdx++;
165
+ const priority = model.evolution.decisionPriorities[i];
166
+ const ruleId = `rule-${String(ruleIdx).padStart(3, "0")}`;
167
+ lines.push(
168
+ `## ${ruleId}: ${priority.preferred} over ${priority.over} (structural)`
169
+ );
170
+ lines.push(
171
+ `Priority: ${priority.preferred} takes precedence over ${priority.over} in tradeoff situations.`
172
+ );
173
+ lines.push("");
174
+ lines.push("When alignment_score < 40 [state]");
175
+ lines.push("Then alignment_score *= 0.70");
176
+ lines.push("Collapse: alignment_score < 10");
177
+ lines.push("");
178
+ lines.push(`> trigger: Alignment score critically low`);
179
+ lines.push(
180
+ `> rule: Priority violation \u2014 ${priority.preferred} must outweigh ${priority.over}`
181
+ );
182
+ lines.push(`> shift: System enters structural enforcement`);
183
+ lines.push(`> effect: Alignment sharply reduced; collapse if critical`);
184
+ lines.push("");
185
+ }
186
+ lines.push("# Gates");
187
+ lines.push("");
188
+ lines.push("- STRONG: alignment_score >= 85");
189
+ lines.push("- STABLE: alignment_score >= 65");
190
+ lines.push("- WATCHING: alignment_score >= 45");
191
+ lines.push("- FRAGILE: alignment_score > 30");
192
+ lines.push("- MISALIGNED: alignment_score <= 30");
193
+ lines.push("");
194
+ lines.push("# Outcomes");
195
+ lines.push("");
196
+ lines.push("## alignment_score");
197
+ lines.push("- type: number");
198
+ lines.push("- range: 0-100");
199
+ lines.push("- display: percentage");
200
+ lines.push("- label: Alignment Score");
201
+ lines.push("- primary: true");
202
+ lines.push("");
203
+ for (const signal of model.evolution.signals) {
204
+ const signalId = toSnakeCase(signal);
205
+ const signalLabel = titleCase(signal);
206
+ lines.push(`## ${signalId}`);
207
+ lines.push("- type: number");
208
+ lines.push("- range: 0-100");
209
+ lines.push("- display: percentage");
210
+ lines.push(`- label: ${signalLabel}`);
211
+ lines.push("");
212
+ }
213
+ const lensSuggestions = buildLensSuggestions(model);
214
+ if (lensSuggestions.length > 0) {
215
+ lines.push("# Lenses");
216
+ lines.push("- policy: role_default");
217
+ lines.push("");
218
+ for (const lens of lensSuggestions) {
219
+ lines.push(`## ${lens.id}`);
220
+ lines.push(`- tagline: ${lens.tagline}`);
221
+ lines.push(
222
+ `- description: Lens derived from ${lens.derived_from.domainA} and ${lens.derived_from.domainB} interaction, producing ${lens.derived_from.effect}.`
223
+ );
224
+ lines.push(`- formality: ${lens.tone.formality}`);
225
+ lines.push(`- verbosity: ${lens.tone.verbosity}`);
226
+ lines.push(`- emotion: ${lens.tone.emotion}`);
227
+ lines.push(`- confidence: ${lens.tone.confidence}`);
228
+ lines.push("- tags: behavioral, worldmodel, overlap");
229
+ lines.push("- default_for_roles: all");
230
+ lines.push("- priority: 50");
231
+ lines.push("- stackable: true");
232
+ lines.push("");
233
+ for (const directive of lens.directives) {
234
+ lines.push(`> ${directive.scope}: ${directive.instruction}`);
341
235
  }
342
- currentSection = {
343
- name: line.replace(/^####\s+/, "").trim(),
344
- content: "",
345
- startLine: baseLine + i
346
- };
347
- } else if (currentSection) {
348
- contentLines.push(line);
236
+ lines.push("");
349
237
  }
350
238
  }
351
- if (currentSection) {
352
- currentSection.content = contentLines.join("\n").trim();
353
- sections.push(currentSection);
354
- }
355
- return sections;
239
+ return lines.join("\n");
356
240
  }
357
- function parseBulletList(content) {
358
- const items = [];
359
- let inComment = false;
360
- for (const line of content.split("\n")) {
361
- const trimmed = line.trim();
362
- if (trimmed.startsWith("<!--")) {
363
- inComment = true;
364
- }
365
- if (inComment) {
366
- if (trimmed.includes("-->")) {
367
- inComment = false;
368
- }
369
- continue;
370
- }
371
- if (trimmed.startsWith("- ")) {
372
- items.push(trimmed.slice(2).trim());
373
- }
374
- }
375
- return items;
241
+ function emitSignalSchema(model) {
242
+ const signals = model.evolution.signals.map((signal) => ({
243
+ id: toSnakeCase(signal),
244
+ name: titleCase(signal),
245
+ type: "number",
246
+ default: 70
247
+ }));
248
+ return {
249
+ model_id: model.frontmatter.model_id,
250
+ signals
251
+ };
376
252
  }
377
- function extractTextContent(content) {
378
- const lines = content.split("\n");
379
- const textLines = [];
380
- let inComment = false;
381
- for (const line of lines) {
382
- const trimmed = line.trim();
383
- if (trimmed.startsWith("<!--")) {
384
- inComment = true;
385
- }
386
- if (inComment) {
387
- if (trimmed.includes("-->")) {
388
- inComment = false;
389
- }
390
- continue;
391
- }
392
- if (trimmed && !trimmed.startsWith("#")) {
393
- textLines.push(trimmed);
394
- }
253
+ function emitOverlapMap(model) {
254
+ const pairings = model.geometry.overlapEffects.map((o) => ({
255
+ domainA: o.domainA,
256
+ domainB: o.domainB,
257
+ effect: o.effect
258
+ }));
259
+ const matrix = {};
260
+ for (const overlap of model.geometry.overlapEffects) {
261
+ const keyA = toKebabCase2(overlap.domainA);
262
+ const keyB = toKebabCase2(overlap.domainB);
263
+ if (!matrix[keyA]) matrix[keyA] = {};
264
+ matrix[keyA][keyB] = overlap.effect;
395
265
  }
396
- return textLines.join("\n").trim();
266
+ return {
267
+ model_id: model.frontmatter.model_id,
268
+ pairings,
269
+ matrix
270
+ };
397
271
  }
398
- function toKebabCase(name) {
399
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
272
+ function emitContextsConfig(model) {
273
+ return {
274
+ model_id: model.frontmatter.model_id,
275
+ authority_layers: model.modifiers.authorityLayers,
276
+ spatial_contexts: model.modifiers.spatialContexts,
277
+ interpretation_rules: model.modifiers.interpretationRules
278
+ };
400
279
  }
401
- function parseFrontmatter(yaml, issues) {
402
- const result = {};
403
- for (const line of yaml.split("\n")) {
404
- const trimmed = line.trim();
405
- if (!trimmed || trimmed.startsWith("#")) continue;
406
- const colonIdx = trimmed.indexOf(":");
407
- if (colonIdx === -1) continue;
408
- const key = trimmed.slice(0, colonIdx).trim();
409
- const value = trimmed.slice(colonIdx + 1).trim();
410
- result[key] = value;
280
+ function deriveTone(domainA, domainB) {
281
+ const combined = `${domainA} ${domainB}`.toLowerCase();
282
+ let formality = "neutral";
283
+ if (/strateg|technic|analytic|research|engineer/.test(combined)) {
284
+ formality = "professional";
285
+ } else if (/narrat|story|communi|creative/.test(combined)) {
286
+ formality = "casual";
411
287
  }
412
- const name = result.name || "";
413
- if (!name) {
414
- issues.push({
415
- line: 1,
416
- section: "frontmatter",
417
- message: "Missing name in frontmatter. Provide a human-readable model name.",
418
- severity: "error"
288
+ let verbosity = "balanced";
289
+ if (/foresight|scenario|plan|design/.test(combined)) {
290
+ verbosity = "detailed";
291
+ } else if (/prosper|negotiat|stakeholder/.test(combined)) {
292
+ verbosity = "concise";
293
+ }
294
+ let emotion = "neutral";
295
+ if (/empath|emoti|care|safe|trust/.test(combined)) {
296
+ emotion = "warm";
297
+ } else if (/analytic|system|data/.test(combined)) {
298
+ emotion = "clinical";
299
+ }
300
+ let confidence = "balanced";
301
+ if (/lead|command|decis|strateg/.test(combined)) {
302
+ confidence = "authoritative";
303
+ } else if (/explor|experiment|creat/.test(combined)) {
304
+ confidence = "exploratory";
305
+ }
306
+ return { formality, verbosity, emotion, confidence };
307
+ }
308
+ function buildLensSuggestions(model) {
309
+ const lenses = [];
310
+ const domainMap = new Map(model.geometry.domains.map((d) => [d.name.toLowerCase(), d]));
311
+ for (const overlap of model.geometry.overlapEffects) {
312
+ const lensId = toKebabCase2(overlap.effect);
313
+ const tone = deriveTone(overlap.domainA, overlap.domainB);
314
+ const domainAData = domainMap.get(overlap.domainA.toLowerCase());
315
+ const domainBData = domainMap.get(overlap.domainB.toLowerCase());
316
+ const directives = [];
317
+ if (domainAData && domainAData.skills.length > 0) {
318
+ directives.push({
319
+ scope: "response_framing",
320
+ instruction: `Approach through the lens of ${domainAData.skills.join(", ").toLowerCase()}.`
321
+ });
322
+ }
323
+ if (domainBData && domainBData.values.length > 0) {
324
+ directives.push({
325
+ scope: "behavior_shaping",
326
+ instruction: `Maintain ${domainBData.values.join(", ").toLowerCase()} in all responses.`
327
+ });
328
+ }
329
+ directives.push({
330
+ scope: "value_emphasis",
331
+ instruction: `Emphasize ${overlap.effect.toLowerCase()} as the emergent state of aligned behavior.`
332
+ });
333
+ lenses.push({
334
+ id: lensId,
335
+ name: titleCase(overlap.effect),
336
+ tagline: `${overlap.effect} through ${overlap.domainA} and ${overlap.domainB}.`,
337
+ derived_from: {
338
+ domainA: overlap.domainA,
339
+ domainB: overlap.domainB,
340
+ effect: overlap.effect
341
+ },
342
+ tone,
343
+ directives
419
344
  });
420
345
  }
421
- const model_id = result.model_id || toKebabCase(name);
346
+ return lenses;
347
+ }
348
+ function emitLensSuggestions(model) {
349
+ return buildLensSuggestions(model);
350
+ }
351
+ function compileWorldModel(model) {
422
352
  return {
423
- model_id,
424
- name,
425
- version: result.version || "1.0.0"
353
+ worldMarkdown: emitWorldMarkdown(model),
354
+ signalSchema: emitSignalSchema(model),
355
+ overlapMap: emitOverlapMap(model),
356
+ contextsConfig: emitContextsConfig(model),
357
+ lensSuggestions: emitLensSuggestions(model)
426
358
  };
427
359
  }
428
- function parseGeometry(section, issues) {
429
- const geometry = {
430
- mission: "",
431
- domains: [],
432
- overlapEffects: [],
433
- centerIdentity: ""
434
- };
435
- if (!section) {
436
- issues.push({
437
- line: 0,
438
- section: "Core Model Geometry",
439
- message: "Missing # Core Model Geometry section. Define the structural model: mission, domains with embedded skills and values, overlaps, and center identity.",
440
- severity: "error"
441
- });
442
- return geometry;
443
- }
444
- const h2Sections = splitH2Sections(section.content, section.startLine);
445
- const missionSection = h2Sections.find((s) => s.name.toLowerCase() === "mission");
446
- if (missionSection) {
447
- geometry.mission = extractTextContent(missionSection.content);
360
+ var init_worldmodel_compiler = __esm({
361
+ "src/engine/worldmodel-compiler.ts"() {
362
+ "use strict";
448
363
  }
449
- if (!geometry.mission) {
450
- issues.push({
451
- line: missionSection?.startLine ?? section.startLine,
452
- section: "Mission",
453
- message: "Missing ## Mission. Define what this system is trying to achieve \u2014 the core aim, not a slogan.",
454
- severity: "error"
364
+ });
365
+
366
+ // src/cli/worldmodel-create.ts
367
+ var worldmodel_create_exports = {};
368
+ __export(worldmodel_create_exports, {
369
+ askQuestions: () => askQuestions,
370
+ saveWorldmodel: () => saveWorldmodel,
371
+ structureWorldmodel: () => structureWorldmodel
372
+ });
373
+ async function askQuestions() {
374
+ const rl = (0, import_readline.createInterface)({
375
+ input: process.stdin,
376
+ output: process.stderr
377
+ // questions to stderr so stdout stays clean
378
+ });
379
+ const answers = {};
380
+ process.stderr.write("\n");
381
+ process.stderr.write(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n");
382
+ process.stderr.write(" \u2551 NeuroVerseOS \u2014 Build your thinking constitution \u2551\n");
383
+ process.stderr.write(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\n");
384
+ process.stderr.write("\n");
385
+ process.stderr.write(" Answer these questions in your own words.\n");
386
+ process.stderr.write(" There are no wrong answers \u2014 just say what you mean.\n");
387
+ process.stderr.write(" The AI will structure your answers into a worldmodel.\n\n");
388
+ for (const q of QUESTIONS) {
389
+ const answer = await new Promise((resolve3) => {
390
+ process.stderr.write(` \x1B[1m${q.question}\x1B[0m
391
+ `);
392
+ process.stderr.write(` \x1B[2m${q.placeholder}\x1B[0m
393
+ `);
394
+ rl.question(" > ", (ans) => {
395
+ process.stderr.write("\n");
396
+ resolve3(ans.trim());
397
+ });
455
398
  });
399
+ answers[q.id] = answer;
456
400
  }
457
- const domainsSection = h2Sections.find((s) => s.name.toLowerCase() === "domains");
458
- if (domainsSection) {
459
- const domainSections = splitH3Sections(domainsSection.content, domainsSection.startLine);
460
- for (const ds of domainSections) {
461
- const domain = {
462
- id: toKebabCase(ds.name),
463
- name: ds.name,
464
- skills: [],
465
- values: [],
466
- line: ds.startLine
467
- };
468
- const h4Sections = splitH4Sections(ds.content, ds.startLine);
469
- const skillsH4 = h4Sections.find((s) => s.name.toLowerCase() === "skills");
470
- if (skillsH4) {
471
- domain.skills = parseBulletList(skillsH4.content);
472
- }
473
- const valuesH4 = h4Sections.find((s) => s.name.toLowerCase() === "values");
474
- if (valuesH4) {
475
- domain.values = parseBulletList(valuesH4.content);
476
- }
477
- if (domain.skills.length === 0) {
478
- issues.push({
479
- line: ds.startLine,
480
- section: "Domains",
481
- message: `Domain '${ds.name}' has no skills defined. Skills are the capabilities within this domain.`,
482
- severity: "warning"
483
- });
484
- }
485
- if (domain.values.length === 0) {
486
- issues.push({
487
- line: ds.startLine,
488
- section: "Domains",
489
- message: `Domain '${ds.name}' has no values defined. Behavior has no constraints without values.`,
490
- severity: "warning"
491
- });
492
- }
493
- geometry.domains.push(domain);
494
- }
495
- }
496
- if (geometry.domains.length < 2) {
497
- issues.push({
498
- line: domainsSection?.startLine ?? section.startLine,
499
- section: "Domains",
500
- message: "At least 2 domains required. Domains are the major operating modes of the system, each carrying skills and values.",
501
- severity: "error"
502
- });
401
+ rl.close();
402
+ return answers;
403
+ }
404
+ async function structureWorldmodel(answers, apiKey) {
405
+ const prompt = buildStructuringPrompt(answers);
406
+ const res = await fetch("https://api.anthropic.com/v1/messages", {
407
+ method: "POST",
408
+ headers: {
409
+ "x-api-key": apiKey,
410
+ "anthropic-version": "2023-06-01",
411
+ "content-type": "application/json"
412
+ },
413
+ body: JSON.stringify({
414
+ model: "claude-sonnet-4-20250514",
415
+ max_tokens: 4096,
416
+ system: "You are a behavioral model architect. You take conversational answers about an organization's values, purpose, and priorities, and structure them into a precise .worldmodel.md file that follows the NeuroVerseOS three-layer format. Output ONLY the markdown file content, nothing else.",
417
+ messages: [{ role: "user", content: prompt }]
418
+ })
419
+ });
420
+ if (!res.ok) {
421
+ throw new Error(`AI structuring failed: ${res.status}`);
503
422
  }
504
- const overlapsSection = h2Sections.find(
505
- (s) => s.name.toLowerCase() === "overlap effects"
506
- );
507
- if (overlapsSection) {
508
- const bullets = parseBulletList(overlapsSection.content);
509
- const domainNames = new Set(geometry.domains.map((d) => d.name.toLowerCase()));
510
- for (let i = 0; i < bullets.length; i++) {
511
- const bullet = bullets[i];
512
- const match = bullet.match(/^(.+?)\s*\+\s*(.+?)\s*=\s*(.+)$/);
513
- if (match) {
514
- const domainA = match[1].trim();
515
- const domainB = match[2].trim();
516
- const effect = match[3].trim();
517
- if (!domainNames.has(domainA.toLowerCase())) {
518
- issues.push({
519
- line: overlapsSection.startLine + i,
520
- section: "Overlap Effects",
521
- message: `Overlap references unknown domain '${domainA}'. Must reference a declared domain.`,
522
- severity: "warning"
523
- });
524
- }
525
- if (!domainNames.has(domainB.toLowerCase())) {
526
- issues.push({
527
- line: overlapsSection.startLine + i,
528
- section: "Overlap Effects",
529
- message: `Overlap references unknown domain '${domainB}'. Must reference a declared domain.`,
530
- severity: "warning"
531
- });
532
- }
533
- geometry.overlapEffects.push({
534
- domainA,
535
- domainB,
536
- effect,
537
- line: overlapsSection.startLine + i
538
- });
539
- } else {
540
- issues.push({
541
- line: overlapsSection.startLine + i,
542
- section: "Overlap Effects",
543
- message: `Cannot parse overlap: '${bullet}'. Expected format: 'Domain A + Domain B = Emergent State'.`,
544
- severity: "warning"
545
- });
423
+ const data = await res.json();
424
+ const text = data.content?.filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
425
+ if (!text) throw new Error("AI returned no content");
426
+ return text.replace(/^```markdown\n?/, "").replace(/\n?```$/, "").trim();
427
+ }
428
+ function buildStructuringPrompt(answers) {
429
+ return `Structure these conversational answers into a .worldmodel.md file.
430
+
431
+ The file MUST follow this exact three-layer format:
432
+
433
+ ---
434
+ name: ${answers.name}
435
+ version: 1.0.0
436
+ ---
437
+
438
+ # Core Model Geometry
439
+
440
+ ## Mission
441
+ (from the mission answer)
442
+
443
+ ## Domains
444
+ (2-4 domains from the domains answer, each with:)
445
+ ### Domain Name
446
+ #### Skills (8-10 skills per domain, inferred from the answers)
447
+ #### Values (3-4 values per domain, drawn from the non-negotiables + mission)
448
+
449
+ ## Overlap Effects
450
+ (from the overlaps answer, formatted as: Domain A + Domain B = Emergent State)
451
+
452
+ ## Center Identity
453
+ (from the center answer)
454
+
455
+ # Contextual Modifiers
456
+
457
+ ## Authority Layers
458
+ (infer 4-5 authority levels appropriate to this organization)
459
+
460
+ ## Spatial Contexts
461
+ (infer 4-5 contexts where behavior happens)
462
+
463
+ ## Interpretation Rules
464
+ (infer 3-5 rules about how context changes meaning)
465
+
466
+ # Evolution Layer
467
+
468
+ ## Aligned Behaviors
469
+ (from the success answer, expanded to 5-8 items)
470
+
471
+ ## Drift Behaviors
472
+ (from the drift answer, expanded to 5-8 items)
473
+
474
+ ## Signals
475
+ (infer 5-7 observable signals from the answers, snake_case)
476
+
477
+ ## Decision Priorities
478
+ (from the priorities answer, formatted as: preferred > secondary)
479
+
480
+ ## Evolution Conditions
481
+ (infer 3-5 conditions for when the model should adapt)
482
+
483
+ HERE ARE THE ANSWERS:
484
+
485
+ Name: ${answers.name}
486
+
487
+ Mission: ${answers.mission}
488
+
489
+ Domains: ${answers.domains}
490
+
491
+ Overlaps: ${answers.overlaps}
492
+
493
+ Center identity: ${answers.center}
494
+
495
+ Non-negotiables: ${answers.nonnegotiables}
496
+
497
+ Success looks like: ${answers.success}
498
+
499
+ Drift looks like: ${answers.drift}
500
+
501
+ Priorities: ${answers.priorities}
502
+
503
+ Output ONLY the .worldmodel.md content. No explanation. No commentary. Just the file.`;
504
+ }
505
+ function saveWorldmodel(content, outputPath) {
506
+ const resolved = (0, import_path.resolve)(outputPath);
507
+ (0, import_fs.writeFileSync)(resolved, content, "utf-8");
508
+ return resolved;
509
+ }
510
+ var import_readline, import_fs, import_path, QUESTIONS;
511
+ var init_worldmodel_create = __esm({
512
+ "src/cli/worldmodel-create.ts"() {
513
+ "use strict";
514
+ import_readline = require("readline");
515
+ import_fs = require("fs");
516
+ import_path = require("path");
517
+ QUESTIONS = [
518
+ {
519
+ id: "name",
520
+ question: "What should we call this model? (Your name, your org, your project)",
521
+ placeholder: 'e.g., "Kirsten", "Auki", "My Startup"'
522
+ },
523
+ {
524
+ id: "mission",
525
+ question: "In one or two sentences \u2014 what does this system exist to do? Not a slogan. The real purpose.",
526
+ placeholder: 'e.g., "Protect human thinking while expanding cognitive capability through AI"'
527
+ },
528
+ {
529
+ id: "domains",
530
+ question: "What are the 2-3 big areas of focus? Not departments \u2014 the major kinds of work that matter most. Separate with commas.",
531
+ placeholder: 'e.g., "Safety and boundaries, Individual authority, AI as cognitive extension"'
532
+ },
533
+ {
534
+ id: "overlaps",
535
+ question: "When those areas work well TOGETHER, what does that feel like? Name a feeling for each pair.",
536
+ placeholder: 'e.g., "Safety + Authority = Trust, Authority + AI = Possibility"'
537
+ },
538
+ {
539
+ id: "center",
540
+ question: "When EVERYTHING is aligned \u2014 all areas working together \u2014 what does the system become? One name or phrase.",
541
+ placeholder: 'e.g., "The Sovereign Conduit", "Collective Vanguard Leader"'
542
+ },
543
+ {
544
+ id: "nonnegotiables",
545
+ question: "What's absolutely non-negotiable? What would you walk away over? List a few.",
546
+ placeholder: 'e.g., "Humans retain authority over thinking. AI extends, never replaces. People can always leave."'
547
+ },
548
+ {
549
+ id: "success",
550
+ question: `What does success look like in action? What would you point at and say "that's what I mean"?`,
551
+ placeholder: 'e.g., "Someone maintaining clear authorship of decisions even when AI contributed"'
552
+ },
553
+ {
554
+ id: "drift",
555
+ question: "What does drift look like? What would worry you if you saw it happening?",
556
+ placeholder: 'e.g., "Decision ownership quietly shifting to AI without explicit delegation"'
557
+ },
558
+ {
559
+ id: "priorities",
560
+ question: 'When tradeoffs appear, what wins? Give a few "X over Y" pairs.',
561
+ placeholder: 'e.g., "Safety over speed, sovereignty over convenience, diversity over uniformity"'
546
562
  }
547
- }
563
+ ];
548
564
  }
549
- if (geometry.overlapEffects.length === 0) {
550
- issues.push({
551
- line: overlapsSection?.startLine ?? section.startLine,
552
- section: "Overlap Effects",
553
- message: 'No overlap effects defined. Define what emerges when two domains interact well (e.g., "Domain A + Domain B = Inspiration").',
554
- severity: "warning"
555
- });
565
+ });
566
+
567
+ // src/cli/worldmodel.ts
568
+ var worldmodel_exports = {};
569
+ __export(worldmodel_exports, {
570
+ main: () => main
571
+ });
572
+ module.exports = __toCommonJS(worldmodel_exports);
573
+ var import_promises = require("fs/promises");
574
+ var import_fs2 = require("fs");
575
+ var import_path2 = require("path");
576
+
577
+ // src/engine/worldmodel-parser.ts
578
+ function splitSections(markdown) {
579
+ const lines = markdown.split("\n");
580
+ let frontmatter = "";
581
+ let bodyStart = 0;
582
+ if (lines[0]?.trim() === "---") {
583
+ const endIdx = lines.indexOf("---", 1);
584
+ if (endIdx > 0) {
585
+ frontmatter = lines.slice(1, endIdx).join("\n");
586
+ bodyStart = endIdx + 1;
587
+ }
556
588
  }
557
- const identitySection = h2Sections.find(
558
- (s) => s.name.toLowerCase() === "center identity"
559
- );
560
- if (identitySection) {
561
- geometry.centerIdentity = extractTextContent(identitySection.content);
589
+ const sections = [];
590
+ let currentSection = null;
591
+ const contentLines = [];
592
+ for (let i = bodyStart; i < lines.length; i++) {
593
+ const line = lines[i];
594
+ if (line.startsWith("# ")) {
595
+ if (currentSection) {
596
+ currentSection.content = contentLines.join("\n").trim();
597
+ sections.push(currentSection);
598
+ contentLines.length = 0;
599
+ }
600
+ currentSection = {
601
+ name: line.replace(/^#\s+/, "").trim(),
602
+ content: "",
603
+ startLine: i + 1
604
+ // 1-based
605
+ };
606
+ } else if (currentSection) {
607
+ contentLines.push(line);
608
+ }
562
609
  }
563
- if (!geometry.centerIdentity) {
564
- issues.push({
565
- line: identitySection?.startLine ?? section.startLine,
566
- section: "Center Identity",
567
- message: "No center identity defined. Define what the system becomes when all domains are aligned \u2014 the core identity.",
568
- severity: "warning"
569
- });
610
+ if (currentSection) {
611
+ currentSection.content = contentLines.join("\n").trim();
612
+ sections.push(currentSection);
570
613
  }
571
- return geometry;
614
+ return { frontmatter, sections };
572
615
  }
573
- function parseModifiers(section, issues) {
574
- const modifiers = {
575
- authorityLayers: [],
576
- spatialContexts: [],
577
- interpretationRules: []
578
- };
579
- if (!section) {
580
- issues.push({
581
- line: 0,
582
- section: "Contextual Modifiers",
583
- message: "Missing # Contextual Modifiers section. Define how authority, role, and spatial context change how behavior is interpreted.",
584
- severity: "warning"
585
- });
586
- return modifiers;
587
- }
588
- const h2Sections = splitH2Sections(section.content, section.startLine);
589
- const authoritySection = h2Sections.find(
590
- (s) => s.name.toLowerCase() === "authority layers"
591
- );
592
- if (authoritySection) {
593
- modifiers.authorityLayers = parseBulletList(authoritySection.content);
594
- }
595
- const spatialSection = h2Sections.find(
596
- (s) => s.name.toLowerCase() === "spatial contexts"
597
- );
598
- if (spatialSection) {
599
- modifiers.spatialContexts = parseBulletList(spatialSection.content);
616
+ function splitH2Sections(content, baseLine) {
617
+ const lines = content.split("\n");
618
+ const sections = [];
619
+ let currentSection = null;
620
+ const contentLines = [];
621
+ for (let i = 0; i < lines.length; i++) {
622
+ const line = lines[i];
623
+ if (line.startsWith("## ")) {
624
+ if (currentSection) {
625
+ currentSection.content = contentLines.join("\n").trim();
626
+ sections.push(currentSection);
627
+ contentLines.length = 0;
628
+ }
629
+ currentSection = {
630
+ name: line.replace(/^##\s+/, "").trim(),
631
+ content: "",
632
+ startLine: baseLine + i
633
+ };
634
+ } else if (currentSection) {
635
+ contentLines.push(line);
636
+ }
600
637
  }
601
- const rulesSection = h2Sections.find(
602
- (s) => s.name.toLowerCase() === "interpretation rules"
603
- );
604
- if (rulesSection) {
605
- modifiers.interpretationRules = parseBulletList(rulesSection.content);
638
+ if (currentSection) {
639
+ currentSection.content = contentLines.join("\n").trim();
640
+ sections.push(currentSection);
606
641
  }
607
- return modifiers;
642
+ return sections;
608
643
  }
609
- function parseEvolution(section, issues) {
610
- const evolution = {
611
- alignedBehaviors: [],
612
- driftBehaviors: [],
613
- signals: [],
614
- decisionPriorities: [],
615
- evolutionConditions: []
616
- };
617
- if (!section) {
618
- issues.push({
619
- line: 0,
620
- section: "Evolution Layer",
621
- message: "Missing # Evolution Layer section. Define observable behaviors, signals, decision priorities, and adaptation conditions.",
622
- severity: "error"
623
- });
624
- return evolution;
625
- }
626
- const h2Sections = splitH2Sections(section.content, section.startLine);
627
- const alignedSection = h2Sections.find(
628
- (s) => s.name.toLowerCase() === "aligned behaviors"
629
- );
630
- if (alignedSection) {
631
- evolution.alignedBehaviors = parseBulletList(alignedSection.content);
632
- }
633
- if (evolution.alignedBehaviors.length === 0) {
634
- issues.push({
635
- line: alignedSection?.startLine ?? section.startLine,
636
- section: "Aligned Behaviors",
637
- message: "No aligned behaviors defined. Define what success looks like in action.",
638
- severity: "warning"
639
- });
640
- }
641
- const driftSection = h2Sections.find(
642
- (s) => s.name.toLowerCase() === "drift behaviors"
643
- );
644
- if (driftSection) {
645
- evolution.driftBehaviors = parseBulletList(driftSection.content);
646
- }
647
- if (evolution.driftBehaviors.length === 0) {
648
- issues.push({
649
- line: driftSection?.startLine ?? section.startLine,
650
- section: "Drift Behaviors",
651
- message: "No drift behaviors defined. Define what misalignment looks like so the system can detect behavioral drift over time.",
652
- severity: "warning"
653
- });
644
+ function splitH3Sections(content, baseLine) {
645
+ const lines = content.split("\n");
646
+ const sections = [];
647
+ let currentSection = null;
648
+ const contentLines = [];
649
+ for (let i = 0; i < lines.length; i++) {
650
+ const line = lines[i];
651
+ if (line.startsWith("### ")) {
652
+ if (currentSection) {
653
+ currentSection.content = contentLines.join("\n").trim();
654
+ sections.push(currentSection);
655
+ contentLines.length = 0;
656
+ }
657
+ currentSection = {
658
+ name: line.replace(/^###\s+/, "").trim(),
659
+ content: "",
660
+ startLine: baseLine + i
661
+ };
662
+ } else if (currentSection) {
663
+ contentLines.push(line);
664
+ }
654
665
  }
655
- const signalsSection = h2Sections.find(
656
- (s) => s.name.toLowerCase() === "signals"
657
- );
658
- if (signalsSection) {
659
- evolution.signals = parseBulletList(signalsSection.content);
666
+ if (currentSection) {
667
+ currentSection.content = contentLines.join("\n").trim();
668
+ sections.push(currentSection);
660
669
  }
661
- if (evolution.signals.length < 2) {
662
- issues.push({
663
- line: signalsSection?.startLine ?? section.startLine,
664
- section: "Signals",
665
- message: "At least 2 signals required. Signals are the observable metrics for detecting alignment or drift.",
666
- severity: "error"
667
- });
670
+ return sections;
671
+ }
672
+ function splitH4Sections(content, baseLine) {
673
+ const lines = content.split("\n");
674
+ const sections = [];
675
+ let currentSection = null;
676
+ const contentLines = [];
677
+ for (let i = 0; i < lines.length; i++) {
678
+ const line = lines[i];
679
+ if (line.startsWith("#### ")) {
680
+ if (currentSection) {
681
+ currentSection.content = contentLines.join("\n").trim();
682
+ sections.push(currentSection);
683
+ contentLines.length = 0;
684
+ }
685
+ currentSection = {
686
+ name: line.replace(/^####\s+/, "").trim(),
687
+ content: "",
688
+ startLine: baseLine + i
689
+ };
690
+ } else if (currentSection) {
691
+ contentLines.push(line);
692
+ }
668
693
  }
669
- const prioritiesSection = h2Sections.find(
670
- (s) => s.name.toLowerCase() === "decision priorities"
671
- );
672
- if (prioritiesSection) {
673
- const bullets = parseBulletList(prioritiesSection.content);
674
- for (let i = 0; i < bullets.length; i++) {
675
- const bullet = bullets[i];
676
- const match = bullet.match(/^(.+?)\s*>\s*(.+)$/);
677
- if (match) {
678
- evolution.decisionPriorities.push({
679
- preferred: match[1].trim(),
680
- over: match[2].trim(),
681
- line: prioritiesSection.startLine + i
682
- });
683
- } else {
684
- issues.push({
685
- line: prioritiesSection.startLine + i,
686
- section: "Decision Priorities",
687
- message: `Cannot parse priority: '${bullet}'. Expected format: 'preferred > over'.`,
688
- severity: "warning"
689
- });
694
+ if (currentSection) {
695
+ currentSection.content = contentLines.join("\n").trim();
696
+ sections.push(currentSection);
697
+ }
698
+ return sections;
699
+ }
700
+ function parseBulletList(content) {
701
+ const items = [];
702
+ let inComment = false;
703
+ for (const line of content.split("\n")) {
704
+ const trimmed = line.trim();
705
+ if (trimmed.startsWith("<!--")) {
706
+ inComment = true;
707
+ }
708
+ if (inComment) {
709
+ if (trimmed.includes("-->")) {
710
+ inComment = false;
690
711
  }
712
+ continue;
713
+ }
714
+ if (trimmed.startsWith("- ")) {
715
+ items.push(trimmed.slice(2).trim());
691
716
  }
692
717
  }
693
- if (evolution.decisionPriorities.length === 0) {
694
- issues.push({
695
- line: prioritiesSection?.startLine ?? section.startLine,
696
- section: "Decision Priorities",
697
- message: "No decision priorities defined. Define what wins when tradeoffs appear.",
698
- severity: "warning"
699
- });
700
- }
701
- const evoSection = h2Sections.find(
702
- (s) => s.name.toLowerCase() === "evolution conditions"
703
- );
704
- if (evoSection) {
705
- evolution.evolutionConditions = parseBulletList(evoSection.content);
706
- }
707
- if (evolution.signals.length > 0 && evolution.driftBehaviors.length === 0) {
708
- issues.push({
709
- line: signalsSection?.startLine ?? section.startLine,
710
- section: "Evolution Layer",
711
- message: "Signals defined but no drift behaviors. System cannot detect failure without drift definitions.",
712
- severity: "warning"
713
- });
718
+ return items;
719
+ }
720
+ function extractTextContent(content) {
721
+ const lines = content.split("\n");
722
+ const textLines = [];
723
+ let inComment = false;
724
+ for (const line of lines) {
725
+ const trimmed = line.trim();
726
+ if (trimmed.startsWith("<!--")) {
727
+ inComment = true;
728
+ }
729
+ if (inComment) {
730
+ if (trimmed.includes("-->")) {
731
+ inComment = false;
732
+ }
733
+ continue;
734
+ }
735
+ if (trimmed && !trimmed.startsWith("#")) {
736
+ textLines.push(trimmed);
737
+ }
714
738
  }
715
- return evolution;
739
+ return textLines.join("\n").trim();
716
740
  }
717
- function parseWorldModel(markdown) {
718
- const issues = [];
719
- if (!markdown || !markdown.trim()) {
720
- issues.push({
721
- line: 0,
722
- section: "file",
723
- message: "Empty input. Provide a .worldmodel.md file with three layers: Core Model Geometry, Contextual Modifiers, Evolution Layer.",
724
- severity: "error"
725
- });
726
- return { model: null, issues };
741
+ function toKebabCase(name) {
742
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
743
+ }
744
+ function parseFrontmatter(yaml, issues) {
745
+ const result = {};
746
+ for (const line of yaml.split("\n")) {
747
+ const trimmed = line.trim();
748
+ if (!trimmed || trimmed.startsWith("#")) continue;
749
+ const colonIdx = trimmed.indexOf(":");
750
+ if (colonIdx === -1) continue;
751
+ const key = trimmed.slice(0, colonIdx).trim();
752
+ const value = trimmed.slice(colonIdx + 1).trim();
753
+ result[key] = value;
727
754
  }
728
- const { frontmatter: fmRaw, sections } = splitSections(markdown);
729
- const frontmatter = parseFrontmatter(fmRaw, issues);
730
- const geometrySection = sections.find(
731
- (s) => s.name.toLowerCase() === "core model geometry"
732
- );
733
- const modifiersSection = sections.find(
734
- (s) => s.name.toLowerCase() === "contextual modifiers"
735
- );
736
- const evolutionSection = sections.find(
737
- (s) => s.name.toLowerCase() === "evolution layer"
738
- );
739
- const geometry = parseGeometry(geometrySection, issues);
740
- const modifiers = parseModifiers(modifiersSection, issues);
741
- const evolution = parseEvolution(evolutionSection, issues);
742
- if (geometry.overlapEffects.length > 0 && !geometry.centerIdentity) {
755
+ const name = result.name || "";
756
+ if (!name) {
743
757
  issues.push({
744
- line: 0,
745
- section: "Core Model Geometry",
746
- message: "Overlaps defined but no center identity. System lacks coherence without an aligned identity.",
747
- severity: "warning"
758
+ line: 1,
759
+ section: "frontmatter",
760
+ message: "Missing name in frontmatter. Provide a human-readable model name.",
761
+ severity: "error"
748
762
  });
749
763
  }
750
- const hasErrors = issues.some((i) => i.severity === "error");
751
- if (hasErrors) {
752
- return {
753
- model: {
754
- frontmatter,
755
- geometry,
756
- modifiers,
757
- evolution
758
- },
759
- issues
760
- };
761
- }
764
+ const model_id = result.model_id || toKebabCase(name);
762
765
  return {
763
- model: {
764
- frontmatter,
765
- geometry,
766
- modifiers,
767
- evolution
768
- },
769
- issues
766
+ model_id,
767
+ name,
768
+ version: result.version || "1.0.0"
770
769
  };
771
770
  }
772
-
773
- // src/engine/worldmodel-compiler.ts
774
- function toSnakeCase(text) {
775
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
776
- }
777
- function toKebabCase2(text) {
778
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
779
- }
780
- function titleCase(text) {
781
- return text.split(/[\s_-]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
782
- }
783
- function matchSignal(behaviorText, signals) {
784
- const lowerText = behaviorText.toLowerCase();
785
- for (const signal of signals) {
786
- const signalWords = signal.toLowerCase().split("_");
787
- if (signalWords.some((w) => w.length > 3 && lowerText.includes(w))) {
788
- return signal;
789
- }
790
- }
791
- return "alignment_score";
792
- }
793
- function emitWorldMarkdown(model) {
794
- const lines = [];
795
- const worldId = model.frontmatter.model_id;
796
- const worldName = model.frontmatter.name;
797
- const version = model.frontmatter.version;
798
- lines.push("---");
799
- lines.push(`world_id: ${worldId}`);
800
- lines.push(`name: ${worldName}`);
801
- lines.push(`version: ${version}`);
802
- lines.push("runtime_mode: COMPLIANCE");
803
- lines.push("default_profile: aligned");
804
- lines.push("alternative_profile: drifting");
805
- lines.push("---");
806
- lines.push("");
807
- lines.push("# Thesis");
808
- lines.push("");
809
- lines.push(model.geometry.mission);
810
- if (model.geometry.centerIdentity) {
811
- lines.push(
812
- `When all domains are aligned, the system operates as: ${model.geometry.centerIdentity}.`
813
- );
814
- }
815
- lines.push("");
816
- lines.push("# Invariants");
817
- lines.push("");
818
- let invariantIdx = 0;
819
- for (const domain of model.geometry.domains) {
820
- for (const value of domain.values) {
821
- invariantIdx++;
822
- const invId = `${toSnakeCase(domain.name)}_value_${String(invariantIdx).padStart(2, "0")}`;
823
- lines.push(
824
- `- \`${invId}\` \u2014 ${value} [${domain.name}] (structural, immutable)`
825
- );
826
- }
827
- }
828
- lines.push("");
829
- lines.push("# State");
830
- lines.push("");
831
- lines.push("## alignment_score");
832
- lines.push("- type: number");
833
- lines.push("- min: 0");
834
- lines.push("- max: 100");
835
- lines.push("- step: 5");
836
- lines.push("- default: 70");
837
- lines.push("- label: Alignment Score");
838
- lines.push(
839
- "- description: Composite behavioral alignment metric derived from all signals"
840
- );
841
- lines.push("");
842
- for (const signal of model.evolution.signals) {
843
- const signalId = toSnakeCase(signal);
844
- const signalLabel = titleCase(signal);
845
- lines.push(`## ${signalId}`);
846
- lines.push("- type: number");
847
- lines.push("- min: 0");
848
- lines.push("- max: 100");
849
- lines.push("- step: 5");
850
- lines.push("- default: 70");
851
- lines.push(`- label: ${signalLabel}`);
852
- lines.push(`- description: Behavioral signal measuring ${signal.replace(/_/g, " ")}`);
853
- lines.push("");
854
- }
855
- lines.push("# Assumptions");
856
- lines.push("");
857
- lines.push("## aligned");
858
- lines.push("- name: Aligned");
859
- lines.push("- description: All behavioral signals at healthy levels");
860
- lines.push("- pressure_level: low");
861
- lines.push("");
862
- lines.push("## drifting");
863
- lines.push("- name: Drifting");
864
- lines.push("- description: Behavioral signals under pressure with drift risk");
865
- lines.push("- pressure_level: high");
866
- lines.push("");
867
- lines.push("# Rules");
868
- lines.push("");
869
- let ruleIdx = 0;
870
- for (const drift of model.evolution.driftBehaviors) {
871
- ruleIdx++;
872
- const ruleId = `rule-${String(ruleIdx).padStart(3, "0")}`;
873
- const matchedSignal = matchSignal(drift, model.evolution.signals);
874
- const signalId = toSnakeCase(matchedSignal);
875
- lines.push(`## ${ruleId}: ${drift} (degradation)`);
876
- lines.push(`Drift behavior detected: ${drift}`);
877
- lines.push("");
878
- lines.push(`When ${signalId} < 50 [state]`);
879
- lines.push("Then alignment_score *= 0.80");
880
- lines.push("");
881
- lines.push(`> trigger: ${signalId} drops below threshold`);
882
- lines.push(`> rule: Drift behavior weakens alignment`);
883
- lines.push(`> shift: Behavioral alignment decreases`);
884
- lines.push(`> effect: Alignment score reduced by 20%`);
885
- lines.push("");
771
+ function parseGeometry(section, issues) {
772
+ const geometry = {
773
+ mission: "",
774
+ domains: [],
775
+ overlapEffects: [],
776
+ centerIdentity: ""
777
+ };
778
+ if (!section) {
779
+ issues.push({
780
+ line: 0,
781
+ section: "Core Model Geometry",
782
+ message: "Missing # Core Model Geometry section. Define the structural model: mission, domains with embedded skills and values, overlaps, and center identity.",
783
+ severity: "error"
784
+ });
785
+ return geometry;
886
786
  }
887
- for (const aligned of model.evolution.alignedBehaviors) {
888
- ruleIdx++;
889
- const ruleId = `rule-${String(ruleIdx).padStart(3, "0")}`;
890
- const matchedSignal = matchSignal(aligned, model.evolution.signals);
891
- const signalId = toSnakeCase(matchedSignal);
892
- lines.push(`## ${ruleId}: ${aligned} (advantage)`);
893
- lines.push(`Aligned behavior reinforced: ${aligned}`);
894
- lines.push("");
895
- lines.push(`When ${signalId} >= 70 [state]`);
896
- lines.push("Then alignment_score *= 1.10");
897
- lines.push("");
898
- lines.push(`> trigger: ${signalId} above healthy threshold`);
899
- lines.push(`> rule: Aligned behavior strengthens system`);
900
- lines.push(`> shift: Behavioral alignment increases`);
901
- lines.push(`> effect: Alignment score boosted by 10%`);
902
- lines.push("");
787
+ const h2Sections = splitH2Sections(section.content, section.startLine);
788
+ const missionSection = h2Sections.find((s) => s.name.toLowerCase() === "mission");
789
+ if (missionSection) {
790
+ geometry.mission = extractTextContent(missionSection.content);
903
791
  }
904
- for (let i = 0; i < model.evolution.decisionPriorities.length; i++) {
905
- ruleIdx++;
906
- const priority = model.evolution.decisionPriorities[i];
907
- const ruleId = `rule-${String(ruleIdx).padStart(3, "0")}`;
908
- lines.push(
909
- `## ${ruleId}: ${priority.preferred} over ${priority.over} (structural)`
910
- );
911
- lines.push(
912
- `Priority: ${priority.preferred} takes precedence over ${priority.over} in tradeoff situations.`
913
- );
914
- lines.push("");
915
- lines.push("When alignment_score < 40 [state]");
916
- lines.push("Then alignment_score *= 0.70");
917
- lines.push("Collapse: alignment_score < 10");
918
- lines.push("");
919
- lines.push(`> trigger: Alignment score critically low`);
920
- lines.push(
921
- `> rule: Priority violation \u2014 ${priority.preferred} must outweigh ${priority.over}`
922
- );
923
- lines.push(`> shift: System enters structural enforcement`);
924
- lines.push(`> effect: Alignment sharply reduced; collapse if critical`);
925
- lines.push("");
792
+ if (!geometry.mission) {
793
+ issues.push({
794
+ line: missionSection?.startLine ?? section.startLine,
795
+ section: "Mission",
796
+ message: "Missing ## Mission. Define what this system is trying to achieve \u2014 the core aim, not a slogan.",
797
+ severity: "error"
798
+ });
926
799
  }
927
- lines.push("# Gates");
928
- lines.push("");
929
- lines.push("- STRONG: alignment_score >= 85");
930
- lines.push("- STABLE: alignment_score >= 65");
931
- lines.push("- WATCHING: alignment_score >= 45");
932
- lines.push("- FRAGILE: alignment_score > 30");
933
- lines.push("- MISALIGNED: alignment_score <= 30");
934
- lines.push("");
935
- lines.push("# Outcomes");
936
- lines.push("");
937
- lines.push("## alignment_score");
938
- lines.push("- type: number");
939
- lines.push("- range: 0-100");
940
- lines.push("- display: percentage");
941
- lines.push("- label: Alignment Score");
942
- lines.push("- primary: true");
943
- lines.push("");
944
- for (const signal of model.evolution.signals) {
945
- const signalId = toSnakeCase(signal);
946
- const signalLabel = titleCase(signal);
947
- lines.push(`## ${signalId}`);
948
- lines.push("- type: number");
949
- lines.push("- range: 0-100");
950
- lines.push("- display: percentage");
951
- lines.push(`- label: ${signalLabel}`);
952
- lines.push("");
800
+ const domainsSection = h2Sections.find((s) => s.name.toLowerCase() === "domains");
801
+ if (domainsSection) {
802
+ const domainSections = splitH3Sections(domainsSection.content, domainsSection.startLine);
803
+ for (const ds of domainSections) {
804
+ const domain = {
805
+ id: toKebabCase(ds.name),
806
+ name: ds.name,
807
+ skills: [],
808
+ values: [],
809
+ line: ds.startLine
810
+ };
811
+ const h4Sections = splitH4Sections(ds.content, ds.startLine);
812
+ const skillsH4 = h4Sections.find((s) => s.name.toLowerCase() === "skills");
813
+ if (skillsH4) {
814
+ domain.skills = parseBulletList(skillsH4.content);
815
+ }
816
+ const valuesH4 = h4Sections.find((s) => s.name.toLowerCase() === "values");
817
+ if (valuesH4) {
818
+ domain.values = parseBulletList(valuesH4.content);
819
+ }
820
+ if (domain.skills.length === 0) {
821
+ issues.push({
822
+ line: ds.startLine,
823
+ section: "Domains",
824
+ message: `Domain '${ds.name}' has no skills defined. Skills are the capabilities within this domain.`,
825
+ severity: "warning"
826
+ });
827
+ }
828
+ if (domain.values.length === 0) {
829
+ issues.push({
830
+ line: ds.startLine,
831
+ section: "Domains",
832
+ message: `Domain '${ds.name}' has no values defined. Behavior has no constraints without values.`,
833
+ severity: "warning"
834
+ });
835
+ }
836
+ geometry.domains.push(domain);
837
+ }
953
838
  }
954
- const lensSuggestions = buildLensSuggestions(model);
955
- if (lensSuggestions.length > 0) {
956
- lines.push("# Lenses");
957
- lines.push("- policy: role_default");
958
- lines.push("");
959
- for (const lens of lensSuggestions) {
960
- lines.push(`## ${lens.id}`);
961
- lines.push(`- tagline: ${lens.tagline}`);
962
- lines.push(
963
- `- description: Lens derived from ${lens.derived_from.domainA} and ${lens.derived_from.domainB} interaction, producing ${lens.derived_from.effect}.`
964
- );
965
- lines.push(`- formality: ${lens.tone.formality}`);
966
- lines.push(`- verbosity: ${lens.tone.verbosity}`);
967
- lines.push(`- emotion: ${lens.tone.emotion}`);
968
- lines.push(`- confidence: ${lens.tone.confidence}`);
969
- lines.push("- tags: behavioral, worldmodel, overlap");
970
- lines.push("- default_for_roles: all");
971
- lines.push("- priority: 50");
972
- lines.push("- stackable: true");
973
- lines.push("");
974
- for (const directive of lens.directives) {
975
- lines.push(`> ${directive.scope}: ${directive.instruction}`);
839
+ if (geometry.domains.length < 2) {
840
+ issues.push({
841
+ line: domainsSection?.startLine ?? section.startLine,
842
+ section: "Domains",
843
+ message: "At least 2 domains required. Domains are the major operating modes of the system, each carrying skills and values.",
844
+ severity: "error"
845
+ });
846
+ }
847
+ const overlapsSection = h2Sections.find(
848
+ (s) => s.name.toLowerCase() === "overlap effects"
849
+ );
850
+ if (overlapsSection) {
851
+ const bullets = parseBulletList(overlapsSection.content);
852
+ const domainNames = new Set(geometry.domains.map((d) => d.name.toLowerCase()));
853
+ for (let i = 0; i < bullets.length; i++) {
854
+ const bullet = bullets[i];
855
+ const match = bullet.match(/^(.+?)\s*\+\s*(.+?)\s*=\s*(.+)$/);
856
+ if (match) {
857
+ const domainA = match[1].trim();
858
+ const domainB = match[2].trim();
859
+ const effect = match[3].trim();
860
+ if (!domainNames.has(domainA.toLowerCase())) {
861
+ issues.push({
862
+ line: overlapsSection.startLine + i,
863
+ section: "Overlap Effects",
864
+ message: `Overlap references unknown domain '${domainA}'. Must reference a declared domain.`,
865
+ severity: "warning"
866
+ });
867
+ }
868
+ if (!domainNames.has(domainB.toLowerCase())) {
869
+ issues.push({
870
+ line: overlapsSection.startLine + i,
871
+ section: "Overlap Effects",
872
+ message: `Overlap references unknown domain '${domainB}'. Must reference a declared domain.`,
873
+ severity: "warning"
874
+ });
875
+ }
876
+ geometry.overlapEffects.push({
877
+ domainA,
878
+ domainB,
879
+ effect,
880
+ line: overlapsSection.startLine + i
881
+ });
882
+ } else {
883
+ issues.push({
884
+ line: overlapsSection.startLine + i,
885
+ section: "Overlap Effects",
886
+ message: `Cannot parse overlap: '${bullet}'. Expected format: 'Domain A + Domain B = Emergent State'.`,
887
+ severity: "warning"
888
+ });
976
889
  }
977
- lines.push("");
978
890
  }
979
891
  }
980
- return lines.join("\n");
892
+ if (geometry.overlapEffects.length === 0) {
893
+ issues.push({
894
+ line: overlapsSection?.startLine ?? section.startLine,
895
+ section: "Overlap Effects",
896
+ message: 'No overlap effects defined. Define what emerges when two domains interact well (e.g., "Domain A + Domain B = Inspiration").',
897
+ severity: "warning"
898
+ });
899
+ }
900
+ const identitySection = h2Sections.find(
901
+ (s) => s.name.toLowerCase() === "center identity"
902
+ );
903
+ if (identitySection) {
904
+ geometry.centerIdentity = extractTextContent(identitySection.content);
905
+ }
906
+ if (!geometry.centerIdentity) {
907
+ issues.push({
908
+ line: identitySection?.startLine ?? section.startLine,
909
+ section: "Center Identity",
910
+ message: "No center identity defined. Define what the system becomes when all domains are aligned \u2014 the core identity.",
911
+ severity: "warning"
912
+ });
913
+ }
914
+ return geometry;
981
915
  }
982
- function emitSignalSchema(model) {
983
- const signals = model.evolution.signals.map((signal) => ({
984
- id: toSnakeCase(signal),
985
- name: titleCase(signal),
986
- type: "number",
987
- default: 70
988
- }));
989
- return {
990
- model_id: model.frontmatter.model_id,
991
- signals
916
+ function parseModifiers(section, issues) {
917
+ const modifiers = {
918
+ authorityLayers: [],
919
+ spatialContexts: [],
920
+ interpretationRules: []
992
921
  };
993
- }
994
- function emitOverlapMap(model) {
995
- const pairings = model.geometry.overlapEffects.map((o) => ({
996
- domainA: o.domainA,
997
- domainB: o.domainB,
998
- effect: o.effect
999
- }));
1000
- const matrix = {};
1001
- for (const overlap of model.geometry.overlapEffects) {
1002
- const keyA = toKebabCase2(overlap.domainA);
1003
- const keyB = toKebabCase2(overlap.domainB);
1004
- if (!matrix[keyA]) matrix[keyA] = {};
1005
- matrix[keyA][keyB] = overlap.effect;
922
+ if (!section) {
923
+ issues.push({
924
+ line: 0,
925
+ section: "Contextual Modifiers",
926
+ message: "Missing # Contextual Modifiers section. Define how authority, role, and spatial context change how behavior is interpreted.",
927
+ severity: "warning"
928
+ });
929
+ return modifiers;
930
+ }
931
+ const h2Sections = splitH2Sections(section.content, section.startLine);
932
+ const authoritySection = h2Sections.find(
933
+ (s) => s.name.toLowerCase() === "authority layers"
934
+ );
935
+ if (authoritySection) {
936
+ modifiers.authorityLayers = parseBulletList(authoritySection.content);
1006
937
  }
1007
- return {
1008
- model_id: model.frontmatter.model_id,
1009
- pairings,
1010
- matrix
1011
- };
938
+ const spatialSection = h2Sections.find(
939
+ (s) => s.name.toLowerCase() === "spatial contexts"
940
+ );
941
+ if (spatialSection) {
942
+ modifiers.spatialContexts = parseBulletList(spatialSection.content);
943
+ }
944
+ const rulesSection = h2Sections.find(
945
+ (s) => s.name.toLowerCase() === "interpretation rules"
946
+ );
947
+ if (rulesSection) {
948
+ modifiers.interpretationRules = parseBulletList(rulesSection.content);
949
+ }
950
+ return modifiers;
1012
951
  }
1013
- function emitContextsConfig(model) {
1014
- return {
1015
- model_id: model.frontmatter.model_id,
1016
- authority_layers: model.modifiers.authorityLayers,
1017
- spatial_contexts: model.modifiers.spatialContexts,
1018
- interpretation_rules: model.modifiers.interpretationRules
952
+ function parseEvolution(section, issues) {
953
+ const evolution = {
954
+ alignedBehaviors: [],
955
+ driftBehaviors: [],
956
+ signals: [],
957
+ decisionPriorities: [],
958
+ evolutionConditions: []
1019
959
  };
1020
- }
1021
- function deriveTone(domainA, domainB) {
1022
- const combined = `${domainA} ${domainB}`.toLowerCase();
1023
- let formality = "neutral";
1024
- if (/strateg|technic|analytic|research|engineer/.test(combined)) {
1025
- formality = "professional";
1026
- } else if (/narrat|story|communi|creative/.test(combined)) {
1027
- formality = "casual";
960
+ if (!section) {
961
+ issues.push({
962
+ line: 0,
963
+ section: "Evolution Layer",
964
+ message: "Missing # Evolution Layer section. Define observable behaviors, signals, decision priorities, and adaptation conditions.",
965
+ severity: "error"
966
+ });
967
+ return evolution;
1028
968
  }
1029
- let verbosity = "balanced";
1030
- if (/foresight|scenario|plan|design/.test(combined)) {
1031
- verbosity = "detailed";
1032
- } else if (/prosper|negotiat|stakeholder/.test(combined)) {
1033
- verbosity = "concise";
969
+ const h2Sections = splitH2Sections(section.content, section.startLine);
970
+ const alignedSection = h2Sections.find(
971
+ (s) => s.name.toLowerCase() === "aligned behaviors"
972
+ );
973
+ if (alignedSection) {
974
+ evolution.alignedBehaviors = parseBulletList(alignedSection.content);
1034
975
  }
1035
- let emotion = "neutral";
1036
- if (/empath|emoti|care|safe|trust/.test(combined)) {
1037
- emotion = "warm";
1038
- } else if (/analytic|system|data/.test(combined)) {
1039
- emotion = "clinical";
976
+ if (evolution.alignedBehaviors.length === 0) {
977
+ issues.push({
978
+ line: alignedSection?.startLine ?? section.startLine,
979
+ section: "Aligned Behaviors",
980
+ message: "No aligned behaviors defined. Define what success looks like in action.",
981
+ severity: "warning"
982
+ });
1040
983
  }
1041
- let confidence = "balanced";
1042
- if (/lead|command|decis|strateg/.test(combined)) {
1043
- confidence = "authoritative";
1044
- } else if (/explor|experiment|creat/.test(combined)) {
1045
- confidence = "exploratory";
984
+ const driftSection = h2Sections.find(
985
+ (s) => s.name.toLowerCase() === "drift behaviors"
986
+ );
987
+ if (driftSection) {
988
+ evolution.driftBehaviors = parseBulletList(driftSection.content);
1046
989
  }
1047
- return { formality, verbosity, emotion, confidence };
1048
- }
1049
- function buildLensSuggestions(model) {
1050
- const lenses = [];
1051
- const domainMap = new Map(model.geometry.domains.map((d) => [d.name.toLowerCase(), d]));
1052
- for (const overlap of model.geometry.overlapEffects) {
1053
- const lensId = toKebabCase2(overlap.effect);
1054
- const tone = deriveTone(overlap.domainA, overlap.domainB);
1055
- const domainAData = domainMap.get(overlap.domainA.toLowerCase());
1056
- const domainBData = domainMap.get(overlap.domainB.toLowerCase());
1057
- const directives = [];
1058
- if (domainAData && domainAData.skills.length > 0) {
1059
- directives.push({
1060
- scope: "response_framing",
1061
- instruction: `Approach through the lens of ${domainAData.skills.join(", ").toLowerCase()}.`
1062
- });
1063
- }
1064
- if (domainBData && domainBData.values.length > 0) {
1065
- directives.push({
1066
- scope: "behavior_shaping",
1067
- instruction: `Maintain ${domainBData.values.join(", ").toLowerCase()} in all responses.`
1068
- });
990
+ if (evolution.driftBehaviors.length === 0) {
991
+ issues.push({
992
+ line: driftSection?.startLine ?? section.startLine,
993
+ section: "Drift Behaviors",
994
+ message: "No drift behaviors defined. Define what misalignment looks like so the system can detect behavioral drift over time.",
995
+ severity: "warning"
996
+ });
997
+ }
998
+ const signalsSection = h2Sections.find(
999
+ (s) => s.name.toLowerCase() === "signals"
1000
+ );
1001
+ if (signalsSection) {
1002
+ evolution.signals = parseBulletList(signalsSection.content);
1003
+ }
1004
+ if (evolution.signals.length < 2) {
1005
+ issues.push({
1006
+ line: signalsSection?.startLine ?? section.startLine,
1007
+ section: "Signals",
1008
+ message: "At least 2 signals required. Signals are the observable metrics for detecting alignment or drift.",
1009
+ severity: "error"
1010
+ });
1011
+ }
1012
+ const prioritiesSection = h2Sections.find(
1013
+ (s) => s.name.toLowerCase() === "decision priorities"
1014
+ );
1015
+ if (prioritiesSection) {
1016
+ const bullets = parseBulletList(prioritiesSection.content);
1017
+ for (let i = 0; i < bullets.length; i++) {
1018
+ const bullet = bullets[i];
1019
+ const match = bullet.match(/^(.+?)\s*>\s*(.+)$/);
1020
+ if (match) {
1021
+ evolution.decisionPriorities.push({
1022
+ preferred: match[1].trim(),
1023
+ over: match[2].trim(),
1024
+ line: prioritiesSection.startLine + i
1025
+ });
1026
+ } else {
1027
+ issues.push({
1028
+ line: prioritiesSection.startLine + i,
1029
+ section: "Decision Priorities",
1030
+ message: `Cannot parse priority: '${bullet}'. Expected format: 'preferred > over'.`,
1031
+ severity: "warning"
1032
+ });
1033
+ }
1069
1034
  }
1070
- directives.push({
1071
- scope: "value_emphasis",
1072
- instruction: `Emphasize ${overlap.effect.toLowerCase()} as the emergent state of aligned behavior.`
1035
+ }
1036
+ if (evolution.decisionPriorities.length === 0) {
1037
+ issues.push({
1038
+ line: prioritiesSection?.startLine ?? section.startLine,
1039
+ section: "Decision Priorities",
1040
+ message: "No decision priorities defined. Define what wins when tradeoffs appear.",
1041
+ severity: "warning"
1073
1042
  });
1074
- lenses.push({
1075
- id: lensId,
1076
- name: titleCase(overlap.effect),
1077
- tagline: `${overlap.effect} through ${overlap.domainA} and ${overlap.domainB}.`,
1078
- derived_from: {
1079
- domainA: overlap.domainA,
1080
- domainB: overlap.domainB,
1081
- effect: overlap.effect
1082
- },
1083
- tone,
1084
- directives
1043
+ }
1044
+ const evoSection = h2Sections.find(
1045
+ (s) => s.name.toLowerCase() === "evolution conditions"
1046
+ );
1047
+ if (evoSection) {
1048
+ evolution.evolutionConditions = parseBulletList(evoSection.content);
1049
+ }
1050
+ if (evolution.signals.length > 0 && evolution.driftBehaviors.length === 0) {
1051
+ issues.push({
1052
+ line: signalsSection?.startLine ?? section.startLine,
1053
+ section: "Evolution Layer",
1054
+ message: "Signals defined but no drift behaviors. System cannot detect failure without drift definitions.",
1055
+ severity: "warning"
1085
1056
  });
1086
1057
  }
1087
- return lenses;
1088
- }
1089
- function emitLensSuggestions(model) {
1090
- return buildLensSuggestions(model);
1058
+ return evolution;
1091
1059
  }
1092
- function compileWorldModel(model) {
1060
+ function parseWorldModel(markdown) {
1061
+ const issues = [];
1062
+ if (!markdown || !markdown.trim()) {
1063
+ issues.push({
1064
+ line: 0,
1065
+ section: "file",
1066
+ message: "Empty input. Provide a .worldmodel.md file with three layers: Core Model Geometry, Contextual Modifiers, Evolution Layer.",
1067
+ severity: "error"
1068
+ });
1069
+ return { model: null, issues };
1070
+ }
1071
+ const { frontmatter: fmRaw, sections } = splitSections(markdown);
1072
+ const frontmatter = parseFrontmatter(fmRaw, issues);
1073
+ const geometrySection = sections.find(
1074
+ (s) => s.name.toLowerCase() === "core model geometry"
1075
+ );
1076
+ const modifiersSection = sections.find(
1077
+ (s) => s.name.toLowerCase() === "contextual modifiers"
1078
+ );
1079
+ const evolutionSection = sections.find(
1080
+ (s) => s.name.toLowerCase() === "evolution layer"
1081
+ );
1082
+ const geometry = parseGeometry(geometrySection, issues);
1083
+ const modifiers = parseModifiers(modifiersSection, issues);
1084
+ const evolution = parseEvolution(evolutionSection, issues);
1085
+ if (geometry.overlapEffects.length > 0 && !geometry.centerIdentity) {
1086
+ issues.push({
1087
+ line: 0,
1088
+ section: "Core Model Geometry",
1089
+ message: "Overlaps defined but no center identity. System lacks coherence without an aligned identity.",
1090
+ severity: "warning"
1091
+ });
1092
+ }
1093
+ const hasErrors = issues.some((i) => i.severity === "error");
1094
+ if (hasErrors) {
1095
+ return {
1096
+ model: {
1097
+ frontmatter,
1098
+ geometry,
1099
+ modifiers,
1100
+ evolution
1101
+ },
1102
+ issues
1103
+ };
1104
+ }
1093
1105
  return {
1094
- worldMarkdown: emitWorldMarkdown(model),
1095
- signalSchema: emitSignalSchema(model),
1096
- overlapMap: emitOverlapMap(model),
1097
- contextsConfig: emitContextsConfig(model),
1098
- lensSuggestions: emitLensSuggestions(model)
1106
+ model: {
1107
+ frontmatter,
1108
+ geometry,
1109
+ modifiers,
1110
+ evolution
1111
+ },
1112
+ issues
1099
1113
  };
1100
1114
  }
1101
1115
 
1102
1116
  // src/cli/worldmodel.ts
1117
+ init_worldmodel_compiler();
1103
1118
  var BOLD = "\x1B[1m";
1104
1119
  var DIM = "\x1B[2m";
1105
1120
  var CYAN = "\x1B[36m";
@@ -1257,6 +1272,46 @@ ${YELLOW}Validation found ${errors.length} issue${errors.length > 1 ? "s" : ""}
1257
1272
  } else {
1258
1273
  process.stderr.write(`${GREEN}Validates cleanly!${RESET}
1259
1274
  `);
1275
+ try {
1276
+ if (!validateResult.model) throw new Error("no model");
1277
+ const { compileWorldModel: compileWorldModel2 } = await Promise.resolve().then(() => (init_worldmodel_compiler(), worldmodel_compiler_exports));
1278
+ const compiled = compileWorldModel2(validateResult.model);
1279
+ const outDir = (0, import_path2.dirname)(outputPath);
1280
+ const baseName = (0, import_path2.basename)(outputPath).replace(/\.worldmodel\.md$/, "");
1281
+ const worldPath = (0, import_path2.join)(outDir, `${baseName}.nv-world.md`);
1282
+ await (0, import_promises.writeFile)(worldPath, compiled.worldMarkdown, "utf-8");
1283
+ const signalsPath = (0, import_path2.join)(outDir, "signals.json");
1284
+ await (0, import_promises.writeFile)(signalsPath, JSON.stringify(compiled.signalSchema, null, 2), "utf-8");
1285
+ const overlapsPath = (0, import_path2.join)(outDir, "overlaps.json");
1286
+ await (0, import_promises.writeFile)(overlapsPath, JSON.stringify(compiled.overlapMap, null, 2), "utf-8");
1287
+ const contextsPath = (0, import_path2.join)(outDir, "contexts.json");
1288
+ await (0, import_promises.writeFile)(contextsPath, JSON.stringify(compiled.contextsConfig, null, 2), "utf-8");
1289
+ const lensesPath = (0, import_path2.join)(outDir, "lenses.json");
1290
+ await (0, import_promises.writeFile)(lensesPath, JSON.stringify(compiled.lensSuggestions, null, 2), "utf-8");
1291
+ process.stderr.write(`
1292
+ ${GREEN}Compiled!${RESET} Token-optimized artifacts:
1293
+ `);
1294
+ process.stderr.write(`${DIM} ${worldPath}${RESET}
1295
+ `);
1296
+ process.stderr.write(`${DIM} ${signalsPath}${RESET}
1297
+ `);
1298
+ process.stderr.write(`${DIM} ${overlapsPath}${RESET}
1299
+ `);
1300
+ process.stderr.write(`${DIM} ${contextsPath}${RESET}
1301
+ `);
1302
+ process.stderr.write(`${DIM} ${lensesPath}${RESET}
1303
+ `);
1304
+ process.stderr.write(`
1305
+ ${DIM}Source: ${outputPath} (readable, editable)${RESET}
1306
+ `);
1307
+ process.stderr.write(`${DIM}Compiled: ready for Radiant \u2014 token-optimized, no prose overhead.${RESET}
1308
+ `);
1309
+ } catch {
1310
+ process.stderr.write(
1311
+ `${DIM}Auto-compile skipped \u2014 run 'neuroverse worldmodel build ${(0, import_path2.basename)(outputPath)}' manually.${RESET}
1312
+ `
1313
+ );
1314
+ }
1260
1315
  }
1261
1316
  } catch {
1262
1317
  }