intentdna 1.7.0 → 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 (47) 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/feedback.d.ts +2 -2
  5. package/dist/cli/commands/feedback.js +5 -5
  6. package/dist/cli/commands/sync.js +8 -5
  7. package/dist/cli/commands/verify.d.ts +32 -0
  8. package/dist/cli/commands/verify.js +173 -6
  9. package/dist/cli/index.js +3 -1
  10. package/dist/compiler/activate.js +11 -6
  11. package/dist/compiler/cascade.d.ts +3 -1
  12. package/dist/compiler/cascade.js +23 -1
  13. package/dist/compiler/compile.js +1 -0
  14. package/dist/compiler/index.d.ts +5 -1
  15. package/dist/compiler/index.js +7 -4
  16. package/dist/compiler/input-resolver.d.ts +8 -1
  17. package/dist/compiler/input-resolver.js +128 -22
  18. package/dist/compiler/provenance.d.ts +8 -0
  19. package/dist/compiler/provenance.js +127 -0
  20. package/dist/governance/index.d.ts +4 -3
  21. package/dist/governance/index.js +3 -4
  22. package/dist/governance/runtime-decision-event.d.ts +85 -0
  23. package/dist/governance/runtime-decision-event.js +231 -0
  24. package/dist/governance/types.d.ts +3 -3
  25. package/dist/governance/types.js +3 -3
  26. package/dist/hooks/cli.js +162 -13
  27. package/dist/hooks/state.d.ts +3 -10
  28. package/dist/hooks/state.js +147 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +1 -0
  31. package/dist/mcp/tools-observability.js +3 -3
  32. package/dist/mcp/tools-state.d.ts +1 -1
  33. package/dist/mcp/tools-state.js +17 -7
  34. package/dist/report/kernel-report.d.ts +27 -0
  35. package/dist/report/kernel-report.js +60 -19
  36. package/dist/report/kernel-signals.d.ts +5 -2
  37. package/dist/report/kernel-signals.js +84 -2
  38. package/dist/report/report-package.d.ts +9 -1
  39. package/dist/report/report-package.js +32 -27
  40. package/dist/runtime/claude-sdk.d.ts +9 -4
  41. package/dist/runtime/claude-sdk.js +9 -0
  42. package/dist/runtime/plugin-adapter.d.ts +5 -1
  43. package/dist/runtime/plugin-adapter.js +2 -0
  44. package/dist/schema/types.d.ts +51 -0
  45. package/package.json +1 -1
  46. package/spec/README.md +1 -1
  47. package/spec/schema-spec.md +78 -1
@@ -12,6 +12,7 @@
12
12
  import { readFile, writeFile, rename, mkdir, appendFile, unlink, readdir, stat } from "node:fs/promises";
13
13
  import { createHash } from "node:crypto";
14
14
  import { join, dirname } from "node:path";
15
+ import { CASCADE_LAYER_NAMES, RUNTIME_DECISION_EVENT_SCHEMA_VERSION } from "../governance/index.js";
15
16
  // ── Constants ──────────────────────────────────────────────
16
17
  const WORKFLOW_FILE = "workflow.json";
17
18
  const DEFAULT_STALENESS_MS = 2 * 60 * 60 * 1000; // 2 hours
@@ -406,6 +407,7 @@ export async function appendVerifierResult(projectDir, result, sessionId) {
406
407
  await writeVerifierResults(projectDir, results, sessionId);
407
408
  }
408
409
  const TRACE_DIR = "trace";
410
+ const RUNTIME_DECISION_DIR = "runtime-decisions";
409
411
  const MAX_TRACE_SIZE = 10 * 1024 * 1024; // 10MB
410
412
  const TRACE_RETENTION_DAYS = 7;
411
413
  /**
@@ -467,6 +469,151 @@ export async function appendTrace(projectDir, entry, sessionId) {
467
469
  *
468
470
  * Returns parsed entries sorted by timestamp.
469
471
  */
472
+ function datedJsonlFileName(prefix, date) {
473
+ return `${prefix}-${date}.jsonl`;
474
+ }
475
+ function decisionFileName(date) {
476
+ return datedJsonlFileName("runtime-decision", date);
477
+ }
478
+ export async function appendRuntimeDecisionEvent(projectDir, event, sessionId) {
479
+ try {
480
+ const line = JSON.stringify(event) + "\n";
481
+ const date = new Date().toISOString().slice(0, 10);
482
+ const globalDir = join(projectDir, ".dna", "state", RUNTIME_DECISION_DIR);
483
+ await mkdir(globalDir, { recursive: true });
484
+ await appendFile(join(globalDir, decisionFileName(date)), line, "utf-8");
485
+ if (sessionId) {
486
+ const sessionDir = resolveStateDir(projectDir, sessionId);
487
+ await mkdir(sessionDir, { recursive: true });
488
+ await appendFile(join(sessionDir, "runtime-decisions.jsonl"), line, "utf-8");
489
+ }
490
+ }
491
+ catch {
492
+ // Fail-open: evidence write failure never affects hook execution
493
+ }
494
+ }
495
+ function normalizeRuntimeDecisionEvent(value) {
496
+ if (typeof value !== "object" || value === null || Array.isArray(value))
497
+ return null;
498
+ const record = value;
499
+ const hasDecision = record.decision === "allow" || record.decision === "warn" || record.decision === "block" || record.decision === "escalate" || record.decision === "validate";
500
+ const decision = hasDecision ? record.decision : "unknown";
501
+ const schemaVersion = record.schema_version === RUNTIME_DECISION_EVENT_SCHEMA_VERSION ? RUNTIME_DECISION_EVENT_SCHEMA_VERSION : "unknown";
502
+ const hasTimestamp = typeof record.timestamp === "string" && record.timestamp.trim().length > 0;
503
+ const cascadeLayers = Object.fromEntries(CASCADE_LAYER_NAMES.map((layer) => {
504
+ const entry = typeof record.cascade_layers === "object" && record.cascade_layers !== null && !Array.isArray(record.cascade_layers)
505
+ ? record.cascade_layers[layer]
506
+ : undefined;
507
+ const layerRecord = typeof entry === "object" && entry !== null && !Array.isArray(entry) ? entry : {};
508
+ return [layer, {
509
+ layer,
510
+ source_id: typeof layerRecord.source_id === "string" ? layerRecord.source_id : "unknown",
511
+ source_ref: typeof layerRecord.source_ref === "string" ? layerRecord.source_ref : "unknown",
512
+ fingerprint: typeof layerRecord.fingerprint === "string" ? layerRecord.fingerprint : "unknown",
513
+ status: layerRecord.status === "active" || layerRecord.status === "unsupported" ? layerRecord.status : "unknown",
514
+ }];
515
+ }));
516
+ const winningConstraint = typeof record.winning_constraint === "object" && record.winning_constraint !== null && !Array.isArray(record.winning_constraint)
517
+ ? record.winning_constraint
518
+ : {};
519
+ const harnessRuntimeContext = typeof record.harness_runtime_context === "object" && record.harness_runtime_context !== null && !Array.isArray(record.harness_runtime_context)
520
+ ? record.harness_runtime_context
521
+ : {};
522
+ const evidenceRefs = Array.isArray(record.evidence_refs)
523
+ ? record.evidence_refs.filter((ref) => typeof ref === "object" && ref !== null && !Array.isArray(ref) && typeof ref.type === "string" && typeof ref.ref === "string")
524
+ : [];
525
+ return {
526
+ schema_version: schemaVersion,
527
+ event_id: typeof record.event_id === "string" && record.event_id ? record.event_id : "unknown",
528
+ timestamp: hasTimestamp ? record.timestamp : "unknown",
529
+ org_id: typeof record.org_id === "string" ? record.org_id : "unknown",
530
+ team_id: typeof record.team_id === "string" ? record.team_id : "unknown",
531
+ user_id: typeof record.user_id === "string" ? record.user_id : "unknown",
532
+ project_id: typeof record.project_id === "string" ? record.project_id : "unknown",
533
+ agent_id: typeof record.agent_id === "string" ? record.agent_id : "unknown",
534
+ agent_role: typeof record.agent_role === "string" ? record.agent_role : "unknown",
535
+ agent_type: typeof record.agent_type === "string" ? record.agent_type : "unknown",
536
+ session_agent: typeof record.session_agent === "string" ? record.session_agent : "unknown",
537
+ harness_adapter: typeof record.harness_adapter === "string" ? record.harness_adapter : "unknown",
538
+ runtime: typeof record.runtime === "string" ? record.runtime : "unknown",
539
+ policy_bundle_id: typeof record.policy_bundle_id === "string" ? record.policy_bundle_id : "unknown",
540
+ policy_bundle_version: typeof record.policy_bundle_version === "string" ? record.policy_bundle_version : "unknown",
541
+ cascade_layers: cascadeLayers,
542
+ winning_constraint: {
543
+ constraint_id: typeof winningConstraint.constraint_id === "string" ? winningConstraint.constraint_id : "unknown",
544
+ source_layer: typeof winningConstraint.source_layer === "string" && CASCADE_LAYER_NAMES.some((layer) => layer === winningConstraint.source_layer) ? winningConstraint.source_layer : "unknown",
545
+ source_dna_id: typeof winningConstraint.source_dna_id === "string" ? winningConstraint.source_dna_id : "unknown",
546
+ gene_id: typeof winningConstraint.gene_id === "string" ? winningConstraint.gene_id : "unknown",
547
+ codon_type: typeof winningConstraint.codon_type === "string" ? winningConstraint.codon_type : "unknown",
548
+ action: decision,
549
+ provenance_reason: typeof winningConstraint.provenance_reason === "string" ? winningConstraint.provenance_reason : "unknown",
550
+ },
551
+ compiled_ir_hash: typeof record.compiled_ir_hash === "string" ? record.compiled_ir_hash : "unknown",
552
+ decision: decision,
553
+ decision_reason: typeof record.decision_reason === "string" ? record.decision_reason : "unknown",
554
+ enforcement_point: typeof record.enforcement_point === "string" ? record.enforcement_point : "unknown",
555
+ harness_runtime_context: {
556
+ agent_id: typeof harnessRuntimeContext.agent_id === "string" ? harnessRuntimeContext.agent_id : typeof record.agent_id === "string" ? record.agent_id : "unknown",
557
+ agent_role: typeof harnessRuntimeContext.agent_role === "string" ? harnessRuntimeContext.agent_role : typeof record.agent_role === "string" ? record.agent_role : "unknown",
558
+ agent_type: typeof harnessRuntimeContext.agent_type === "string" ? harnessRuntimeContext.agent_type : typeof record.agent_type === "string" ? record.agent_type : "unknown",
559
+ session_agent: typeof harnessRuntimeContext.session_agent === "string" ? harnessRuntimeContext.session_agent : typeof record.session_agent === "string" ? record.session_agent : "unknown",
560
+ harness_adapter: typeof harnessRuntimeContext.harness_adapter === "string" ? harnessRuntimeContext.harness_adapter : typeof record.harness_adapter === "string" ? record.harness_adapter : "unknown",
561
+ runtime: typeof harnessRuntimeContext.runtime === "string" ? harnessRuntimeContext.runtime : typeof record.runtime === "string" ? record.runtime : "unknown",
562
+ },
563
+ session_id: typeof record.session_id === "string" ? record.session_id : "unknown",
564
+ run_id: typeof record.run_id === "string" ? record.run_id : "unknown",
565
+ step_id: typeof record.step_id === "string" ? record.step_id : "unknown",
566
+ tool_name: typeof record.tool_name === "string" ? record.tool_name : "unknown",
567
+ action_kind: typeof record.action_kind === "string" ? record.action_kind : "unknown",
568
+ resource_ref: typeof record.resource_ref === "string" ? record.resource_ref : "unknown",
569
+ evidence_refs: evidenceRefs.length > 0 ? evidenceRefs : [{ type: "unknown", ref: "unknown" }],
570
+ };
571
+ }
572
+ export async function readRuntimeDecisionEvents(projectDir, days = 1, sessionId) {
573
+ const entries = [];
574
+ if (sessionId) {
575
+ entries.push(...await readRuntimeDecisionFile(join(resolveStateDir(projectDir, sessionId), "runtime-decisions.jsonl")));
576
+ }
577
+ else {
578
+ entries.push(...await readGlobalRuntimeDecisionEvents(projectDir, days));
579
+ }
580
+ return entries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
581
+ }
582
+ async function readRuntimeDecisionFile(filePath) {
583
+ const entries = [];
584
+ try {
585
+ const content = await readFile(filePath, "utf-8");
586
+ for (const line of content.trim().split("\n")) {
587
+ if (!line)
588
+ continue;
589
+ try {
590
+ const event = normalizeRuntimeDecisionEvent(JSON.parse(line));
591
+ if (event)
592
+ entries.push(event);
593
+ }
594
+ catch { /* skip malformed JSON */ }
595
+ }
596
+ }
597
+ catch { /* file doesn't exist */ }
598
+ return entries;
599
+ }
600
+ async function readGlobalRuntimeDecisionEvents(projectDir, days) {
601
+ const dir = join(projectDir, ".dna", "state", RUNTIME_DECISION_DIR);
602
+ const entries = [];
603
+ const cutoff = new Date();
604
+ cutoff.setDate(cutoff.getDate() - days);
605
+ const cutoffDate = cutoff.toISOString().slice(0, 10);
606
+ try {
607
+ const files = (await readdir(dir))
608
+ .filter(f => f.startsWith("runtime-decision-") && f.endsWith(".jsonl"))
609
+ .filter(f => f.slice("runtime-decision-".length, "runtime-decision-".length + 10) >= cutoffDate)
610
+ .sort();
611
+ for (const file of files)
612
+ entries.push(...await readRuntimeDecisionFile(join(dir, file)));
613
+ }
614
+ catch { /* no decisions yet */ }
615
+ return entries;
616
+ }
470
617
  export async function readTraces(projectDir, days = 1, sessionId) {
471
618
  const entries = [];
472
619
  if (sessionId) {
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./schema/index.js";
2
2
  export * from "./compiler/index.js";
3
3
  export * from "./runtime/index.js";
4
+ export * from "./governance/index.js";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./schema/index.js";
2
2
  export * from "./compiler/index.js";
3
3
  export * from "./runtime/index.js";
4
+ export * from "./governance/index.js";
@@ -5,7 +5,7 @@ export function createObservabilityTools(projectDir) {
5
5
  return [
6
6
  {
7
7
  name: "dna_kernel_report",
8
- description: "Observability-only local kernel report. Reads traces/verifier results and optional preview-only marker changes; does not execute verifiers or mutate state.",
8
+ description: "Observability-only local kernel report. Reads RuntimeDecisionEvent as primary enterprise evidence; legacy traces/verifier results are diagnostic-only. Does not execute verifiers or mutate state.",
9
9
  inputSchema: {
10
10
  type: "object",
11
11
  properties: {
@@ -28,12 +28,12 @@ export function createObservabilityTools(projectDir) {
28
28
  return textResult(JSON.stringify(report, null, 2));
29
29
  }
30
30
  return textResult(formatKernelReport(report) +
31
- "\nMCP observability only: this tool does not execute verifiers, does not enforce hook parity, and does not mutate state or markers.\n");
31
+ "\nMCP observability only: RuntimeDecisionEvent is primary enterprise evidence; legacy traces/verifiers are diagnostic-only. This tool does not execute verifiers, does not enforce hook parity, and does not mutate state or markers.\n");
32
32
  },
33
33
  },
34
34
  {
35
35
  name: "dna_report_package",
36
- description: "Read-only local evidence package with traces, verifier results, handoffs, diagnostics, deferrals, and fact-backed critical decisions.",
36
+ description: "Read-only local evidence package with RuntimeDecisionEvent primary evidence, diagnostic legacy traces/verifier results/handoffs, deferrals, and fact-backed critical decisions.",
37
37
  inputSchema: {
38
38
  type: "object",
39
39
  properties: {
@@ -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;
@@ -61,6 +62,55 @@ function traceEvidencePayload(trace) {
61
62
  }
62
63
  return Object.keys(payload).length > 0 ? payload : undefined;
63
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
+ }
64
114
  function isPolicyDeniedVerifier(result) {
65
115
  return result.evidence === "policy_denied" || result.exit_code === 126;
66
116
  }
@@ -121,6 +171,21 @@ export function classifyVerifierResult(result, context = {}) {
121
171
  };
122
172
  });
123
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
+ }
124
189
  export function classifyTraceEntry(trace, context = {}) {
125
190
  const gene = extractGeneFromReason(trace.reason);
126
191
  const sourceRef = `trace:${trace.trace_id}`;
@@ -175,11 +240,28 @@ export function classifyTraceEntry(trace, context = {}) {
175
240
  };
176
241
  }
177
242
  export function classifyKernelSignals(input) {
243
+ const runtimeDecisions = input.runtimeDecisions ?? [];
178
244
  const traces = input.traces ?? [];
179
245
  const verifierResults = input.verifierResults ?? [];
180
246
  return [
181
- ...traces.map((trace) => classifyTraceEntry(trace, input.context)),
182
- ...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
+ }))),
183
265
  ].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
184
266
  }
185
267
  export function classifiedSignalsToOutcomes(signals, dnaId) {