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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +26 -15
- package/dist/cli/commands/feedback.d.ts +2 -2
- package/dist/cli/commands/feedback.js +5 -5
- package/dist/cli/commands/sync.js +8 -5
- package/dist/cli/commands/verify.d.ts +32 -0
- package/dist/cli/commands/verify.js +173 -6
- package/dist/cli/index.js +3 -1
- package/dist/compiler/activate.js +11 -6
- package/dist/compiler/cascade.d.ts +3 -1
- package/dist/compiler/cascade.js +23 -1
- package/dist/compiler/compile.js +1 -0
- package/dist/compiler/index.d.ts +5 -1
- package/dist/compiler/index.js +7 -4
- package/dist/compiler/input-resolver.d.ts +8 -1
- package/dist/compiler/input-resolver.js +128 -22
- package/dist/compiler/provenance.d.ts +8 -0
- package/dist/compiler/provenance.js +127 -0
- package/dist/governance/index.d.ts +4 -3
- package/dist/governance/index.js +3 -4
- package/dist/governance/runtime-decision-event.d.ts +85 -0
- package/dist/governance/runtime-decision-event.js +231 -0
- package/dist/governance/types.d.ts +3 -3
- package/dist/governance/types.js +3 -3
- package/dist/hooks/cli.js +162 -13
- package/dist/hooks/state.d.ts +3 -10
- package/dist/hooks/state.js +147 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/tools-observability.js +3 -3
- package/dist/mcp/tools-state.d.ts +1 -1
- package/dist/mcp/tools-state.js +17 -7
- package/dist/report/kernel-report.d.ts +27 -0
- package/dist/report/kernel-report.js +60 -19
- package/dist/report/kernel-signals.d.ts +5 -2
- package/dist/report/kernel-signals.js +84 -2
- package/dist/report/report-package.d.ts +9 -1
- package/dist/report/report-package.js +32 -27
- package/dist/runtime/claude-sdk.d.ts +9 -4
- package/dist/runtime/claude-sdk.js +9 -0
- package/dist/runtime/plugin-adapter.d.ts +5 -1
- package/dist/runtime/plugin-adapter.js +2 -0
- package/dist/schema/types.d.ts +51 -0
- package/package.json +1 -1
- package/spec/README.md +1 -1
- package/spec/schema-spec.md +78 -1
package/dist/hooks/state.js
CHANGED
|
@@ -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
package/dist/index.js
CHANGED
|
@@ -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
|
|
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:
|
|
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
|
|
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[];
|
package/dist/mcp/tools-state.js
CHANGED
|
@@ -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,
|
|
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(`\
|
|
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 (
|
|
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
|
|
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 = [`
|
|
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:
|
|
170
|
-
quality_negative:
|
|
199
|
+
quality_positive: 0,
|
|
200
|
+
quality_negative: 0,
|
|
171
201
|
},
|
|
172
202
|
enforcement: {
|
|
173
|
-
blocks:
|
|
174
|
-
warns:
|
|
175
|
-
allows:
|
|
176
|
-
|
|
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(`
|
|
198
|
-
lines.push(`
|
|
199
|
-
lines.push(`
|
|
200
|
-
lines.push(`
|
|
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(
|
|
204
|
-
lines.push(`
|
|
205
|
-
lines.push(`
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
-
...
|
|
182
|
-
...
|
|
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) {
|