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
@@ -5,7 +5,7 @@
5
5
  * - dna_status: Overview of DNA state
6
6
  * - dna_workflow_read: Read current workflow state
7
7
  * - dna_workflow_write: Update workflow state
8
- * - dna_trace_query: Query trace data with filters
8
+ * - dna_trace_query: Query legacy diagnostic trace data with filters
9
9
  */
10
10
  import type { ToolDef } from "./server.js";
11
11
  export declare function createStateTools(projectDir: string): ToolDef[];
@@ -5,10 +5,11 @@
5
5
  * - dna_status: Overview of DNA state
6
6
  * - dna_workflow_read: Read current workflow state
7
7
  * - dna_workflow_write: Update workflow state
8
- * - dna_trace_query: Query trace data with filters
8
+ * - dna_trace_query: Query legacy diagnostic trace data with filters
9
9
  */
10
10
  import { resolve } from "node:path";
11
- import { readWorkflowState, writeWorkflowState, readTraces, listSessions } from "../hooks/state.js";
11
+ import { readWorkflowState, writeWorkflowState, readRuntimeDecisionEvents, readTraces, listSessions } from "../hooks/state.js";
12
+ import { validateRuntimeDecisionEvent } from "../governance/index.js";
12
13
  import { loadCompiledIR } from "../runtime/plugin-adapter.js";
13
14
  import { textResult } from "./server.js";
14
15
  function isStringRecord(value) {
@@ -20,7 +21,7 @@ export function createStateTools(projectDir) {
20
21
  // ── dna_status ────────────────────────────────────────
21
22
  {
22
23
  name: "dna_status",
23
- description: "Overview of DNA governance state: compiled IR info, active workflow, session list, and recent enforcement stats",
24
+ description: "Overview of DNA governance state: compiled IR info, active workflow, sessions, RuntimeDecisionEvent evidence, and legacy diagnostic trace stats",
24
25
  inputSchema: { type: "object", properties: {} },
25
26
  handler: async () => {
26
27
  const lines = [];
@@ -58,12 +59,21 @@ export function createStateTools(projectDir) {
58
59
  const preview = sessions.slice(0, 5).join(", ");
59
60
  lines.push(`\nSessions: ${sessions.length} (${preview}${sessions.length > 5 ? "..." : ""})`);
60
61
  }
62
+ // Recent RuntimeDecisionEvent stats
63
+ const runtimeDecisions = await readRuntimeDecisionEvents(projectDir, 1);
64
+ if (runtimeDecisions.length > 0) {
65
+ const enterprise = runtimeDecisions.filter(e => validateRuntimeDecisionEvent(e).classification === "enterprise_evidence");
66
+ const blocks = enterprise.filter(e => e.decision === "block").length;
67
+ const warns = enterprise.filter(e => e.decision === "warn").length;
68
+ const escalates = enterprise.filter(e => e.decision === "escalate").length;
69
+ lines.push(`\nRuntimeDecisionEvent (24h): ${runtimeDecisions.length} events, ${enterprise.length} enterprise evidence, ${blocks} blocks, ${warns} warns, ${escalates} escalates`);
70
+ }
61
71
  // Recent trace stats
62
72
  const traces = await readTraces(projectDir, 1);
63
73
  if (traces.length > 0) {
64
74
  const blocks = traces.filter(t => t.decision === "block").length;
65
75
  const warns = traces.filter(t => t.decision === "warn").length;
66
- lines.push(`\nTrace (24h): ${traces.length} events, ${blocks} blocks, ${warns} warns`);
76
+ lines.push(`\nLegacy trace diagnostics (24h): ${traces.length} events, ${blocks} blocks, ${warns} warns`);
67
77
  }
68
78
  return textResult(lines.join("\n"));
69
79
  },
@@ -71,7 +81,7 @@ export function createStateTools(projectDir) {
71
81
  // ── dna_workflow_read ─────────────────────────────────
72
82
  {
73
83
  name: "dna_workflow_read",
74
- description: "Read current workflow state (step, role, iteration, artifacts)",
84
+ description: "Read current workflow runtime state (diagnostic/cache only; not enterprise evidence)",
75
85
  inputSchema: {
76
86
  type: "object",
77
87
  properties: {
@@ -126,7 +136,7 @@ export function createStateTools(projectDir) {
126
136
  // ── dna_trace_query ──────────────────────────────────
127
137
  {
128
138
  name: "dna_trace_query",
129
- description: "Query enforcement trace data with filters (blocks, warns, by tool, by path)",
139
+ description: "Query legacy diagnostic enforcement trace data with filters. Trace records are not enterprise evidence unless referenced by RuntimeDecisionEvent.",
130
140
  inputSchema: {
131
141
  type: "object",
132
142
  properties: {
@@ -150,7 +160,7 @@ export function createStateTools(projectDir) {
150
160
  traces = traces.filter(t => t.tool_name === toolName);
151
161
  const total = traces.length;
152
162
  traces = traces.slice(-limit);
153
- const lines = [`Trace: ${total} matching entries (showing last ${traces.length})`];
163
+ const lines = [`Legacy diagnostic trace: ${total} matching entries (showing last ${traces.length}); not enterprise evidence unless referenced by RuntimeDecisionEvent`];
154
164
  for (const t of traces) {
155
165
  const parts = [t.timestamp.slice(11, 19), t.event, t.decision];
156
166
  if (t.tool_name)
@@ -18,6 +18,9 @@ export interface KernelReport {
18
18
  period_days: number;
19
19
  };
20
20
  summary: {
21
+ runtime_decisions: number;
22
+ enterprise_evidence: number;
23
+ legacy_diagnostics: number;
21
24
  traces: number;
22
25
  verifier_results: number;
23
26
  signals: number;
@@ -32,9 +35,33 @@ export interface KernelReport {
32
35
  blocks: number;
33
36
  warns: number;
34
37
  allows: number;
38
+ escalates: number;
39
+ validates: number;
35
40
  policy_denied_verifiers: number;
36
41
  };
37
42
  signals: ClassifiedSignal[];
43
+ runtime_decision_summary: {
44
+ total: number;
45
+ enterprise_evidence: number;
46
+ diagnostics: number;
47
+ blocks: number;
48
+ warns: number;
49
+ allows: number;
50
+ escalates: number;
51
+ validates: number;
52
+ top_policy_bundles: Array<{
53
+ policy_bundle_id: string;
54
+ count: number;
55
+ }>;
56
+ top_constraints: Array<{
57
+ constraint_id: string;
58
+ count: number;
59
+ }>;
60
+ top_decision_reasons: Array<{
61
+ reason: string;
62
+ count: number;
63
+ }>;
64
+ };
38
65
  verifier_summary: {
39
66
  total: number;
40
67
  passes: number;
@@ -1,6 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
- import { readTraces, readVerifierResults, safePathComponent } from "../hooks/state.js";
3
+ import { readRuntimeDecisionEvents, readTraces, readVerifierResults, safePathComponent } from "../hooks/state.js";
4
+ import { validateRuntimeDecisionEvent } from "../governance/index.js";
4
5
  import { describeMarkerChanges } from "../evolution/marker-gen.js";
5
6
  import { classifiedSignalsToOutcomes, classifyKernelSignals, } from "./kernel-signals.js";
6
7
  const CLASSIFICATIONS = [
@@ -50,6 +51,30 @@ function buildVerifierSummary(results) {
50
51
  failures: results.filter((result) => result.status === "fail").length,
51
52
  };
52
53
  }
54
+ function buildRuntimeDecisionSummary(events) {
55
+ const enterpriseEvidence = events.filter((event) => validateRuntimeDecisionEvent(event).classification === "enterprise_evidence");
56
+ const blocks = enterpriseEvidence.filter((event) => event.decision === "block");
57
+ const warns = enterpriseEvidence.filter((event) => event.decision === "warn").length;
58
+ const allows = enterpriseEvidence.filter((event) => event.decision === "allow").length;
59
+ const escalates = enterpriseEvidence.filter((event) => event.decision === "escalate").length;
60
+ const validates = enterpriseEvidence.filter((event) => event.decision === "validate").length;
61
+ const reasons = blocks
62
+ .map((event) => event.decision_reason.split("\n")[0].replace(/^\[Intent DNA\]\s*/, "").slice(0, 100))
63
+ .filter((reason) => Boolean(reason));
64
+ return {
65
+ total: events.length,
66
+ enterprise_evidence: enterpriseEvidence.length,
67
+ diagnostics: events.length - enterpriseEvidence.length,
68
+ blocks: blocks.length,
69
+ warns,
70
+ allows,
71
+ escalates,
72
+ validates,
73
+ top_policy_bundles: sortedCounts(enterpriseEvidence.map((event) => event.policy_bundle_id), 5, "policy_bundle_id"),
74
+ top_constraints: sortedCounts(enterpriseEvidence.map((event) => event.winning_constraint.constraint_id), 5, "constraint_id"),
75
+ top_decision_reasons: sortedCounts(reasons, 5, "reason"),
76
+ };
77
+ }
53
78
  async function readCompiledSourceIds(projectDir) {
54
79
  try {
55
80
  const raw = await readFile(join(projectDir, ".dna", "compiled", "ir.json"), "utf-8");
@@ -136,13 +161,15 @@ async function buildMarkerPreview(projectDir, explicitDnaId, signals) {
136
161
  }
137
162
  export async function buildKernelReport(options) {
138
163
  const days = options.days ?? 7;
164
+ const runtimeDecisions = await readRuntimeDecisionEvents(options.projectDir, days, options.sessionId);
139
165
  const traces = await readTraces(options.projectDir, days, options.sessionId);
140
166
  const verifierResults = (await readVerifierResults(options.projectDir, options.sessionId))
141
167
  .filter((result) => withinDays(result.timestamp, days));
142
- const signals = classifyKernelSignals({ traces, verifierResults });
168
+ const signals = classifyKernelSignals({ runtimeDecisions, traces, verifierResults });
143
169
  const classifications = Object.fromEntries(CLASSIFICATIONS.map((classification) => [classification, 0]));
144
170
  for (const signal of signals)
145
171
  classifications[signal.classification]++;
172
+ const runtimeDecisionSummary = buildRuntimeDecisionSummary(runtimeDecisions);
146
173
  const traceSummary = buildTraceSummary(traces);
147
174
  const verifierSummary = buildVerifierSummary(verifierResults);
148
175
  const markerPreview = options.includeMarkerPreview
@@ -159,6 +186,9 @@ export async function buildKernelReport(options) {
159
186
  period_days: days,
160
187
  },
161
188
  summary: {
189
+ runtime_decisions: runtimeDecisions.length,
190
+ enterprise_evidence: runtimeDecisionSummary.enterprise_evidence,
191
+ legacy_diagnostics: traces.length + verifierResults.length + runtimeDecisionSummary.diagnostics,
162
192
  traces: traces.length,
163
193
  verifier_results: verifierResults.length,
164
194
  signals: signals.length,
@@ -166,16 +196,19 @@ export async function buildKernelReport(options) {
166
196
  },
167
197
  classifications,
168
198
  quality: {
169
- quality_positive: signals.filter((signal) => signal.semantic === "quality-positive").length,
170
- quality_negative: signals.filter((signal) => signal.semantic === "quality-negative").length,
199
+ quality_positive: 0,
200
+ quality_negative: 0,
171
201
  },
172
202
  enforcement: {
173
- blocks: signals.filter((signal) => signal.semantic === "enforcement-block").length,
174
- warns: signals.filter((signal) => signal.semantic === "enforcement-warn").length,
175
- allows: signals.filter((signal) => signal.semantic === "enforcement-allow").length,
176
- policy_denied_verifiers: signals.filter((signal) => signal.semantic === "policy-denied-verifier").length,
203
+ blocks: runtimeDecisionSummary.blocks,
204
+ warns: runtimeDecisionSummary.warns,
205
+ allows: runtimeDecisionSummary.allows,
206
+ escalates: runtimeDecisionSummary.escalates,
207
+ validates: runtimeDecisionSummary.validates,
208
+ policy_denied_verifiers: 0,
177
209
  },
178
210
  signals,
211
+ runtime_decision_summary: runtimeDecisionSummary,
179
212
  verifier_summary: verifierSummary,
180
213
  trace_summary: traceSummary,
181
214
  marker_preview: markerPreview,
@@ -194,15 +227,23 @@ export function formatKernelReport(report) {
194
227
  lines.push("=".repeat(50));
195
228
  lines.push("");
196
229
  lines.push(`Schema: ${report.schema_version}`);
197
- lines.push(`Total events: ${report.trace_summary.total}`);
198
- lines.push(` Allowed: ${report.trace_summary.allows}`);
199
- lines.push(` Blocked: ${report.trace_summary.blocks}`);
200
- lines.push(` Warned: ${report.trace_summary.warns}`);
230
+ lines.push(`RuntimeDecisionEvents: ${report.runtime_decision_summary.total}`);
231
+ lines.push(` Enterprise evidence: ${report.runtime_decision_summary.enterprise_evidence}`);
232
+ lines.push(` Diagnostic-only events: ${report.runtime_decision_summary.diagnostics}`);
233
+ lines.push(` Allowed: ${report.runtime_decision_summary.allows}`);
234
+ lines.push(` Blocked: ${report.runtime_decision_summary.blocks}`);
235
+ lines.push(` Warned: ${report.runtime_decision_summary.warns}`);
236
+ lines.push(` Escalated: ${report.runtime_decision_summary.escalates}`);
237
+ lines.push(` Validated: ${report.runtime_decision_summary.validates}`);
201
238
  lines.push("");
202
- if (report.verifier_summary.total > 0) {
203
- lines.push(`Verifier results: ${report.verifier_summary.total}`);
204
- lines.push(` Passed: ${report.verifier_summary.passes}`);
205
- lines.push(` Failed: ${report.verifier_summary.failures}`);
239
+ if (report.trace_summary.total > 0 || report.verifier_summary.total > 0) {
240
+ lines.push("Legacy diagnostics (not enterprise evidence without RuntimeDecisionEvent provenance):");
241
+ lines.push(` Traces: ${report.trace_summary.total}`);
242
+ lines.push(` Verifier results: ${report.verifier_summary.total}`);
243
+ if (report.verifier_summary.total > 0) {
244
+ lines.push(` Passed: ${report.verifier_summary.passes}`);
245
+ lines.push(` Failed: ${report.verifier_summary.failures}`);
246
+ }
206
247
  lines.push("");
207
248
  }
208
249
  lines.push(`Classified signals: ${report.summary.signals}`);
@@ -214,19 +255,19 @@ export function formatKernelReport(report) {
214
255
  lines.push(` Unknown: ${report.classifications.unknown}`);
215
256
  lines.push("");
216
257
  if (report.trace_summary.top_blocked_tools.length > 0) {
217
- lines.push("Top blocked tools:");
258
+ lines.push("Legacy diagnostic top blocked tools:");
218
259
  for (const item of report.trace_summary.top_blocked_tools)
219
260
  lines.push(` ${item.tool}: ${item.count}`);
220
261
  lines.push("");
221
262
  }
222
263
  if (report.trace_summary.top_blocked_paths.length > 0) {
223
- lines.push("Top blocked paths:");
264
+ lines.push("Legacy diagnostic top blocked paths:");
224
265
  for (const item of report.trace_summary.top_blocked_paths)
225
266
  lines.push(` ${item.path}: ${item.count}`);
226
267
  lines.push("");
227
268
  }
228
269
  if (report.trace_summary.top_block_reasons.length > 0) {
229
- lines.push("Top block reasons:");
270
+ lines.push("Legacy diagnostic top block reasons:");
230
271
  for (const item of report.trace_summary.top_block_reasons)
231
272
  lines.push(` \"${item.reason}\": ${item.count}`);
232
273
  lines.push("");
@@ -1,11 +1,12 @@
1
1
  import type { TraceEntry, VerifierResultEntry } from "../hooks/state.js";
2
2
  import type { ExecutionOutcome } from "../evolution/types.js";
3
+ import { type RuntimeDecisionEvent } from "../governance/index.js";
3
4
  export type KernelSignalClassification = "protective" | "friction" | "positive" | "negative" | "neutral" | "unknown";
4
5
  export type KernelEvolutionEffect = "helped" | "hindered" | "neutral";
5
- export type KernelSignalSemantic = "quality-positive" | "quality-negative" | "enforcement-block" | "enforcement-warn" | "enforcement-allow" | "policy-denied-verifier" | "external";
6
+ export type KernelSignalSemantic = "quality-positive" | "quality-negative" | "enforcement-block" | "enforcement-warn" | "enforcement-allow" | "enforcement-escalate" | "enforcement-validate" | "policy-denied-verifier" | "external";
6
7
  export interface ClassifiedSignal {
7
8
  id: string;
8
- source_type: "trace" | "verifier" | "external";
9
+ source_type: "runtime_decision" | "trace" | "verifier" | "external";
9
10
  source_ref: string;
10
11
  timestamp: string;
11
12
  gene?: string;
@@ -22,11 +23,13 @@ export interface KernelSignalContext {
22
23
  friction_verifier_ids?: string[];
23
24
  }
24
25
  export interface ClassifyKernelSignalsInput {
26
+ runtimeDecisions?: RuntimeDecisionEvent[];
25
27
  traces?: TraceEntry[];
26
28
  verifierResults?: VerifierResultEntry[];
27
29
  context?: KernelSignalContext;
28
30
  }
29
31
  export declare function classifyVerifierResult(result: VerifierResultEntry, context?: KernelSignalContext): ClassifiedSignal[];
32
+ export declare function classifyRuntimeDecisionEvent(event: RuntimeDecisionEvent): ClassifiedSignal;
30
33
  export declare function classifyTraceEntry(trace: TraceEntry, context?: KernelSignalContext): ClassifiedSignal;
31
34
  export declare function classifyKernelSignals(input: ClassifyKernelSignalsInput): ClassifiedSignal[];
32
35
  export declare function classifiedSignalsToOutcomes(signals: ClassifiedSignal[], dnaId: string): {
@@ -1,3 +1,4 @@
1
+ import { validateRuntimeDecisionEvent } from "../governance/index.js";
1
2
  function extractGeneFromReason(reason) {
2
3
  if (!reason)
3
4
  return undefined;
@@ -11,6 +12,8 @@ function extractGeneFromReason(reason) {
11
12
  return sourceMatch?.[1];
12
13
  }
13
14
  function verifierSourceRef(result) {
15
+ if (result.result_id)
16
+ return `verifier:${result.result_id}`;
14
17
  return [
15
18
  "verifier",
16
19
  result.workflow ?? "unknown-workflow",
@@ -21,6 +24,7 @@ function verifierSourceRef(result) {
21
24
  }
22
25
  function verifierEvidencePayload(result) {
23
26
  const payload = {
27
+ result_id: result.result_id,
24
28
  verifier_id: result.verifier_id,
25
29
  when: result.when,
26
30
  severity: result.severity,
@@ -58,6 +62,55 @@ function traceEvidencePayload(trace) {
58
62
  }
59
63
  return Object.keys(payload).length > 0 ? payload : undefined;
60
64
  }
65
+ function runtimeDecisionEvidencePayload(event) {
66
+ return {
67
+ event_id: event.event_id,
68
+ decision: event.decision,
69
+ decision_reason: event.decision_reason,
70
+ policy_bundle_id: event.policy_bundle_id,
71
+ policy_bundle_version: event.policy_bundle_version,
72
+ compiled_ir_hash: event.compiled_ir_hash,
73
+ winning_constraint: event.winning_constraint,
74
+ evidence_refs: event.evidence_refs,
75
+ classification: validateRuntimeDecisionEvent(event).classification,
76
+ };
77
+ }
78
+ function semanticForRuntimeDecision(event) {
79
+ if (event.decision === "block")
80
+ return "enforcement-block";
81
+ if (event.decision === "warn")
82
+ return "enforcement-warn";
83
+ if (event.decision === "escalate")
84
+ return "enforcement-escalate";
85
+ if (event.decision === "validate")
86
+ return "enforcement-validate";
87
+ return "enforcement-allow";
88
+ }
89
+ function classificationForRuntimeDecision(event) {
90
+ const validation = validateRuntimeDecisionEvent(event);
91
+ if (validation.classification !== "enterprise_evidence") {
92
+ return {
93
+ classification: "unknown",
94
+ classification_reason: "RuntimeDecisionEvent is diagnostic-only because enterprise provenance is incomplete",
95
+ evolution_effect: "neutral",
96
+ eligible_for_marker_preview: false,
97
+ };
98
+ }
99
+ if (event.decision === "block") {
100
+ return {
101
+ classification: "protective",
102
+ classification_reason: "enterprise RuntimeDecisionEvent block is protective evidence",
103
+ evolution_effect: "neutral",
104
+ eligible_for_marker_preview: false,
105
+ };
106
+ }
107
+ return {
108
+ classification: "neutral",
109
+ classification_reason: "enterprise RuntimeDecisionEvent is primary evidence but not marker-preview feedback by default",
110
+ evolution_effect: "neutral",
111
+ eligible_for_marker_preview: false,
112
+ };
113
+ }
61
114
  function isPolicyDeniedVerifier(result) {
62
115
  return result.evidence === "policy_denied" || result.exit_code === 126;
63
116
  }
@@ -118,6 +171,21 @@ export function classifyVerifierResult(result, context = {}) {
118
171
  };
119
172
  });
120
173
  }
174
+ export function classifyRuntimeDecisionEvent(event) {
175
+ const sourceRef = `runtime_decision:${event.event_id}`;
176
+ const gene = event.winning_constraint.gene_id === "unknown" ? undefined : event.winning_constraint.gene_id;
177
+ return {
178
+ id: sourceRef,
179
+ source_type: "runtime_decision",
180
+ source_ref: sourceRef,
181
+ timestamp: event.timestamp,
182
+ gene,
183
+ semantic: semanticForRuntimeDecision(event),
184
+ ...classificationForRuntimeDecision(event),
185
+ evidence_ref: sourceRef,
186
+ evidence_payload: runtimeDecisionEvidencePayload(event),
187
+ };
188
+ }
121
189
  export function classifyTraceEntry(trace, context = {}) {
122
190
  const gene = extractGeneFromReason(trace.reason);
123
191
  const sourceRef = `trace:${trace.trace_id}`;
@@ -172,11 +240,28 @@ export function classifyTraceEntry(trace, context = {}) {
172
240
  };
173
241
  }
174
242
  export function classifyKernelSignals(input) {
243
+ const runtimeDecisions = input.runtimeDecisions ?? [];
175
244
  const traces = input.traces ?? [];
176
245
  const verifierResults = input.verifierResults ?? [];
177
246
  return [
178
- ...traces.map((trace) => classifyTraceEntry(trace, input.context)),
179
- ...verifierResults.flatMap((result) => classifyVerifierResult(result, input.context)),
247
+ ...runtimeDecisions.map((event) => classifyRuntimeDecisionEvent(event)),
248
+ ...traces.map((trace) => {
249
+ const signal = classifyTraceEntry(trace, input.context);
250
+ return {
251
+ ...signal,
252
+ classification: "unknown",
253
+ classification_reason: `legacy diagnostic trace only: ${signal.classification_reason}`,
254
+ evolution_effect: "neutral",
255
+ eligible_for_marker_preview: false,
256
+ };
257
+ }),
258
+ ...verifierResults.flatMap((result) => classifyVerifierResult(result, input.context).map((signal) => ({
259
+ ...signal,
260
+ classification: "unknown",
261
+ classification_reason: `legacy diagnostic verifier only: ${signal.classification_reason}`,
262
+ evolution_effect: "neutral",
263
+ eligible_for_marker_preview: false,
264
+ }))),
180
265
  ].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
181
266
  }
182
267
  export function classifiedSignalsToOutcomes(signals, dnaId) {
@@ -0,0 +1,64 @@
1
+ import type { DNAWorkflowState, TraceEntry, VerifierResultEntry } from "../hooks/state.js";
2
+ import type { CompletedArtifactEntry } from "../schema/types.js";
3
+ import { type RuntimeDecisionEvent } from "../governance/index.js";
4
+ import { type KernelReport, type KernelReportOptions } from "./kernel-report.js";
5
+ export type EvidenceRef = {
6
+ type: "runtime_decision";
7
+ event_id: string;
8
+ } | {
9
+ type: "trace";
10
+ trace_id: string;
11
+ } | {
12
+ type: "audit";
13
+ audit_id: string;
14
+ } | {
15
+ type: "verifier_result";
16
+ result_id: string;
17
+ } | {
18
+ type: "artifact";
19
+ artifact_id: string;
20
+ path?: string;
21
+ };
22
+ export interface CriticalDecision {
23
+ decision_id: string;
24
+ kind: "block" | "warn" | "escalate" | "verifier_failure" | "handoff_gate";
25
+ summary: string;
26
+ refs: EvidenceRef[];
27
+ }
28
+ export interface ReportPackage {
29
+ schema_version: "intentdna.report_package.v1";
30
+ generated_at: string;
31
+ source: KernelReport["source"];
32
+ kernel_report: KernelReport;
33
+ runtime_decisions: RuntimeDecisionEvent[];
34
+ traces: TraceEntry[];
35
+ verifier_results: VerifierResultEntry[];
36
+ handoffs: {
37
+ workflow_state?: DNAWorkflowState;
38
+ completed_artifacts: CompletedArtifactEntry[];
39
+ artifact_refs: Array<{
40
+ artifact_id: string;
41
+ path: string;
42
+ metadata?: Record<string, unknown>;
43
+ }>;
44
+ };
45
+ diagnostics: Array<{
46
+ severity: "error" | "warning" | "info";
47
+ message: string;
48
+ code?: string;
49
+ }>;
50
+ deferrals: Array<{
51
+ id: string;
52
+ reason: string;
53
+ refs: EvidenceRef[];
54
+ }>;
55
+ critical_decisions: CriticalDecision[];
56
+ mutation: {
57
+ command_class: "read-only" | "preview-only";
58
+ wrote_state: false;
59
+ wrote_evolution: false;
60
+ persisted_outcomes: false;
61
+ mutated_markers: false;
62
+ };
63
+ }
64
+ export declare function buildReportPackage(options: KernelReportOptions): Promise<ReportPackage>;
@@ -0,0 +1,90 @@
1
+ import { readRuntimeDecisionEvents, readTraces, readVerifierResults, readWorkflowState } from "../hooks/state.js";
2
+ import { validateRuntimeDecisionEvent } from "../governance/index.js";
3
+ import { buildKernelReport } from "./kernel-report.js";
4
+ function withinDays(timestamp, days) {
5
+ const parsed = Date.parse(timestamp);
6
+ if (Number.isNaN(parsed))
7
+ return true;
8
+ return parsed >= Date.now() - days * 24 * 60 * 60 * 1000;
9
+ }
10
+ function artifactRefs(completed) {
11
+ const refs = [];
12
+ for (const entry of completed) {
13
+ for (const artifact of entry.artifacts) {
14
+ refs.push({
15
+ artifact_id: artifact.artifact_id ?? `${entry.step_id}:${artifact.type}:${artifact.path}`,
16
+ path: artifact.path,
17
+ metadata: artifact.metadata,
18
+ });
19
+ }
20
+ }
21
+ return refs;
22
+ }
23
+ function refsForRuntimeDecision(event) {
24
+ const refs = [{ type: "runtime_decision", event_id: event.event_id }];
25
+ for (const ref of event.evidence_refs) {
26
+ if (ref.type === "trace")
27
+ refs.push({ type: "trace", trace_id: ref.ref });
28
+ if (ref.type === "audit")
29
+ refs.push({ type: "audit", audit_id: ref.ref });
30
+ if (ref.type === "verifier")
31
+ refs.push({ type: "verifier_result", result_id: ref.ref });
32
+ if (ref.type === "artifact" || ref.type === "handoff")
33
+ refs.push({ type: "artifact", artifact_id: ref.ref });
34
+ }
35
+ return refs;
36
+ }
37
+ function buildCriticalDecisions(runtimeDecisions) {
38
+ const decisions = [];
39
+ for (const event of runtimeDecisions) {
40
+ if (validateRuntimeDecisionEvent(event).classification !== "enterprise_evidence")
41
+ continue;
42
+ if (event.decision !== "block" && event.decision !== "warn" && event.decision !== "escalate")
43
+ continue;
44
+ decisions.push({
45
+ decision_id: `${event.decision}:${event.event_id}`,
46
+ kind: event.decision,
47
+ summary: event.decision_reason.split("\n")[0] || `${event.decision} decision`,
48
+ refs: refsForRuntimeDecision(event),
49
+ });
50
+ }
51
+ return decisions;
52
+ }
53
+ export async function buildReportPackage(options) {
54
+ const days = options.days ?? 7;
55
+ const kernelReport = await buildKernelReport(options);
56
+ const runtimeDecisions = await readRuntimeDecisionEvents(options.projectDir, days, options.sessionId);
57
+ const traces = await readTraces(options.projectDir, days, options.sessionId);
58
+ const verifierResults = (await readVerifierResults(options.projectDir, options.sessionId))
59
+ .filter((result) => withinDays(result.timestamp, days));
60
+ const workflowState = await readWorkflowState(options.projectDir, options.sessionId, 0) ?? undefined;
61
+ const completedArtifacts = workflowState?.completed_artifacts ?? [];
62
+ return {
63
+ schema_version: "intentdna.report_package.v1",
64
+ generated_at: new Date().toISOString(),
65
+ source: kernelReport.source,
66
+ kernel_report: kernelReport,
67
+ runtime_decisions: runtimeDecisions,
68
+ traces,
69
+ verifier_results: verifierResults,
70
+ handoffs: {
71
+ workflow_state: workflowState,
72
+ completed_artifacts: completedArtifacts,
73
+ artifact_refs: artifactRefs(completedArtifacts),
74
+ },
75
+ diagnostics: [
76
+ ...(traces.length > 0 ? [{ severity: "info", code: "legacy_trace_diagnostic", message: `${traces.length} trace record(s) included as diagnostic-only evidence` }] : []),
77
+ ...(verifierResults.length > 0 ? [{ severity: "info", code: "legacy_verifier_diagnostic", message: `${verifierResults.length} verifier result(s) included as diagnostic-only evidence` }] : []),
78
+ ...(completedArtifacts.length > 0 ? [{ severity: "info", code: "workflow_state_diagnostic", message: `${completedArtifacts.length} workflow handoff entry group(s) included as diagnostic-only evidence` }] : []),
79
+ ],
80
+ deferrals: [],
81
+ critical_decisions: buildCriticalDecisions(runtimeDecisions),
82
+ mutation: {
83
+ command_class: options.includeMarkerPreview ? "preview-only" : "read-only",
84
+ wrote_state: false,
85
+ wrote_evolution: false,
86
+ persisted_outcomes: false,
87
+ mutated_markers: false,
88
+ },
89
+ };
90
+ }
@@ -31,6 +31,7 @@ export interface AgentMDResult {
31
31
  * Convert camelCase or snake_case to kebab-case.
32
32
  */
33
33
  export declare function toKebabCase(name: string): string;
34
+ export declare function assertSafeGeneratedName(name: string, label?: string): void;
34
35
  /**
35
36
  * Compile a single RoleDef into an agent MD file.
36
37
  * Pure function, no side effects.
@@ -9,8 +9,9 @@
9
9
  * Output directory: .claude/agents/<name>.md
10
10
  */
11
11
  import { mkdir, writeFile, readFile, readdir, rm } from "node:fs/promises";
12
- import { join } from "node:path";
12
+ import { join, resolve, sep } from "node:path";
13
13
  const SENTINEL_COMMENT = "<!-- intentdna:managed — do not edit manually -->";
14
+ const SAFE_GENERATED_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
14
15
  // ── Helpers ────────────────────────────────────────────────
15
16
  /**
16
17
  * Convert camelCase or snake_case to kebab-case.
@@ -23,12 +24,26 @@ export function toKebabCase(name) {
23
24
  .replace(/[_\s]+/g, "-")
24
25
  .toLowerCase();
25
26
  }
27
+ export function assertSafeGeneratedName(name, label = "generated name") {
28
+ if (!SAFE_GENERATED_NAME.test(name)) {
29
+ throw new Error(`${label} '${name}' must contain only lowercase letters, digits, and single hyphens`);
30
+ }
31
+ }
32
+ function resolveContainedPath(baseDir, ...segments) {
33
+ const base = resolve(baseDir);
34
+ const target = resolve(baseDir, ...segments);
35
+ if (target !== base && !target.startsWith(`${base}${sep}`)) {
36
+ throw new Error(`generated path escapes output directory: ${target}`);
37
+ }
38
+ return target;
39
+ }
26
40
  /**
27
41
  * Render YAML frontmatter string (without --- delimiters).
28
42
  */
29
43
  function renderFrontmatter(name, role, options) {
30
44
  const prefix = options?.namePrefix ?? "dna-";
31
45
  const fullName = `${prefix}${toKebabCase(name)}`;
46
+ assertSafeGeneratedName(fullName, "agent name");
32
47
  const lines = [];
33
48
  lines.push(`name: ${fullName}`);
34
49
  lines.push(`description: "${escapeYamlString(role.description)}"`);
@@ -65,6 +80,7 @@ function renderConstraints(ir) {
65
80
  function renderBody(name, role, ir, options) {
66
81
  const prefix = options?.namePrefix ?? "dna-";
67
82
  const fullName = `${prefix}${toKebabCase(name)}`;
83
+ assertSafeGeneratedName(fullName, "agent name");
68
84
  const compiledAt = new Date().toISOString();
69
85
  const includeDNA = options?.includeDNAGuidelines ?? true;
70
86
  const sections = [];
@@ -184,7 +200,9 @@ function renderBody(name, role, ir, options) {
184
200
  export function compileRoleToAgentMD(roleName, role, ir, options) {
185
201
  const prefix = options?.namePrefix ?? "dna-";
186
202
  const kebabName = toKebabCase(roleName);
187
- const fileName = `${prefix}${kebabName}.md`;
203
+ const fullName = `${prefix}${kebabName}`;
204
+ assertSafeGeneratedName(fullName, "agent name");
205
+ const fileName = `${fullName}.md`;
188
206
  const frontmatter = renderFrontmatter(roleName, role, options);
189
207
  const body = renderBody(roleName, role, ir, options);
190
208
  const content = `---\n${frontmatter}\n---\n\n${body}`;
@@ -207,7 +225,7 @@ export async function writeAgentMDFiles(results, outputDir) {
207
225
  await mkdir(outputDir, { recursive: true });
208
226
  const written = [];
209
227
  for (const result of results) {
210
- const path = join(outputDir, result.fileName);
228
+ const path = resolveContainedPath(outputDir, result.fileName);
211
229
  await writeFile(path, result.content, "utf-8");
212
230
  written.push(path);
213
231
  }