intentdna 1.6.5 → 1.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +26 -15
  4. package/dist/cli/commands/compile.js +2 -47
  5. package/dist/cli/commands/context.d.ts +8 -0
  6. package/dist/cli/commands/context.js +63 -0
  7. package/dist/cli/commands/feedback.d.ts +3 -2
  8. package/dist/cli/commands/feedback.js +16 -5
  9. package/dist/cli/commands/init.js +11 -63
  10. package/dist/cli/commands/run.js +5 -4
  11. package/dist/cli/commands/show.js +2 -38
  12. package/dist/cli/commands/sync.d.ts +9 -6
  13. package/dist/cli/commands/sync.js +191 -186
  14. package/dist/cli/commands/templates.d.ts +10 -1
  15. package/dist/cli/commands/templates.js +50 -1
  16. package/dist/cli/commands/validate.js +15 -9
  17. package/dist/cli/commands/verify.d.ts +32 -0
  18. package/dist/cli/commands/verify.js +270 -31
  19. package/dist/cli/index.js +78 -11
  20. package/dist/compiler/activate.js +11 -6
  21. package/dist/compiler/cascade.d.ts +5 -1
  22. package/dist/compiler/cascade.js +74 -1
  23. package/dist/compiler/compile.js +38 -0
  24. package/dist/compiler/diagnostics.d.ts +17 -0
  25. package/dist/compiler/diagnostics.js +30 -0
  26. package/dist/compiler/index.d.ts +7 -0
  27. package/dist/compiler/index.js +13 -13
  28. package/dist/compiler/input-resolver.d.ts +32 -0
  29. package/dist/compiler/input-resolver.js +281 -0
  30. package/dist/compiler/provenance.d.ts +8 -0
  31. package/dist/compiler/provenance.js +127 -0
  32. package/dist/governance/index.d.ts +4 -3
  33. package/dist/governance/index.js +3 -4
  34. package/dist/governance/runtime-decision-event.d.ts +85 -0
  35. package/dist/governance/runtime-decision-event.js +231 -0
  36. package/dist/governance/types.d.ts +3 -3
  37. package/dist/governance/types.js +3 -3
  38. package/dist/hooks/cli.d.ts +10 -1
  39. package/dist/hooks/cli.js +199 -35
  40. package/dist/hooks/state.d.ts +5 -10
  41. package/dist/hooks/state.js +170 -2
  42. package/dist/index.d.ts +1 -0
  43. package/dist/index.js +1 -0
  44. package/dist/mcp/index.js +2 -0
  45. package/dist/mcp/tools-compile.js +18 -49
  46. package/dist/mcp/tools-context.d.ts +2 -0
  47. package/dist/mcp/tools-context.js +85 -0
  48. package/dist/mcp/tools-enforce.d.ts +2 -2
  49. package/dist/mcp/tools-enforce.js +19 -49
  50. package/dist/mcp/tools-observability.js +26 -2
  51. package/dist/mcp/tools-state.d.ts +1 -1
  52. package/dist/mcp/tools-state.js +17 -7
  53. package/dist/report/kernel-report.d.ts +27 -0
  54. package/dist/report/kernel-report.js +60 -19
  55. package/dist/report/kernel-signals.d.ts +5 -2
  56. package/dist/report/kernel-signals.js +87 -2
  57. package/dist/report/report-package.d.ts +64 -0
  58. package/dist/report/report-package.js +90 -0
  59. package/dist/runtime/agent-md.d.ts +1 -0
  60. package/dist/runtime/agent-md.js +21 -3
  61. package/dist/runtime/claude-sdk.d.ts +9 -4
  62. package/dist/runtime/claude-sdk.js +9 -0
  63. package/dist/runtime/context-sources.d.ts +14 -0
  64. package/dist/runtime/context-sources.js +60 -0
  65. package/dist/runtime/plugin-adapter.d.ts +5 -1
  66. package/dist/runtime/plugin-adapter.js +2 -0
  67. package/dist/runtime/skill-adapter.d.ts +32 -4
  68. package/dist/runtime/skill-adapter.js +184 -9
  69. package/dist/runtime/workflow-runner.d.ts +1 -1
  70. package/dist/runtime/workflow-runner.js +1 -1
  71. package/dist/schema/types.d.ts +84 -0
  72. package/dist/schema/validate.js +156 -2
  73. package/dist/schema/validators/controllers.js +16 -0
  74. package/dist/signals/index.d.ts +10 -0
  75. package/dist/signals/index.js +90 -5
  76. package/dist/templates/catalog.d.ts +19 -0
  77. package/dist/templates/catalog.js +57 -0
  78. package/dist/templates/flutter-rewrite.dna.yaml +2 -2
  79. package/package.json +1 -1
  80. package/spec/README.md +1 -1
  81. package/spec/foundation-hardening.md +2 -1
  82. package/spec/schema-spec.md +78 -1
package/dist/cli/index.js CHANGED
@@ -12,7 +12,7 @@
12
12
  * help Show this help message
13
13
  */
14
14
  import { parseArgs } from "node:util";
15
- const HELP = `Intent DNA CLI v0.3.0
15
+ const HELP = `Intent DNA CLI v1.7.0
16
16
 
17
17
  Usage: dna <command> [options]
18
18
 
@@ -29,7 +29,8 @@ Commands:
29
29
  show Show gene expression state
30
30
  evolve Preview epigenetic marker changes from persisted outcomes
31
31
  epigenetic Record outcomes, view markers and summaries
32
- feedback Build local kernel report (--days <N>, --json, --evolve preview, --dna-id <id>)
32
+ feedback Build local kernel report (--days <N>, --json, --package, --evolve preview, --dna-id <id>)
33
+ context Inspect planning-only advisory context sources
33
34
  templates Template management (deploy to global install)
34
35
 
35
36
  Options:
@@ -68,15 +69,39 @@ Examples:
68
69
  dna evolve my_dna_id Preview marker changes without persisting
69
70
  dna evolve my_dna_id --apply Unsupported in this kernel; use sync --evolve for explicit mutation
70
71
  `;
72
+ const COMMAND_HELP = {
73
+ sync: `Usage: dna sync [files...] [options]\n\nOptions:\n --mode auto|plugin|bin Sync mode (default: auto)\n --dry-run Print planned outputs without writing files\n --post-summary Print registry-backed output summary\n --target <target> Markdown target for injection\n --inject <path> File to inject markdown into\n --agents <dir> Generate agent files\n --skills <dir> Generate skill files\n --workflow <dir> Generate workflow shell scripts\n --settings <path> Write hook settings in bin mode\n --lock <path> Write lock file (default .dna/lock)\n`,
74
+ verify: `Usage: dna verify [--lock <path>] [--stats] [--health] [--strict]\n\nOptions:\n --health Comprehensive installation health check\n --stats Show hook call statistics\n --strict Require valid RuntimeDecisionEvent enterprise evidence\n --lock <path> Lock file path (default .dna/lock)\n`,
75
+ templates: `Usage: dna templates <list|show|deploy>\n\nSubcommands:\n list List built-in and project templates\n show <name> Show template metadata and YAML\n deploy Copy local templates to global install\n`,
76
+ context: `Usage: dna context <index|search|doc> [query|id] [files...]\n\nSubcommands:\n index List explicitly declared planning-only advisory context sources\n search <query> Search source metadata\n doc <id> Read a local file/doc source body\n`,
77
+ run: `Usage: dna run --dna <file> --workflow <name> --task <id> [options]\n\nOptions:\n --dry-run Print execution plan only\n --permission-mode <mode> Claude permission mode (default: default)\n`,
78
+ compile: `Usage: dna compile <files...> [--target claude-sdk] [--context <name>] [--output <path>] [--ir]\n`,
79
+ validate: `Usage: dna validate <files...> [--quiet]\n`,
80
+ show: `Usage: dna show <files...> [--context <name>] [--json]\n`,
81
+ init: `Usage: dna init [--template <name>|--template list|--register <path>|--upgrade <name>]\n`,
82
+ };
83
+ function commandHelp(command) {
84
+ return command ? COMMAND_HELP[command] : undefined;
85
+ }
71
86
  async function main() {
72
87
  const args = process.argv.slice(2);
73
88
  const command = args[0];
74
- if (!command || command === "help" || command === "--help" || command === "-h") {
89
+ if (!command || command === "--help" || command === "-h") {
75
90
  process.stderr.write(HELP);
76
91
  process.exit(0);
77
92
  }
93
+ if (command === "help") {
94
+ const help = commandHelp(args[1]);
95
+ process.stderr.write(help ?? HELP);
96
+ process.exit(0);
97
+ }
78
98
  // Parse remaining args (after command)
79
99
  const rest = args.slice(1);
100
+ if (rest.includes("--help") || rest.includes("-h")) {
101
+ const help = commandHelp(command);
102
+ process.stderr.write(help ?? HELP);
103
+ process.exit(help ? 0 : 2);
104
+ }
80
105
  switch (command) {
81
106
  case "sync": {
82
107
  const { values, positionals } = parseArgs({
@@ -96,11 +121,19 @@ async function main() {
96
121
  settings: { type: "string" },
97
122
  lock: { type: "string", default: ".dna/lock" },
98
123
  remove: { type: "boolean", default: false },
99
- plugin: { type: "boolean", default: false },
124
+ mode: { type: "string", default: "auto" },
125
+ plugin: { type: "boolean" },
126
+ "dry-run": { type: "boolean", default: false },
127
+ "post-summary": { type: "boolean", default: false },
100
128
  },
101
129
  allowPositionals: true,
102
130
  strict: false,
103
131
  });
132
+ const mode = values.mode;
133
+ if (!["auto", "plugin", "bin"].includes(mode)) {
134
+ process.stderr.write(`Error: --mode must be one of: auto, plugin, bin\n`);
135
+ process.exit(2);
136
+ }
104
137
  const { runSync } = await import("./commands/sync.js");
105
138
  const code = await runSync({
106
139
  files: positionals,
@@ -118,7 +151,10 @@ async function main() {
118
151
  settingsPath: values.settings,
119
152
  lockFile: values.lock,
120
153
  remove: values.remove,
154
+ mode: mode,
121
155
  plugin: values.plugin,
156
+ dryRun: values["dry-run"],
157
+ postSummary: values["post-summary"],
122
158
  });
123
159
  process.exit(code);
124
160
  break;
@@ -130,6 +166,7 @@ async function main() {
130
166
  lock: { type: "string", default: ".dna/lock" },
131
167
  stats: { type: "boolean", default: false },
132
168
  health: { type: "boolean", default: false },
169
+ strict: { type: "boolean", default: false },
133
170
  },
134
171
  allowPositionals: true,
135
172
  strict: false,
@@ -139,6 +176,7 @@ async function main() {
139
176
  lockFile: verifyValues.lock,
140
177
  stats: verifyValues.stats,
141
178
  health: verifyValues.health,
179
+ strict: verifyValues.strict,
142
180
  });
143
181
  process.exit(code);
144
182
  break;
@@ -413,6 +451,25 @@ async function main() {
413
451
  process.exit(code);
414
452
  break;
415
453
  }
454
+ case "context": {
455
+ const subcommand = rest[0] ?? "index";
456
+ const contextRest = rest.slice(1);
457
+ const { positionals } = parseArgs({
458
+ args: contextRest,
459
+ options: {},
460
+ allowPositionals: true,
461
+ strict: false,
462
+ });
463
+ const { runContext } = await import("./commands/context.js");
464
+ const code = await runContext({
465
+ subcommand,
466
+ query: subcommand === "search" ? positionals[0] : undefined,
467
+ id: subcommand === "doc" ? positionals[0] : undefined,
468
+ files: subcommand === "index" ? positionals : positionals.slice(1),
469
+ });
470
+ process.exit(code);
471
+ break;
472
+ }
416
473
  case "feedback": {
417
474
  const { values: fbValues } = parseArgs({
418
475
  args: rest,
@@ -420,6 +477,7 @@ async function main() {
420
477
  days: { type: "string", short: "d", default: "7" },
421
478
  json: { type: "boolean", default: false },
422
479
  evolve: { type: "boolean", default: false },
480
+ package: { type: "boolean", default: false },
423
481
  "dna-id": { type: "string" },
424
482
  },
425
483
  strict: false,
@@ -429,20 +487,29 @@ async function main() {
429
487
  days: parseInt(fbValues.days, 10) || 7,
430
488
  json: fbValues.json,
431
489
  evolve: fbValues.evolve,
490
+ packageReport: fbValues.package,
432
491
  dnaId: fbValues["dna-id"],
433
492
  });
434
493
  process.exit(code);
435
494
  break;
436
495
  }
437
496
  case "templates": {
438
- const subcommand = rest[0] ?? "";
439
- if (subcommand !== "deploy") {
440
- process.stderr.write(`Usage: dna templates deploy\n\nCopy local src/templates/*.dna.yaml to global intentdna install.\n`);
441
- process.exit(subcommand ? 2 : 0);
497
+ const subcommand = rest[0] ?? "list";
498
+ const { runTemplatesDeploy, runTemplatesList, runTemplatesShow } = await import("./commands/templates.js");
499
+ if (subcommand === "list") {
500
+ const code = await runTemplatesList();
501
+ process.exit(code);
442
502
  }
443
- const { runTemplatesDeploy } = await import("./commands/templates.js");
444
- const code = await runTemplatesDeploy();
445
- process.exit(code);
503
+ if (subcommand === "show") {
504
+ const code = await runTemplatesShow({ name: rest[1] ?? "" });
505
+ process.exit(code);
506
+ }
507
+ if (subcommand === "deploy") {
508
+ const code = await runTemplatesDeploy();
509
+ process.exit(code);
510
+ }
511
+ process.stderr.write(COMMAND_HELP.templates);
512
+ process.exit(2);
446
513
  break;
447
514
  }
448
515
  default:
@@ -10,7 +10,9 @@
10
10
  /**
11
11
  * Apply a modifier to a gene's expression level.
12
12
  */
13
- function applyModifier(level, mod) {
13
+ function applyModifier(level, mod, protectedThreshold = false) {
14
+ if (protectedThreshold && (mod.action === "deactivate" || mod.action === "suppress"))
15
+ return Math.max(level, 1.0);
14
16
  switch (mod.action) {
15
17
  case "amplify":
16
18
  return level * (mod.factor ?? 2.0);
@@ -28,7 +30,7 @@ function applyModifier(level, mod) {
28
30
  * Apply epigenetic markers to gene expression levels.
29
31
  * Markers decay over time based on their decay factor and age.
30
32
  */
31
- function applyEpigeneticMarkers(genes, markers) {
33
+ function applyEpigeneticMarkers(genes, markers, protectedThresholdGenes) {
32
34
  const now = Date.now();
33
35
  for (const marker of markers) {
34
36
  // Calculate decay based on time elapsed
@@ -40,6 +42,8 @@ function applyEpigeneticMarkers(genes, markers) {
40
42
  continue; // marker too old, skip
41
43
  for (const effect of marker.effects) {
42
44
  if (effect.gene && genes[effect.gene] && effect.action && effect.factor) {
45
+ if (protectedThresholdGenes.has(effect.gene) && effect.action === "suppress")
46
+ continue;
43
47
  const gene = genes[effect.gene];
44
48
  const effectiveFactor = 1 + (effect.factor - 1) * decayedStrength;
45
49
  if (effect.action === "amplify") {
@@ -77,18 +81,19 @@ export function activateDNA(cascaded, contextName = null, activeRole = null) {
77
81
  role: gene.role,
78
82
  };
79
83
  }
84
+ const protectedThresholdGenes = new Set(cascaded.protected_threshold_genes ?? []);
80
85
  // Apply context modifiers if a context is active
81
86
  if (contextName && cascaded.contexts[contextName]) {
82
87
  const ctx = cascaded.contexts[contextName];
83
88
  // Apply gene modifiers
84
89
  for (const mod of ctx.modifiers ?? []) {
85
90
  if (activated[mod.gene]) {
86
- activated[mod.gene].expression_level = applyModifier(activated[mod.gene].expression_level, mod);
91
+ activated[mod.gene].expression_level = applyModifier(activated[mod.gene].expression_level, mod, protectedThresholdGenes.has(mod.gene));
87
92
  }
88
93
  }
89
94
  // Deactivate genes
90
95
  for (const g of ctx.deactivate_genes ?? []) {
91
- if (activated[g]) {
96
+ if (activated[g] && !protectedThresholdGenes.has(g)) {
92
97
  activated[g].expression_level = 0;
93
98
  }
94
99
  }
@@ -125,13 +130,13 @@ export function activateDNA(cascaded, contextName = null, activeRole = null) {
125
130
  }
126
131
  // Suppress genes
127
132
  for (const geneName of role.suppresses_genes ?? []) {
128
- if (activated[geneName]) {
133
+ if (activated[geneName] && !protectedThresholdGenes.has(geneName)) {
129
134
  activated[geneName].expression_level *= 0.1;
130
135
  }
131
136
  }
132
137
  }
133
138
  // Apply epigenetic markers
134
- applyEpigeneticMarkers(activated, cascaded.epigenetic_markers);
139
+ applyEpigeneticMarkers(activated, cascaded.epigenetic_markers, protectedThresholdGenes);
135
140
  return {
136
141
  genes: activated,
137
142
  active_context: contextName,
@@ -10,7 +10,7 @@
10
10
  * - attract/repel: same target accumulates, different targets independent
11
11
  * - amplify/suppress: factors multiply (1.5x × 2.0x = 3.0x)
12
12
  */
13
- import type { IntentDNA, Gene, ContextRegion, EpigeneticMarker, RoleDef, WorkflowDef, LegibilityAssetMap, VerifierCommandPolicy, ControllerDef } from "../schema/types.js";
13
+ import type { IntentDNA, Gene, ContextRegion, EpigeneticMarker, RoleDef, WorkflowDef, LegibilityAssetMap, VerifierCommandPolicy, ControllerDef, AdvisoryContextSource, PlanningContext, CompiledProvenanceManifest } from "../schema/types.js";
14
14
  export interface CascadedDNA {
15
15
  genes: Record<string, Gene>;
16
16
  contexts: Record<string, ContextRegion>;
@@ -27,7 +27,11 @@ export interface CascadedDNA {
27
27
  per_role?: Record<string, string[]>;
28
28
  per_workflow?: Record<string, string[]>;
29
29
  };
30
+ context_sources?: AdvisoryContextSource[];
31
+ planning_context?: PlanningContext;
30
32
  verifier_policy?: VerifierCommandPolicy;
33
+ provenance?: CompiledProvenanceManifest;
34
+ protected_threshold_genes?: string[];
31
35
  }
32
36
  /**
33
37
  * Cascade multiple DNA layers into a single merged DNA.
@@ -10,6 +10,21 @@
10
10
  * - attract/repel: same target accumulates, different targets independent
11
11
  * - amplify/suppress: factors multiply (1.5x × 2.0x = 3.0x)
12
12
  */
13
+ const PROTECTED_THRESHOLD_LAYERS = new Set(["species", "enterprise"]);
14
+ function originForDNA(dna) {
15
+ return {
16
+ source_dna_id: dna.id,
17
+ source_layer: dna.type,
18
+ cascade_priority: dna.cascade.priority,
19
+ protected_threshold: PROTECTED_THRESHOLD_LAYERS.has(dna.type),
20
+ };
21
+ }
22
+ function withCodonOrigins(gene, origin) {
23
+ return {
24
+ ...gene,
25
+ codons: gene.codons.map((codon) => codon.type === "threshold" ? { ...codon, origin } : { ...codon }),
26
+ };
27
+ }
13
28
  /**
14
29
  * Normalize workflow/workflows from an IntentDNA into a unified Record.
15
30
  * Validation ensures workflow and workflows are mutually exclusive.
@@ -156,6 +171,47 @@ function mergeContextFiles(base, incoming) {
156
171
  per_workflow: mergePrefixedRecords(base.per_workflow, incoming.per_workflow),
157
172
  };
158
173
  }
174
+ function prefixContextSources(sources, ns) {
175
+ if (!sources)
176
+ return undefined;
177
+ if (!ns)
178
+ return sources;
179
+ return sources.map((source) => ({ ...source, id: `${ns}_${source.id}` }));
180
+ }
181
+ function prefixPlanningContext(planning, ns) {
182
+ if (!planning)
183
+ return undefined;
184
+ if (!ns)
185
+ return planning;
186
+ return {
187
+ ...planning,
188
+ sources: planning.sources.map((sourceId) => `${ns}_${sourceId}`),
189
+ };
190
+ }
191
+ function mergeContextSources(base, incoming) {
192
+ if (!base)
193
+ return incoming;
194
+ if (!incoming)
195
+ return base;
196
+ const merged = new Map();
197
+ for (const source of base)
198
+ merged.set(source.id, source);
199
+ for (const source of incoming)
200
+ merged.set(source.id, source);
201
+ return [...merged.values()];
202
+ }
203
+ function mergePlanningContext(base, incoming) {
204
+ if (!base)
205
+ return incoming;
206
+ if (!incoming)
207
+ return base;
208
+ return {
209
+ sources: incoming.sources,
210
+ instructions: incoming.instructions ?? base.instructions,
211
+ require_quotes: incoming.require_quotes ?? base.require_quotes,
212
+ require_attribution: incoming.require_attribution ?? base.require_attribution,
213
+ };
214
+ }
159
215
  function mergeVerifierPolicy(base, incoming) {
160
216
  if (!base)
161
217
  return incoming;
@@ -187,6 +243,7 @@ export function cascadeDNA(layers) {
187
243
  const sorted = [...layers].sort((a, b) => a.cascade.priority - b.cascade.priority);
188
244
  const mergedGenes = {};
189
245
  const geneSourceNs = {}; // track which namespace defined each gene
246
+ const protectedThresholdGenes = new Set();
190
247
  const mergedContexts = {};
191
248
  const mergedRoles = {};
192
249
  const mergedWorkflows = {};
@@ -198,7 +255,12 @@ export function cascadeDNA(layers) {
198
255
  sourceIds.push(dna.id);
199
256
  const ns = dna.namespace;
200
257
  // Merge genes (cross-namespace: additive merge + warning)
201
- for (const [name, gene] of Object.entries(dna.genes)) {
258
+ const origin = originForDNA(dna);
259
+ for (const [name, rawGene] of Object.entries(dna.genes)) {
260
+ const gene = withCodonOrigins(rawGene, origin);
261
+ if (origin.protected_threshold && gene.codons.some((codon) => codon.type === "threshold")) {
262
+ protectedThresholdGenes.add(name);
263
+ }
202
264
  if (mergedGenes[name]) {
203
265
  const existingNs = geneSourceNs[name];
204
266
  if (ns !== existingNs) {
@@ -257,6 +319,8 @@ export function cascadeDNA(layers) {
257
319
  // legibility_assets/context_files: highest-priority DNA wins (last in sorted layers)
258
320
  let legibilityAssets;
259
321
  let contextFiles;
322
+ let contextSources;
323
+ let planningContext;
260
324
  let verifierPolicy;
261
325
  for (const dna of sorted) {
262
326
  if (dna.legibility_assets) {
@@ -265,6 +329,12 @@ export function cascadeDNA(layers) {
265
329
  if (dna.context_files) {
266
330
  contextFiles = mergeContextFiles(contextFiles, prefixContextFiles(dna.context_files, dna.namespace));
267
331
  }
332
+ if (dna.context_sources) {
333
+ contextSources = mergeContextSources(contextSources, prefixContextSources(dna.context_sources, dna.namespace));
334
+ }
335
+ if (dna.planning_context) {
336
+ planningContext = mergePlanningContext(planningContext, prefixPlanningContext(dna.planning_context, dna.namespace));
337
+ }
268
338
  if (dna.verifier_policy) {
269
339
  verifierPolicy = mergeVerifierPolicy(verifierPolicy, dna.verifier_policy);
270
340
  }
@@ -280,6 +350,9 @@ export function cascadeDNA(layers) {
280
350
  warnings: warnings.length > 0 ? warnings : undefined,
281
351
  legibility_assets: legibilityAssets,
282
352
  context_files: contextFiles,
353
+ context_sources: contextSources,
354
+ planning_context: planningContext,
283
355
  verifier_policy: verifierPolicy,
356
+ protected_threshold_genes: protectedThresholdGenes.size > 0 ? [...protectedThresholdGenes].sort() : undefined,
284
357
  };
285
358
  }
@@ -105,6 +105,7 @@ function geneToGates(name, gene) {
105
105
  action: "block",
106
106
  message: `DNA threshold violation: ${codon.condition} (gene: ${name})`,
107
107
  source_gene: name,
108
+ origin: codon.origin,
108
109
  });
109
110
  }
110
111
  if (codon.type === "sense" && codon.response === "escalate_to_human") {
@@ -172,6 +173,39 @@ function legibilityAssetsToContextFiles(assets) {
172
173
  return undefined;
173
174
  return { mandatory, per_role, per_workflow };
174
175
  }
176
+ function compileAdvisoryContext(cascaded) {
177
+ const declaredSources = cascaded?.context_sources ?? [];
178
+ const planning = cascaded?.planning_context;
179
+ const plannedSourceIds = planning?.sources ?? declaredSources.map((source) => source.id);
180
+ const sourceById = new Map(declaredSources.map((source) => [source.id, source]));
181
+ const sources = plannedSourceIds
182
+ .map((sourceId) => sourceById.get(sourceId))
183
+ .filter((source) => Boolean(source))
184
+ .map((source) => ({
185
+ ...source,
186
+ usage: "planning_only",
187
+ non_enforcing: true,
188
+ requires_quotes: planning?.require_quotes ?? true,
189
+ requires_attribution: planning?.require_attribution ?? true,
190
+ }));
191
+ const compiledPlanning = planning ? {
192
+ sources: [...planning.sources],
193
+ instructions: planning.instructions ? [...planning.instructions] : undefined,
194
+ require_quotes: planning.require_quotes ?? true,
195
+ require_attribution: planning.require_attribution ?? true,
196
+ non_enforcing: true,
197
+ } : undefined;
198
+ const injections = sources.length > 0 ? [{
199
+ type: "preference",
200
+ text: `Advisory context sources are available for planning only: ${sources.map((source) => source.id).join(", ")}. Cite sources and quote relevant passages; these sources do not satisfy enforcement or handoff requirements.`,
201
+ relevance_tags: ["advisory_context", "planning_only"],
202
+ }] : [];
203
+ return {
204
+ sources: sources.length > 0 ? sources : undefined,
205
+ planning: compiledPlanning,
206
+ injections,
207
+ };
208
+ }
175
209
  function resolvePostCheckCommand(check) {
176
210
  const normalized = check.trim();
177
211
  const builtins = {
@@ -362,6 +396,8 @@ export function compileDNA(activated, cascaded) {
362
396
  directives.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
363
397
  const legacyContextFiles = cascaded?.context_files;
364
398
  const compiledContextFiles = legacyContextFiles ?? legibilityAssetsToContextFiles(cascaded?.legibility_assets);
399
+ const advisoryContext = compileAdvisoryContext(cascaded);
400
+ injections.push(...advisoryContext.injections);
365
401
  return {
366
402
  prompt_directives: directives,
367
403
  tool_filters: toolFilters,
@@ -369,6 +405,8 @@ export function compileDNA(activated, cascaded) {
369
405
  post_execution_validators: validators,
370
406
  verifier_specs: verifierSpecs.length > 0 ? verifierSpecs : undefined,
371
407
  context_injections: injections,
408
+ context_sources: advisoryContext.sources,
409
+ planning_context: advisoryContext.planning,
372
410
  source_dna_ids: activated.source_ids,
373
411
  compiled_at: new Date().toISOString(),
374
412
  active_context: activated.active_context ?? undefined,
@@ -0,0 +1,17 @@
1
+ export type DiagnosticSeverity = "error" | "warning" | "info";
2
+ export interface Diagnostic {
3
+ severity: DiagnosticSeverity;
4
+ code: string;
5
+ message: string;
6
+ file?: string;
7
+ path?: string;
8
+ hint?: string;
9
+ }
10
+ export declare class DNADiagnosticsError extends Error {
11
+ readonly diagnostics: Diagnostic[];
12
+ constructor(message: string, diagnostics: Diagnostic[]);
13
+ }
14
+ export declare function hasDiagnosticErrors(diagnostics: Diagnostic[]): boolean;
15
+ export declare function formatDiagnostic(diagnostic: Diagnostic): string;
16
+ export declare function formatDiagnostics(diagnostics: Diagnostic[]): string;
17
+ export declare function diagnosticsFromError(error: unknown, file?: string): Diagnostic[];
@@ -0,0 +1,30 @@
1
+ export class DNADiagnosticsError extends Error {
2
+ diagnostics;
3
+ constructor(message, diagnostics) {
4
+ super(message);
5
+ this.name = "DNADiagnosticsError";
6
+ this.diagnostics = diagnostics;
7
+ }
8
+ }
9
+ export function hasDiagnosticErrors(diagnostics) {
10
+ return diagnostics.some((diagnostic) => diagnostic.severity === "error");
11
+ }
12
+ export function formatDiagnostic(diagnostic) {
13
+ const location = [diagnostic.file, diagnostic.path].filter(Boolean).join(":");
14
+ const prefix = location ? `${location}: ` : "";
15
+ const hint = diagnostic.hint ? ` (${diagnostic.hint})` : "";
16
+ return `${diagnostic.severity.toUpperCase()} ${diagnostic.code}: ${prefix}${diagnostic.message}${hint}`;
17
+ }
18
+ export function formatDiagnostics(diagnostics) {
19
+ return diagnostics.map(formatDiagnostic).join("\n");
20
+ }
21
+ export function diagnosticsFromError(error, file) {
22
+ if (error instanceof DNADiagnosticsError)
23
+ return error.diagnostics;
24
+ return [{
25
+ severity: "error",
26
+ code: "unexpected_error",
27
+ message: error instanceof Error ? error.message : String(error),
28
+ file,
29
+ }];
30
+ }
@@ -9,7 +9,12 @@ export { cascadeDNA } from "./cascade.js";
9
9
  export { activateDNA } from "./activate.js";
10
10
  export { compileDNA } from "./compile.js";
11
11
  export { compileWorkflow } from "./workflow.js";
12
+ export { PROVENANCE_LAYERS, PROVENANCE_SCHEMA_VERSION, attachProvenance, buildCascadeLayers, buildConstraintWinners, hashObject, hashString, } from "./provenance.js";
12
13
  export type { CompileWorkflowOptions, CompileWorkflowResult, CompileWorkflowError, } from "./workflow.js";
14
+ export type { Diagnostic, DiagnosticSeverity } from "./diagnostics.js";
15
+ export { DNADiagnosticsError, formatDiagnostic, formatDiagnostics } from "./diagnostics.js";
16
+ export { assertNoNamespaceCollisions, checkNamespaceCollisions, detectDNAConfigs, expandDNAInputFiles, expandDNAInputs, loadDNAWithDiagnostics, resolveDNAInputs, resolveSpeciesReference, } from "./input-resolver.js";
17
+ export type { ExpandedDNAInput, ExpandDNAInputOptions } from "./input-resolver.js";
13
18
  /**
14
19
  * Load and parse a DNA file from disk.
15
20
  */
@@ -17,6 +22,8 @@ export declare function loadDNA(filePath: string): Promise<IntentDNA>;
17
22
  export interface CompileOptions {
18
23
  context?: string;
19
24
  role?: string;
25
+ cwd?: string;
26
+ enterprisePolicyDirs?: string[];
20
27
  /** Additional epigenetic markers from evolution store */
21
28
  epigenetic_markers?: EpigeneticMarker[];
22
29
  }
@@ -4,29 +4,28 @@
4
4
  * Full compilation pipeline:
5
5
  * DNA files → parse → cascade → activate (+ epigenetic) → compile → IR
6
6
  */
7
- import { readFile } from "node:fs/promises";
8
- import { validateDNA } from "../schema/validate.js";
9
- import { parseYAML } from "../schema/yaml-parser.js";
10
7
  import { cascadeDNA } from "./cascade.js";
11
8
  import { activateDNA } from "./activate.js";
12
9
  import { compileDNA } from "./compile.js";
10
+ import { DNADiagnosticsError, formatDiagnostics } from "./diagnostics.js";
11
+ import { expandDNAInputs, loadDNAWithDiagnostics } from "./input-resolver.js";
12
+ import { attachProvenance } from "./provenance.js";
13
13
  export { cascadeDNA } from "./cascade.js";
14
14
  export { activateDNA } from "./activate.js";
15
15
  export { compileDNA } from "./compile.js";
16
16
  export { compileWorkflow } from "./workflow.js";
17
+ export { PROVENANCE_LAYERS, PROVENANCE_SCHEMA_VERSION, attachProvenance, buildCascadeLayers, buildConstraintWinners, hashObject, hashString, } from "./provenance.js";
18
+ export { DNADiagnosticsError, formatDiagnostic, formatDiagnostics } from "./diagnostics.js";
19
+ export { assertNoNamespaceCollisions, checkNamespaceCollisions, detectDNAConfigs, expandDNAInputFiles, expandDNAInputs, loadDNAWithDiagnostics, resolveDNAInputs, resolveSpeciesReference, } from "./input-resolver.js";
17
20
  /**
18
21
  * Load and parse a DNA file from disk.
19
22
  */
20
23
  export async function loadDNA(filePath) {
21
- const raw = await readFile(filePath, "utf-8");
22
- const isYAML = filePath.endsWith(".yaml") || filePath.endsWith(".yml");
23
- const dna = (isYAML ? parseYAML(raw) : JSON.parse(raw));
24
- const result = validateDNA(dna);
25
- if (!result.valid) {
26
- const messages = result.errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
27
- throw new Error(`Invalid DNA file ${filePath}:\n${messages}`);
24
+ const result = await loadDNAWithDiagnostics(filePath);
25
+ if (!result.dna) {
26
+ throw new DNADiagnosticsError(`Invalid DNA file ${filePath}:\n${formatDiagnostics(result.diagnostics)}`, result.diagnostics);
28
27
  }
29
- return dna;
28
+ return result.dna;
30
29
  }
31
30
  /**
32
31
  * Full compilation pipeline: load DNA files, cascade, activate context, compile to IR.
@@ -36,8 +35,9 @@ export async function compileFromFiles(filePaths, contextOrOptions) {
36
35
  const opts = typeof contextOrOptions === "string"
37
36
  ? { context: contextOrOptions }
38
37
  : contextOrOptions ?? {};
38
+ const expanded = await expandDNAInputs(filePaths, { cwd: opts.cwd, enterprisePolicyDirs: opts.enterprisePolicyDirs });
39
39
  const dnas = [];
40
- for (const path of filePaths) {
40
+ for (const path of expanded.files) {
41
41
  dnas.push(await loadDNA(path));
42
42
  }
43
43
  const cascaded = cascadeDNA(dnas);
@@ -46,5 +46,5 @@ export async function compileFromFiles(filePaths, contextOrOptions) {
46
46
  cascaded.epigenetic_markers.push(...opts.epigenetic_markers);
47
47
  }
48
48
  const activated = activateDNA(cascaded, opts.context ?? null, opts.role ?? null);
49
- return compileDNA(activated, cascaded);
49
+ return attachProvenance(compileDNA(activated, cascaded), expanded.provenance);
50
50
  }
@@ -0,0 +1,32 @@
1
+ import type { DNASourceProvenance, IntentDNA } from "../schema/types.js";
2
+ import type { Diagnostic } from "./diagnostics.js";
3
+ export interface ResolveDNAInputOptions {
4
+ cwd?: string;
5
+ includeDiagnostics?: boolean;
6
+ }
7
+ export interface ResolvedDNAInputs {
8
+ files: string[];
9
+ dnas: IntentDNA[];
10
+ diagnostics: Diagnostic[];
11
+ }
12
+ export declare function parseDNAFile(filePath: string): Promise<IntentDNA>;
13
+ export declare function resolveSpeciesReference(ref: string): string | null;
14
+ export declare function detectDNAConfigs(projectDir?: string): Promise<string[]>;
15
+ export interface ExpandDNAInputOptions {
16
+ cwd?: string;
17
+ enterprisePolicyDirs?: string[];
18
+ }
19
+ export interface ExpandedDNAInput {
20
+ files: string[];
21
+ provenance: DNASourceProvenance[];
22
+ diagnostics: Diagnostic[];
23
+ }
24
+ export declare function expandDNAInputs(files: string[], options?: ExpandDNAInputOptions): Promise<ExpandedDNAInput>;
25
+ export declare function expandDNAInputFiles(files: string[], options?: ExpandDNAInputOptions): Promise<string[]>;
26
+ export declare function checkNamespaceCollisions(configPaths: string[]): Promise<Diagnostic[]>;
27
+ export declare function assertNoNamespaceCollisions(configPaths: string[]): Promise<void>;
28
+ export declare function loadDNAWithDiagnostics(filePath: string): Promise<{
29
+ dna?: IntentDNA;
30
+ diagnostics: Diagnostic[];
31
+ }>;
32
+ export declare function resolveDNAInputs(files: string[], options?: ResolveDNAInputOptions): Promise<ResolvedDNAInputs>;