@neuroverseos/governance 0.8.0 → 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.
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
6
9
  var __export = (target, all) => {
7
10
  for (var name in all)
8
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -17,885 +20,1101 @@ var __copyProps = (to, from, except, desc) => {
17
20
  };
18
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
22
 
20
- // src/cli/worldmodel.ts
21
- var worldmodel_exports = {};
22
- __export(worldmodel_exports, {
23
- main: () => main
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
24
32
  });
25
- module.exports = __toCommonJS(worldmodel_exports);
26
- var import_promises = require("fs/promises");
27
- var import_fs = require("fs");
28
- var import_path = require("path");
29
-
30
- // src/engine/worldmodel-parser.ts
31
- function splitSections(markdown) {
32
- const lines = markdown.split("\n");
33
- let frontmatter = "";
34
- let bodyStart = 0;
35
- if (lines[0]?.trim() === "---") {
36
- const endIdx = lines.indexOf("---", 1);
37
- if (endIdx > 0) {
38
- frontmatter = lines.slice(1, endIdx).join("\n");
39
- bodyStart = endIdx + 1;
40
- }
41
- }
42
- const sections = [];
43
- let currentSection = null;
44
- const contentLines = [];
45
- for (let i = bodyStart; i < lines.length; i++) {
46
- const line = lines[i];
47
- if (line.startsWith("# ")) {
48
- if (currentSection) {
49
- currentSection.content = contentLines.join("\n").trim();
50
- sections.push(currentSection);
51
- contentLines.length = 0;
52
- }
53
- currentSection = {
54
- name: line.replace(/^#\s+/, "").trim(),
55
- content: "",
56
- startLine: i + 1
57
- // 1-based
58
- };
59
- } else if (currentSection) {
60
- contentLines.push(line);
61
- }
62
- }
63
- if (currentSection) {
64
- currentSection.content = contentLines.join("\n").trim();
65
- sections.push(currentSection);
66
- }
67
- return { frontmatter, sections };
33
+ function toSnakeCase(text) {
34
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
68
35
  }
69
- function splitH2Sections(content, baseLine) {
70
- const lines = content.split("\n");
71
- const sections = [];
72
- let currentSection = null;
73
- const contentLines = [];
74
- for (let i = 0; i < lines.length; i++) {
75
- const line = lines[i];
76
- if (line.startsWith("## ")) {
77
- if (currentSection) {
78
- currentSection.content = contentLines.join("\n").trim();
79
- sections.push(currentSection);
80
- contentLines.length = 0;
81
- }
82
- currentSection = {
83
- name: line.replace(/^##\s+/, "").trim(),
84
- content: "",
85
- startLine: baseLine + i
86
- };
87
- } else if (currentSection) {
88
- contentLines.push(line);
89
- }
90
- }
91
- if (currentSection) {
92
- currentSection.content = contentLines.join("\n").trim();
93
- sections.push(currentSection);
94
- }
95
- return sections;
36
+ function toKebabCase2(text) {
37
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
96
38
  }
97
- function splitH3Sections(content, baseLine) {
98
- const lines = content.split("\n");
99
- const sections = [];
100
- let currentSection = null;
101
- const contentLines = [];
102
- for (let i = 0; i < lines.length; i++) {
103
- const line = lines[i];
104
- if (line.startsWith("### ")) {
105
- if (currentSection) {
106
- currentSection.content = contentLines.join("\n").trim();
107
- sections.push(currentSection);
108
- contentLines.length = 0;
109
- }
110
- currentSection = {
111
- name: line.replace(/^###\s+/, "").trim(),
112
- content: "",
113
- startLine: baseLine + i
114
- };
115
- } else if (currentSection) {
116
- contentLines.push(line);
117
- }
118
- }
119
- if (currentSection) {
120
- currentSection.content = contentLines.join("\n").trim();
121
- sections.push(currentSection);
122
- }
123
- return sections;
39
+ function titleCase(text) {
40
+ return text.split(/[\s_-]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
124
41
  }
125
- function splitH4Sections(content, baseLine) {
126
- const lines = content.split("\n");
127
- const sections = [];
128
- let currentSection = null;
129
- const contentLines = [];
130
- for (let i = 0; i < lines.length; i++) {
131
- const line = lines[i];
132
- if (line.startsWith("#### ")) {
133
- if (currentSection) {
134
- currentSection.content = contentLines.join("\n").trim();
135
- sections.push(currentSection);
136
- contentLines.length = 0;
137
- }
138
- currentSection = {
139
- name: line.replace(/^####\s+/, "").trim(),
140
- content: "",
141
- startLine: baseLine + i
142
- };
143
- } else if (currentSection) {
144
- 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;
145
48
  }
146
49
  }
147
- if (currentSection) {
148
- currentSection.content = contentLines.join("\n").trim();
149
- sections.push(currentSection);
150
- }
151
- return sections;
50
+ return "alignment_score";
152
51
  }
153
- function parseBulletList(content) {
154
- const items = [];
155
- let inComment = false;
156
- for (const line of content.split("\n")) {
157
- const trimmed = line.trim();
158
- if (trimmed.startsWith("<!--")) {
159
- inComment = true;
160
- }
161
- if (inComment) {
162
- if (trimmed.includes("-->")) {
163
- inComment = false;
164
- }
165
- continue;
166
- }
167
- if (trimmed.startsWith("- ")) {
168
- items.push(trimmed.slice(2).trim());
169
- }
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
+ );
170
73
  }
171
- return items;
172
- }
173
- function extractTextContent(content) {
174
- const lines = content.split("\n");
175
- const textLines = [];
176
- let inComment = false;
177
- for (const line of lines) {
178
- const trimmed = line.trim();
179
- if (trimmed.startsWith("<!--")) {
180
- inComment = true;
181
- }
182
- if (inComment) {
183
- if (trimmed.includes("-->")) {
184
- inComment = false;
185
- }
186
- continue;
187
- }
188
- if (trimmed && !trimmed.startsWith("#")) {
189
- textLines.push(trimmed);
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
+ );
190
85
  }
191
86
  }
192
- return textLines.join("\n").trim();
193
- }
194
- function toKebabCase(name) {
195
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
196
- }
197
- function parseFrontmatter(yaml, issues) {
198
- const result = {};
199
- for (const line of yaml.split("\n")) {
200
- const trimmed = line.trim();
201
- if (!trimmed || trimmed.startsWith("#")) continue;
202
- const colonIdx = trimmed.indexOf(":");
203
- if (colonIdx === -1) continue;
204
- const key = trimmed.slice(0, colonIdx).trim();
205
- const value = trimmed.slice(colonIdx + 1).trim();
206
- result[key] = value;
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("");
207
113
  }
208
- const name = result.name || "";
209
- if (!name) {
210
- issues.push({
211
- line: 1,
212
- section: "frontmatter",
213
- message: "Missing name in frontmatter. Provide a human-readable model name.",
214
- severity: "error"
215
- });
216
- }
217
- const model_id = result.model_id || toKebabCase(name);
218
- return {
219
- model_id,
220
- name,
221
- version: result.version || "1.0.0"
222
- };
223
- }
224
- function parseGeometry(section, issues) {
225
- const geometry = {
226
- mission: "",
227
- domains: [],
228
- overlapEffects: [],
229
- centerIdentity: ""
230
- };
231
- if (!section) {
232
- issues.push({
233
- line: 0,
234
- section: "Core Model Geometry",
235
- message: "Missing # Core Model Geometry section. Define the structural model: mission, domains with embedded skills and values, overlaps, and center identity.",
236
- severity: "error"
237
- });
238
- return geometry;
239
- }
240
- const h2Sections = splitH2Sections(section.content, section.startLine);
241
- const missionSection = h2Sections.find((s) => s.name.toLowerCase() === "mission");
242
- if (missionSection) {
243
- geometry.mission = extractTextContent(missionSection.content);
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("");
244
145
  }
245
- if (!geometry.mission) {
246
- issues.push({
247
- line: missionSection?.startLine ?? section.startLine,
248
- section: "Mission",
249
- message: "Missing ## Mission. Define what this system is trying to achieve \u2014 the core aim, not a slogan.",
250
- severity: "error"
251
- });
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("");
252
162
  }
253
- const domainsSection = h2Sections.find((s) => s.name.toLowerCase() === "domains");
254
- if (domainsSection) {
255
- const domainSections = splitH3Sections(domainsSection.content, domainsSection.startLine);
256
- for (const ds of domainSections) {
257
- const domain = {
258
- id: toKebabCase(ds.name),
259
- name: ds.name,
260
- skills: [],
261
- values: [],
262
- line: ds.startLine
263
- };
264
- const h4Sections = splitH4Sections(ds.content, ds.startLine);
265
- const skillsH4 = h4Sections.find((s) => s.name.toLowerCase() === "skills");
266
- if (skillsH4) {
267
- domain.skills = parseBulletList(skillsH4.content);
268
- }
269
- const valuesH4 = h4Sections.find((s) => s.name.toLowerCase() === "values");
270
- if (valuesH4) {
271
- domain.values = parseBulletList(valuesH4.content);
272
- }
273
- if (domain.skills.length === 0) {
274
- issues.push({
275
- line: ds.startLine,
276
- section: "Domains",
277
- message: `Domain '${ds.name}' has no skills defined. Skills are the capabilities within this domain.`,
278
- severity: "warning"
279
- });
280
- }
281
- if (domain.values.length === 0) {
282
- issues.push({
283
- line: ds.startLine,
284
- section: "Domains",
285
- message: `Domain '${ds.name}' has no values defined. Behavior has no constraints without values.`,
286
- severity: "warning"
287
- });
288
- }
289
- geometry.domains.push(domain);
290
- }
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("");
291
185
  }
292
- if (geometry.domains.length < 2) {
293
- issues.push({
294
- line: domainsSection?.startLine ?? section.startLine,
295
- section: "Domains",
296
- message: "At least 2 domains required. Domains are the major operating modes of the system, each carrying skills and values.",
297
- severity: "error"
298
- });
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("");
299
212
  }
300
- const overlapsSection = h2Sections.find(
301
- (s) => s.name.toLowerCase() === "overlap effects"
302
- );
303
- if (overlapsSection) {
304
- const bullets = parseBulletList(overlapsSection.content);
305
- const domainNames = new Set(geometry.domains.map((d) => d.name.toLowerCase()));
306
- for (let i = 0; i < bullets.length; i++) {
307
- const bullet = bullets[i];
308
- const match = bullet.match(/^(.+?)\s*\+\s*(.+?)\s*=\s*(.+)$/);
309
- if (match) {
310
- const domainA = match[1].trim();
311
- const domainB = match[2].trim();
312
- const effect = match[3].trim();
313
- if (!domainNames.has(domainA.toLowerCase())) {
314
- issues.push({
315
- line: overlapsSection.startLine + i,
316
- section: "Overlap Effects",
317
- message: `Overlap references unknown domain '${domainA}'. Must reference a declared domain.`,
318
- severity: "warning"
319
- });
320
- }
321
- if (!domainNames.has(domainB.toLowerCase())) {
322
- issues.push({
323
- line: overlapsSection.startLine + i,
324
- section: "Overlap Effects",
325
- message: `Overlap references unknown domain '${domainB}'. Must reference a declared domain.`,
326
- severity: "warning"
327
- });
328
- }
329
- geometry.overlapEffects.push({
330
- domainA,
331
- domainB,
332
- effect,
333
- line: overlapsSection.startLine + i
334
- });
335
- } else {
336
- issues.push({
337
- line: overlapsSection.startLine + i,
338
- section: "Overlap Effects",
339
- message: `Cannot parse overlap: '${bullet}'. Expected format: 'Domain A + Domain B = Emergent State'.`,
340
- severity: "warning"
341
- });
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}`);
342
235
  }
236
+ lines.push("");
343
237
  }
344
238
  }
345
- if (geometry.overlapEffects.length === 0) {
346
- issues.push({
347
- line: overlapsSection?.startLine ?? section.startLine,
348
- section: "Overlap Effects",
349
- message: 'No overlap effects defined. Define what emerges when two domains interact well (e.g., "Domain A + Domain B = Inspiration").',
350
- severity: "warning"
351
- });
352
- }
353
- const identitySection = h2Sections.find(
354
- (s) => s.name.toLowerCase() === "center identity"
355
- );
356
- if (identitySection) {
357
- geometry.centerIdentity = extractTextContent(identitySection.content);
358
- }
359
- if (!geometry.centerIdentity) {
360
- issues.push({
361
- line: identitySection?.startLine ?? section.startLine,
362
- section: "Center Identity",
363
- message: "No center identity defined. Define what the system becomes when all domains are aligned \u2014 the core identity.",
364
- severity: "warning"
365
- });
366
- }
367
- return geometry;
239
+ return lines.join("\n");
368
240
  }
369
- function parseModifiers(section, issues) {
370
- const modifiers = {
371
- authorityLayers: [],
372
- spatialContexts: [],
373
- interpretationRules: []
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
374
251
  };
375
- if (!section) {
376
- issues.push({
377
- line: 0,
378
- section: "Contextual Modifiers",
379
- message: "Missing # Contextual Modifiers section. Define how authority, role, and spatial context change how behavior is interpreted.",
380
- severity: "warning"
381
- });
382
- return modifiers;
383
- }
384
- const h2Sections = splitH2Sections(section.content, section.startLine);
385
- const authoritySection = h2Sections.find(
386
- (s) => s.name.toLowerCase() === "authority layers"
387
- );
388
- if (authoritySection) {
389
- modifiers.authorityLayers = parseBulletList(authoritySection.content);
390
- }
391
- const spatialSection = h2Sections.find(
392
- (s) => s.name.toLowerCase() === "spatial contexts"
393
- );
394
- if (spatialSection) {
395
- modifiers.spatialContexts = parseBulletList(spatialSection.content);
396
- }
397
- const rulesSection = h2Sections.find(
398
- (s) => s.name.toLowerCase() === "interpretation rules"
399
- );
400
- if (rulesSection) {
401
- modifiers.interpretationRules = parseBulletList(rulesSection.content);
252
+ }
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;
402
265
  }
403
- return modifiers;
266
+ return {
267
+ model_id: model.frontmatter.model_id,
268
+ pairings,
269
+ matrix
270
+ };
404
271
  }
405
- function parseEvolution(section, issues) {
406
- const evolution = {
407
- alignedBehaviors: [],
408
- driftBehaviors: [],
409
- signals: [],
410
- decisionPriorities: [],
411
- evolutionConditions: []
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
412
278
  };
413
- if (!section) {
414
- issues.push({
415
- line: 0,
416
- section: "Evolution Layer",
417
- message: "Missing # Evolution Layer section. Define observable behaviors, signals, decision priorities, and adaptation conditions.",
418
- severity: "error"
419
- });
420
- return evolution;
421
- }
422
- const h2Sections = splitH2Sections(section.content, section.startLine);
423
- const alignedSection = h2Sections.find(
424
- (s) => s.name.toLowerCase() === "aligned behaviors"
425
- );
426
- if (alignedSection) {
427
- evolution.alignedBehaviors = parseBulletList(alignedSection.content);
428
- }
429
- if (evolution.alignedBehaviors.length === 0) {
430
- issues.push({
431
- line: alignedSection?.startLine ?? section.startLine,
432
- section: "Aligned Behaviors",
433
- message: "No aligned behaviors defined. Define what success looks like in action.",
434
- severity: "warning"
435
- });
436
- }
437
- const driftSection = h2Sections.find(
438
- (s) => s.name.toLowerCase() === "drift behaviors"
439
- );
440
- if (driftSection) {
441
- evolution.driftBehaviors = parseBulletList(driftSection.content);
279
+ }
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";
442
287
  }
443
- if (evolution.driftBehaviors.length === 0) {
444
- issues.push({
445
- line: driftSection?.startLine ?? section.startLine,
446
- section: "Drift Behaviors",
447
- message: "No drift behaviors defined. Define what misalignment looks like so the system can detect behavioral drift over time.",
448
- severity: "warning"
449
- });
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";
450
293
  }
451
- const signalsSection = h2Sections.find(
452
- (s) => s.name.toLowerCase() === "signals"
453
- );
454
- if (signalsSection) {
455
- evolution.signals = parseBulletList(signalsSection.content);
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";
456
299
  }
457
- if (evolution.signals.length < 2) {
458
- issues.push({
459
- line: signalsSection?.startLine ?? section.startLine,
460
- section: "Signals",
461
- message: "At least 2 signals required. Signals are the observable metrics for detecting alignment or drift.",
462
- severity: "error"
463
- });
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";
464
305
  }
465
- const prioritiesSection = h2Sections.find(
466
- (s) => s.name.toLowerCase() === "decision priorities"
467
- );
468
- if (prioritiesSection) {
469
- const bullets = parseBulletList(prioritiesSection.content);
470
- for (let i = 0; i < bullets.length; i++) {
471
- const bullet = bullets[i];
472
- const match = bullet.match(/^(.+?)\s*>\s*(.+)$/);
473
- if (match) {
474
- evolution.decisionPriorities.push({
475
- preferred: match[1].trim(),
476
- over: match[2].trim(),
477
- line: prioritiesSection.startLine + i
478
- });
479
- } else {
480
- issues.push({
481
- line: prioritiesSection.startLine + i,
482
- section: "Decision Priorities",
483
- message: `Cannot parse priority: '${bullet}'. Expected format: 'preferred > over'.`,
484
- severity: "warning"
485
- });
486
- }
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
+ });
487
322
  }
488
- }
489
- if (evolution.decisionPriorities.length === 0) {
490
- issues.push({
491
- line: prioritiesSection?.startLine ?? section.startLine,
492
- section: "Decision Priorities",
493
- message: "No decision priorities defined. Define what wins when tradeoffs appear.",
494
- severity: "warning"
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.`
495
332
  });
496
- }
497
- const evoSection = h2Sections.find(
498
- (s) => s.name.toLowerCase() === "evolution conditions"
499
- );
500
- if (evoSection) {
501
- evolution.evolutionConditions = parseBulletList(evoSection.content);
502
- }
503
- if (evolution.signals.length > 0 && evolution.driftBehaviors.length === 0) {
504
- issues.push({
505
- line: signalsSection?.startLine ?? section.startLine,
506
- section: "Evolution Layer",
507
- message: "Signals defined but no drift behaviors. System cannot detect failure without drift definitions.",
508
- severity: "warning"
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
509
344
  });
510
345
  }
511
- return evolution;
346
+ return lenses;
512
347
  }
513
- function parseWorldModel(markdown) {
514
- const issues = [];
515
- if (!markdown || !markdown.trim()) {
516
- issues.push({
517
- line: 0,
518
- section: "file",
519
- message: "Empty input. Provide a .worldmodel.md file with three layers: Core Model Geometry, Contextual Modifiers, Evolution Layer.",
520
- severity: "error"
521
- });
522
- return { model: null, issues };
348
+ function emitLensSuggestions(model) {
349
+ return buildLensSuggestions(model);
350
+ }
351
+ function compileWorldModel(model) {
352
+ return {
353
+ worldMarkdown: emitWorldMarkdown(model),
354
+ signalSchema: emitSignalSchema(model),
355
+ overlapMap: emitOverlapMap(model),
356
+ contextsConfig: emitContextsConfig(model),
357
+ lensSuggestions: emitLensSuggestions(model)
358
+ };
359
+ }
360
+ var init_worldmodel_compiler = __esm({
361
+ "src/engine/worldmodel-compiler.ts"() {
362
+ "use strict";
523
363
  }
524
- const { frontmatter: fmRaw, sections } = splitSections(markdown);
525
- const frontmatter = parseFrontmatter(fmRaw, issues);
526
- const geometrySection = sections.find(
527
- (s) => s.name.toLowerCase() === "core model geometry"
528
- );
529
- const modifiersSection = sections.find(
530
- (s) => s.name.toLowerCase() === "contextual modifiers"
531
- );
532
- const evolutionSection = sections.find(
533
- (s) => s.name.toLowerCase() === "evolution layer"
534
- );
535
- const geometry = parseGeometry(geometrySection, issues);
536
- const modifiers = parseModifiers(modifiersSection, issues);
537
- const evolution = parseEvolution(evolutionSection, issues);
538
- if (geometry.overlapEffects.length > 0 && !geometry.centerIdentity) {
539
- issues.push({
540
- line: 0,
541
- section: "Core Model Geometry",
542
- message: "Overlaps defined but no center identity. System lacks coherence without an aligned identity.",
543
- severity: "warning"
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
+ });
544
398
  });
399
+ answers[q.id] = answer;
545
400
  }
546
- const hasErrors = issues.some((i) => i.severity === "error");
547
- if (hasErrors) {
548
- return {
549
- model: {
550
- frontmatter,
551
- geometry,
552
- modifiers,
553
- evolution
554
- },
555
- issues
556
- };
557
- }
558
- return {
559
- model: {
560
- frontmatter,
561
- geometry,
562
- modifiers,
563
- evolution
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"
564
412
  },
565
- issues
566
- };
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}`);
422
+ }
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();
567
427
  }
428
+ function buildStructuringPrompt(answers) {
429
+ return `Structure these conversational answers into a .worldmodel.md file.
568
430
 
569
- // src/engine/worldmodel-compiler.ts
570
- function toSnakeCase(text) {
571
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
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.`;
572
504
  }
573
- function toKebabCase2(text) {
574
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
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;
575
509
  }
576
- function titleCase(text) {
577
- return text.split(/[\s_-]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
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"'
562
+ }
563
+ ];
564
+ }
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
+ }
588
+ }
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
+ }
609
+ }
610
+ if (currentSection) {
611
+ currentSection.content = contentLines.join("\n").trim();
612
+ sections.push(currentSection);
613
+ }
614
+ return { frontmatter, sections };
578
615
  }
579
- function matchSignal(behaviorText, signals) {
580
- const lowerText = behaviorText.toLowerCase();
581
- for (const signal of signals) {
582
- const signalWords = signal.toLowerCase().split("_");
583
- if (signalWords.some((w) => w.length > 3 && lowerText.includes(w))) {
584
- return signal;
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);
585
636
  }
586
637
  }
587
- return "alignment_score";
588
- }
589
- function emitWorldMarkdown(model) {
590
- const lines = [];
591
- const worldId = model.frontmatter.model_id;
592
- const worldName = model.frontmatter.name;
593
- const version = model.frontmatter.version;
594
- lines.push("---");
595
- lines.push(`world_id: ${worldId}`);
596
- lines.push(`name: ${worldName}`);
597
- lines.push(`version: ${version}`);
598
- lines.push("runtime_mode: COMPLIANCE");
599
- lines.push("default_profile: aligned");
600
- lines.push("alternative_profile: drifting");
601
- lines.push("---");
602
- lines.push("");
603
- lines.push("# Thesis");
604
- lines.push("");
605
- lines.push(model.geometry.mission);
606
- if (model.geometry.centerIdentity) {
607
- lines.push(
608
- `When all domains are aligned, the system operates as: ${model.geometry.centerIdentity}.`
609
- );
638
+ if (currentSection) {
639
+ currentSection.content = contentLines.join("\n").trim();
640
+ sections.push(currentSection);
610
641
  }
611
- lines.push("");
612
- lines.push("# Invariants");
613
- lines.push("");
614
- let invariantIdx = 0;
615
- for (const domain of model.geometry.domains) {
616
- for (const value of domain.values) {
617
- invariantIdx++;
618
- const invId = `${toSnakeCase(domain.name)}_value_${String(invariantIdx).padStart(2, "0")}`;
619
- lines.push(
620
- `- \`${invId}\` \u2014 ${value} [${domain.name}] (structural, immutable)`
621
- );
642
+ return sections;
643
+ }
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);
622
664
  }
623
665
  }
624
- lines.push("");
625
- lines.push("# State");
626
- lines.push("");
627
- lines.push("## alignment_score");
628
- lines.push("- type: number");
629
- lines.push("- min: 0");
630
- lines.push("- max: 100");
631
- lines.push("- step: 5");
632
- lines.push("- default: 70");
633
- lines.push("- label: Alignment Score");
634
- lines.push(
635
- "- description: Composite behavioral alignment metric derived from all signals"
636
- );
637
- lines.push("");
638
- for (const signal of model.evolution.signals) {
639
- const signalId = toSnakeCase(signal);
640
- const signalLabel = titleCase(signal);
641
- lines.push(`## ${signalId}`);
642
- lines.push("- type: number");
643
- lines.push("- min: 0");
644
- lines.push("- max: 100");
645
- lines.push("- step: 5");
646
- lines.push("- default: 70");
647
- lines.push(`- label: ${signalLabel}`);
648
- lines.push(`- description: Behavioral signal measuring ${signal.replace(/_/g, " ")}`);
649
- lines.push("");
650
- }
651
- lines.push("# Assumptions");
652
- lines.push("");
653
- lines.push("## aligned");
654
- lines.push("- name: Aligned");
655
- lines.push("- description: All behavioral signals at healthy levels");
656
- lines.push("- pressure_level: low");
657
- lines.push("");
658
- lines.push("## drifting");
659
- lines.push("- name: Drifting");
660
- lines.push("- description: Behavioral signals under pressure with drift risk");
661
- lines.push("- pressure_level: high");
662
- lines.push("");
663
- lines.push("# Rules");
664
- lines.push("");
665
- let ruleIdx = 0;
666
- for (const drift of model.evolution.driftBehaviors) {
667
- ruleIdx++;
668
- const ruleId = `rule-${String(ruleIdx).padStart(3, "0")}`;
669
- const matchedSignal = matchSignal(drift, model.evolution.signals);
670
- const signalId = toSnakeCase(matchedSignal);
671
- lines.push(`## ${ruleId}: ${drift} (degradation)`);
672
- lines.push(`Drift behavior detected: ${drift}`);
673
- lines.push("");
674
- lines.push(`When ${signalId} < 50 [state]`);
675
- lines.push("Then alignment_score *= 0.80");
676
- lines.push("");
677
- lines.push(`> trigger: ${signalId} drops below threshold`);
678
- lines.push(`> rule: Drift behavior weakens alignment`);
679
- lines.push(`> shift: Behavioral alignment decreases`);
680
- lines.push(`> effect: Alignment score reduced by 20%`);
681
- lines.push("");
666
+ if (currentSection) {
667
+ currentSection.content = contentLines.join("\n").trim();
668
+ sections.push(currentSection);
682
669
  }
683
- for (const aligned of model.evolution.alignedBehaviors) {
684
- ruleIdx++;
685
- const ruleId = `rule-${String(ruleIdx).padStart(3, "0")}`;
686
- const matchedSignal = matchSignal(aligned, model.evolution.signals);
687
- const signalId = toSnakeCase(matchedSignal);
688
- lines.push(`## ${ruleId}: ${aligned} (advantage)`);
689
- lines.push(`Aligned behavior reinforced: ${aligned}`);
690
- lines.push("");
691
- lines.push(`When ${signalId} >= 70 [state]`);
692
- lines.push("Then alignment_score *= 1.10");
693
- lines.push("");
694
- lines.push(`> trigger: ${signalId} above healthy threshold`);
695
- lines.push(`> rule: Aligned behavior strengthens system`);
696
- lines.push(`> shift: Behavioral alignment increases`);
697
- lines.push(`> effect: Alignment score boosted by 10%`);
698
- lines.push("");
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
+ }
699
693
  }
700
- for (let i = 0; i < model.evolution.decisionPriorities.length; i++) {
701
- ruleIdx++;
702
- const priority = model.evolution.decisionPriorities[i];
703
- const ruleId = `rule-${String(ruleIdx).padStart(3, "0")}`;
704
- lines.push(
705
- `## ${ruleId}: ${priority.preferred} over ${priority.over} (structural)`
706
- );
707
- lines.push(
708
- `Priority: ${priority.preferred} takes precedence over ${priority.over} in tradeoff situations.`
709
- );
710
- lines.push("");
711
- lines.push("When alignment_score < 40 [state]");
712
- lines.push("Then alignment_score *= 0.70");
713
- lines.push("Collapse: alignment_score < 10");
714
- lines.push("");
715
- lines.push(`> trigger: Alignment score critically low`);
716
- lines.push(
717
- `> rule: Priority violation \u2014 ${priority.preferred} must outweigh ${priority.over}`
718
- );
719
- lines.push(`> shift: System enters structural enforcement`);
720
- lines.push(`> effect: Alignment sharply reduced; collapse if critical`);
721
- lines.push("");
694
+ if (currentSection) {
695
+ currentSection.content = contentLines.join("\n").trim();
696
+ sections.push(currentSection);
722
697
  }
723
- lines.push("# Gates");
724
- lines.push("");
725
- lines.push("- STRONG: alignment_score >= 85");
726
- lines.push("- STABLE: alignment_score >= 65");
727
- lines.push("- WATCHING: alignment_score >= 45");
728
- lines.push("- FRAGILE: alignment_score > 30");
729
- lines.push("- MISALIGNED: alignment_score <= 30");
730
- lines.push("");
731
- lines.push("# Outcomes");
732
- lines.push("");
733
- lines.push("## alignment_score");
734
- lines.push("- type: number");
735
- lines.push("- range: 0-100");
736
- lines.push("- display: percentage");
737
- lines.push("- label: Alignment Score");
738
- lines.push("- primary: true");
739
- lines.push("");
740
- for (const signal of model.evolution.signals) {
741
- const signalId = toSnakeCase(signal);
742
- const signalLabel = titleCase(signal);
743
- lines.push(`## ${signalId}`);
744
- lines.push("- type: number");
745
- lines.push("- range: 0-100");
746
- lines.push("- display: percentage");
747
- lines.push(`- label: ${signalLabel}`);
748
- lines.push("");
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;
711
+ }
712
+ continue;
713
+ }
714
+ if (trimmed.startsWith("- ")) {
715
+ items.push(trimmed.slice(2).trim());
716
+ }
749
717
  }
750
- const lensSuggestions = buildLensSuggestions(model);
751
- if (lensSuggestions.length > 0) {
752
- lines.push("# Lenses");
753
- lines.push("- policy: role_default");
754
- lines.push("");
755
- for (const lens of lensSuggestions) {
756
- lines.push(`## ${lens.id}`);
757
- lines.push(`- tagline: ${lens.tagline}`);
758
- lines.push(
759
- `- description: Lens derived from ${lens.derived_from.domainA} and ${lens.derived_from.domainB} interaction, producing ${lens.derived_from.effect}.`
760
- );
761
- lines.push(`- formality: ${lens.tone.formality}`);
762
- lines.push(`- verbosity: ${lens.tone.verbosity}`);
763
- lines.push(`- emotion: ${lens.tone.emotion}`);
764
- lines.push(`- confidence: ${lens.tone.confidence}`);
765
- lines.push("- tags: behavioral, worldmodel, overlap");
766
- lines.push("- default_for_roles: all");
767
- lines.push("- priority: 50");
768
- lines.push("- stackable: true");
769
- lines.push("");
770
- for (const directive of lens.directives) {
771
- lines.push(`> ${directive.scope}: ${directive.instruction}`);
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;
772
732
  }
773
- lines.push("");
733
+ continue;
734
+ }
735
+ if (trimmed && !trimmed.startsWith("#")) {
736
+ textLines.push(trimmed);
774
737
  }
775
738
  }
776
- return lines.join("\n");
739
+ return textLines.join("\n").trim();
777
740
  }
778
- function emitSignalSchema(model) {
779
- const signals = model.evolution.signals.map((signal) => ({
780
- id: toSnakeCase(signal),
781
- name: titleCase(signal),
782
- type: "number",
783
- default: 70
784
- }));
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;
754
+ }
755
+ const name = result.name || "";
756
+ if (!name) {
757
+ issues.push({
758
+ line: 1,
759
+ section: "frontmatter",
760
+ message: "Missing name in frontmatter. Provide a human-readable model name.",
761
+ severity: "error"
762
+ });
763
+ }
764
+ const model_id = result.model_id || toKebabCase(name);
785
765
  return {
786
- model_id: model.frontmatter.model_id,
787
- signals
766
+ model_id,
767
+ name,
768
+ version: result.version || "1.0.0"
788
769
  };
789
770
  }
790
- function emitOverlapMap(model) {
791
- const pairings = model.geometry.overlapEffects.map((o) => ({
792
- domainA: o.domainA,
793
- domainB: o.domainB,
794
- effect: o.effect
795
- }));
796
- const matrix = {};
797
- for (const overlap of model.geometry.overlapEffects) {
798
- const keyA = toKebabCase2(overlap.domainA);
799
- const keyB = toKebabCase2(overlap.domainB);
800
- if (!matrix[keyA]) matrix[keyA] = {};
801
- matrix[keyA][keyB] = overlap.effect;
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;
802
786
  }
803
- return {
804
- model_id: model.frontmatter.model_id,
805
- pairings,
806
- matrix
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);
791
+ }
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
+ });
799
+ }
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
+ }
838
+ }
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
+ });
889
+ }
890
+ }
891
+ }
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;
915
+ }
916
+ function parseModifiers(section, issues) {
917
+ const modifiers = {
918
+ authorityLayers: [],
919
+ spatialContexts: [],
920
+ interpretationRules: []
807
921
  };
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);
937
+ }
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;
808
951
  }
809
- function emitContextsConfig(model) {
810
- return {
811
- model_id: model.frontmatter.model_id,
812
- authority_layers: model.modifiers.authorityLayers,
813
- spatial_contexts: model.modifiers.spatialContexts,
814
- interpretation_rules: model.modifiers.interpretationRules
952
+ function parseEvolution(section, issues) {
953
+ const evolution = {
954
+ alignedBehaviors: [],
955
+ driftBehaviors: [],
956
+ signals: [],
957
+ decisionPriorities: [],
958
+ evolutionConditions: []
815
959
  };
816
- }
817
- function deriveTone(domainA, domainB) {
818
- const combined = `${domainA} ${domainB}`.toLowerCase();
819
- let formality = "neutral";
820
- if (/strateg|technic|analytic|research|engineer/.test(combined)) {
821
- formality = "professional";
822
- } else if (/narrat|story|communi|creative/.test(combined)) {
823
- 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;
824
968
  }
825
- let verbosity = "balanced";
826
- if (/foresight|scenario|plan|design/.test(combined)) {
827
- verbosity = "detailed";
828
- } else if (/prosper|negotiat|stakeholder/.test(combined)) {
829
- 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);
830
975
  }
831
- let emotion = "neutral";
832
- if (/empath|emoti|care|safe|trust/.test(combined)) {
833
- emotion = "warm";
834
- } else if (/analytic|system|data/.test(combined)) {
835
- 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
+ });
836
983
  }
837
- let confidence = "balanced";
838
- if (/lead|command|decis|strateg/.test(combined)) {
839
- confidence = "authoritative";
840
- } else if (/explor|experiment|creat/.test(combined)) {
841
- confidence = "exploratory";
984
+ const driftSection = h2Sections.find(
985
+ (s) => s.name.toLowerCase() === "drift behaviors"
986
+ );
987
+ if (driftSection) {
988
+ evolution.driftBehaviors = parseBulletList(driftSection.content);
842
989
  }
843
- return { formality, verbosity, emotion, confidence };
844
- }
845
- function buildLensSuggestions(model) {
846
- const lenses = [];
847
- const domainMap = new Map(model.geometry.domains.map((d) => [d.name.toLowerCase(), d]));
848
- for (const overlap of model.geometry.overlapEffects) {
849
- const lensId = toKebabCase2(overlap.effect);
850
- const tone = deriveTone(overlap.domainA, overlap.domainB);
851
- const domainAData = domainMap.get(overlap.domainA.toLowerCase());
852
- const domainBData = domainMap.get(overlap.domainB.toLowerCase());
853
- const directives = [];
854
- if (domainAData && domainAData.skills.length > 0) {
855
- directives.push({
856
- scope: "response_framing",
857
- instruction: `Approach through the lens of ${domainAData.skills.join(", ").toLowerCase()}.`
858
- });
859
- }
860
- if (domainBData && domainBData.values.length > 0) {
861
- directives.push({
862
- scope: "behavior_shaping",
863
- instruction: `Maintain ${domainBData.values.join(", ").toLowerCase()} in all responses.`
864
- });
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
+ }
865
1034
  }
866
- directives.push({
867
- scope: "value_emphasis",
868
- 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"
869
1042
  });
870
- lenses.push({
871
- id: lensId,
872
- name: titleCase(overlap.effect),
873
- tagline: `${overlap.effect} through ${overlap.domainA} and ${overlap.domainB}.`,
874
- derived_from: {
875
- domainA: overlap.domainA,
876
- domainB: overlap.domainB,
877
- effect: overlap.effect
878
- },
879
- tone,
880
- 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"
881
1056
  });
882
1057
  }
883
- return lenses;
884
- }
885
- function emitLensSuggestions(model) {
886
- return buildLensSuggestions(model);
1058
+ return evolution;
887
1059
  }
888
- 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
+ }
889
1105
  return {
890
- worldMarkdown: emitWorldMarkdown(model),
891
- signalSchema: emitSignalSchema(model),
892
- overlapMap: emitOverlapMap(model),
893
- contextsConfig: emitContextsConfig(model),
894
- lensSuggestions: emitLensSuggestions(model)
1106
+ model: {
1107
+ frontmatter,
1108
+ geometry,
1109
+ modifiers,
1110
+ evolution
1111
+ },
1112
+ issues
895
1113
  };
896
1114
  }
897
1115
 
898
1116
  // src/cli/worldmodel.ts
1117
+ init_worldmodel_compiler();
899
1118
  var BOLD = "\x1B[1m";
900
1119
  var DIM = "\x1B[2m";
901
1120
  var CYAN = "\x1B[36m";
@@ -968,8 +1187,8 @@ function formatIssue(issue) {
968
1187
  return ` ${color}${icon}${RESET} [${issue.section}${lineRef}] ${issue.message}`;
969
1188
  }
970
1189
  async function readSourceFile(inputPath) {
971
- const fullPath = (0, import_path.resolve)(inputPath);
972
- if (!(0, import_fs.existsSync)(fullPath)) {
1190
+ const fullPath = (0, import_path2.resolve)(inputPath);
1191
+ if (!(0, import_fs2.existsSync)(fullPath)) {
973
1192
  process.stderr.write(`File not found: ${fullPath}
974
1193
  `);
975
1194
  process.exit(3);
@@ -993,30 +1212,145 @@ async function cmdInit(argv) {
993
1212
  const args = parseArgs(argv);
994
1213
  if (args.help) {
995
1214
  process.stdout.write(
996
- 'neuroverse worldmodel init \u2014 Scaffold a new .worldmodel.md template\n\nOptions:\n --name <name> Model name (default: "My Behavioral Model")\n --output <path> Output path (default: ./model.worldmodel.md)\n'
1215
+ 'neuroverse worldmodel init \u2014 Create a new behavioral model\n\nWith ANTHROPIC_API_KEY set:\n Interactive \u2014 answers questions, AI structures your worldmodel.\n Your natural process: talk \u2192 structure \u2192 refine.\n\nWithout ANTHROPIC_API_KEY:\n Template \u2014 generates a scaffold you fill in manually.\n\nOptions:\n --name <name> Model name (default: "My Behavioral Model")\n --output <path> Output path (default: ./model.worldmodel.md)\n --template Force template mode even with API key set\n'
997
1216
  );
998
1217
  return;
999
1218
  }
1000
1219
  const modelName = args.name || "My Behavioral Model";
1001
- const outputPath = (0, import_path.resolve)(
1220
+ const outputPath = (0, import_path2.resolve)(
1002
1221
  args.output || `./${toFileName(modelName)}.worldmodel.md`
1003
1222
  );
1004
- if ((0, import_fs.existsSync)(outputPath)) {
1223
+ if ((0, import_fs2.existsSync)(outputPath)) {
1005
1224
  process.stderr.write(`File already exists: ${outputPath}
1006
1225
  `);
1007
1226
  process.stderr.write("Use --output to specify a different path.\n");
1008
1227
  process.exit(1);
1009
1228
  }
1229
+ const apiKey = process.env.ANTHROPIC_API_KEY;
1230
+ const forceTemplate = argv.includes("--template");
1231
+ if (apiKey && !forceTemplate && process.stdin.isTTY) {
1232
+ const { askQuestions: askQuestions2, structureWorldmodel: structureWorldmodel2, saveWorldmodel: saveWorldmodel2 } = await Promise.resolve().then(() => (init_worldmodel_create(), worldmodel_create_exports));
1233
+ process.stderr.write(`
1234
+ `);
1235
+ const answers = await askQuestions2();
1236
+ process.stderr.write(`${DIM}Structuring your worldmodel...${RESET}
1237
+ `);
1238
+ try {
1239
+ const content = await structureWorldmodel2(answers, apiKey);
1240
+ const dir2 = (0, import_path2.dirname)(outputPath);
1241
+ if (!(0, import_fs2.existsSync)(dir2)) {
1242
+ await (0, import_promises.mkdir)(dir2, { recursive: true });
1243
+ }
1244
+ saveWorldmodel2(content, outputPath);
1245
+ process.stderr.write(`
1246
+ ${GREEN}Created${RESET} ${outputPath}
1247
+ `);
1248
+ process.stderr.write(
1249
+ `${DIM}Review the file, then run: neuroverse worldmodel validate ${(0, import_path2.basename)(outputPath)}${RESET}
1250
+ `
1251
+ );
1252
+ try {
1253
+ const validateResult = parseWorldModel(content);
1254
+ if (validateResult.model) {
1255
+ const m = validateResult.model;
1256
+ process.stderr.write(
1257
+ `${DIM} Domains: ${m.geometry.domains.length} Overlaps: ${m.geometry.overlapEffects.length} Signals: ${m.evolution.signals.length}${RESET}
1258
+ `
1259
+ );
1260
+ }
1261
+ const errors = validateResult.issues.filter((i) => i.severity === "error");
1262
+ if (errors.length > 0) {
1263
+ process.stderr.write(
1264
+ `
1265
+ ${YELLOW}Validation found ${errors.length} issue${errors.length > 1 ? "s" : ""} \u2014 review and fix:${RESET}
1266
+ `
1267
+ );
1268
+ for (const issue of errors) {
1269
+ process.stderr.write(` ${formatIssue(issue)}
1270
+ `);
1271
+ }
1272
+ } else {
1273
+ process.stderr.write(`${GREEN}Validates cleanly!${RESET}
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
+ }
1315
+ }
1316
+ } catch {
1317
+ }
1318
+ } catch (err) {
1319
+ process.stderr.write(`${RED}Error:${RESET} ${err}
1320
+ `);
1321
+ process.stderr.write(
1322
+ `${DIM}Falling back to template mode.${RESET}
1323
+
1324
+ `
1325
+ );
1326
+ const template2 = generateScaffold(modelName);
1327
+ const dir2 = (0, import_path2.dirname)(outputPath);
1328
+ if (!(0, import_fs2.existsSync)(dir2)) {
1329
+ await (0, import_promises.mkdir)(dir2, { recursive: true });
1330
+ }
1331
+ await (0, import_promises.writeFile)(outputPath, template2, "utf-8");
1332
+ process.stderr.write(`${GREEN}Created${RESET} ${outputPath} (template)
1333
+ `);
1334
+ }
1335
+ return;
1336
+ }
1337
+ if (!apiKey && !forceTemplate) {
1338
+ process.stderr.write(
1339
+ `${DIM}Tip: Set ANTHROPIC_API_KEY for the guided experience \u2014 AI asks you questions and creates your worldmodel.${RESET}
1340
+
1341
+ `
1342
+ );
1343
+ }
1010
1344
  const template = generateScaffold(modelName);
1011
- const dir = (0, import_path.dirname)(outputPath);
1012
- if (!(0, import_fs.existsSync)(dir)) {
1345
+ const dir = (0, import_path2.dirname)(outputPath);
1346
+ if (!(0, import_fs2.existsSync)(dir)) {
1013
1347
  await (0, import_promises.mkdir)(dir, { recursive: true });
1014
1348
  }
1015
1349
  await (0, import_promises.writeFile)(outputPath, template, "utf-8");
1016
1350
  process.stderr.write(`${GREEN}Created${RESET} ${outputPath}
1017
1351
  `);
1018
1352
  process.stderr.write(
1019
- `${DIM}Edit the template, then run: neuroverse worldmodel validate ${(0, import_path.basename)(outputPath)}${RESET}
1353
+ `${DIM}Edit the template, then run: neuroverse worldmodel validate ${(0, import_path2.basename)(outputPath)}${RESET}
1020
1354
  `
1021
1355
  );
1022
1356
  }
@@ -1447,17 +1781,17 @@ async function cmdBuild(argv) {
1447
1781
  process.stderr.write("\n");
1448
1782
  }
1449
1783
  const output = compileWorldModel(model);
1450
- const outputDir = (0, import_path.resolve)(
1784
+ const outputDir = (0, import_path2.resolve)(
1451
1785
  args.output || `.neuroverse/worldmodels/${model.frontmatter.model_id}/`
1452
1786
  );
1453
- if (!(0, import_fs.existsSync)(outputDir)) {
1787
+ if (!(0, import_fs2.existsSync)(outputDir)) {
1454
1788
  await (0, import_promises.mkdir)(outputDir, { recursive: true });
1455
1789
  }
1456
- const worldPath = (0, import_path.join)(outputDir, `${model.frontmatter.model_id}.nv-world.md`);
1457
- const signalsPath = (0, import_path.join)(outputDir, "signals.json");
1458
- const overlapsPath = (0, import_path.join)(outputDir, "overlaps.json");
1459
- const contextsPath = (0, import_path.join)(outputDir, "contexts.json");
1460
- const lensesPath = (0, import_path.join)(outputDir, "lenses.json");
1790
+ const worldPath = (0, import_path2.join)(outputDir, `${model.frontmatter.model_id}.nv-world.md`);
1791
+ const signalsPath = (0, import_path2.join)(outputDir, "signals.json");
1792
+ const overlapsPath = (0, import_path2.join)(outputDir, "overlaps.json");
1793
+ const contextsPath = (0, import_path2.join)(outputDir, "contexts.json");
1794
+ const lensesPath = (0, import_path2.join)(outputDir, "lenses.json");
1461
1795
  await Promise.all([
1462
1796
  (0, import_promises.writeFile)(worldPath, output.worldMarkdown, "utf-8"),
1463
1797
  (0, import_promises.writeFile)(signalsPath, JSON.stringify(output.signalSchema, null, 2) + "\n", "utf-8"),
@@ -1489,7 +1823,7 @@ async function cmdBuild(argv) {
1489
1823
  `);
1490
1824
  process.stderr.write(` ${BOLD}Output:${RESET} ${outputDir}/
1491
1825
  `);
1492
- process.stderr.write(` ${(0, import_path.basename)(worldPath)}${DIM} \u2014 executable world${RESET}
1826
+ process.stderr.write(` ${(0, import_path2.basename)(worldPath)}${DIM} \u2014 executable world${RESET}
1493
1827
  `);
1494
1828
  process.stderr.write(` signals.json${DIM} \u2014 signal schema${RESET}
1495
1829
  `);
@@ -1525,9 +1859,9 @@ async function cmdEmitWorld(argv) {
1525
1859
  const { model } = await loadAndParse(args.inputPath);
1526
1860
  const markdown = emitWorldMarkdown(model);
1527
1861
  if (args.output) {
1528
- const dir = (0, import_path.dirname)((0, import_path.resolve)(args.output));
1529
- if (!(0, import_fs.existsSync)(dir)) await (0, import_promises.mkdir)(dir, { recursive: true });
1530
- await (0, import_promises.writeFile)((0, import_path.resolve)(args.output), markdown, "utf-8");
1862
+ const dir = (0, import_path2.dirname)((0, import_path2.resolve)(args.output));
1863
+ if (!(0, import_fs2.existsSync)(dir)) await (0, import_promises.mkdir)(dir, { recursive: true });
1864
+ await (0, import_promises.writeFile)((0, import_path2.resolve)(args.output), markdown, "utf-8");
1531
1865
  process.stderr.write(`${GREEN}Written${RESET} ${args.output}
1532
1866
  `);
1533
1867
  } else {