intentdna 1.5.16 → 1.5.18
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 +72 -92
- package/dist/audit/index.d.ts +12 -3
- package/dist/cli/commands/feedback.d.ts +3 -0
- package/dist/cli/commands/feedback.js +33 -11
- package/dist/cli/commands/run.js +1 -1
- package/dist/cli/commands/sync.js +5 -5
- package/dist/compiler/cascade.d.ts +3 -1
- package/dist/compiler/cascade.js +86 -2
- package/dist/compiler/compile.js +93 -3
- package/dist/compiler/workflow.d.ts +1 -0
- package/dist/compiler/workflow.js +1 -0
- package/dist/evolution/trace-bridge.d.ts +4 -5
- package/dist/evolution/trace-bridge.js +31 -55
- package/dist/hooks/cli.d.ts +18 -1
- package/dist/hooks/cli.js +624 -24
- package/dist/hooks/enforce.js +3 -2
- package/dist/hooks/state.d.ts +20 -1
- package/dist/hooks/state.js +55 -0
- package/dist/runtime/markdown.d.ts +1 -1
- package/dist/runtime/markdown.js +149 -0
- package/dist/runtime/settings-adapter.d.ts +3 -2
- package/dist/runtime/settings-adapter.js +26 -4
- package/dist/runtime/skill-adapter.js +49 -23
- package/dist/schema/types.d.ts +39 -0
- package/dist/schema/validate.js +143 -0
- package/dist/signals/index.d.ts +45 -0
- package/dist/signals/index.js +117 -0
- package/dist/templates/code-review-pipeline.dna.yaml +4 -0
- package/dist/templates/flutter-rewrite.dna.yaml +139 -212
- package/dist/templates/full-pipeline.dna.yaml +4 -0
- package/dist/templates/mobile-dev.dna.yaml +5 -0
- package/hooks/hooks.json +1 -1
- package/package.json +1 -1
- package/spec/control-plane-convergence-handoff-2026-04-24.md +432 -0
package/dist/compiler/compile.js
CHANGED
|
@@ -150,6 +150,38 @@ function geneToValidators(name, gene) {
|
|
|
150
150
|
}
|
|
151
151
|
return validators;
|
|
152
152
|
}
|
|
153
|
+
function legibilityAssetsToContextFiles(assets) {
|
|
154
|
+
if (!assets)
|
|
155
|
+
return undefined;
|
|
156
|
+
const mandatory = assets.mandatory
|
|
157
|
+
?.filter((asset) => asset.type === "required_read")
|
|
158
|
+
.map((asset) => asset.path);
|
|
159
|
+
const per_role = assets.per_role
|
|
160
|
+
? Object.fromEntries(Object.entries(assets.per_role).map(([role, roleAssets]) => [
|
|
161
|
+
role,
|
|
162
|
+
roleAssets.filter((asset) => asset.type === "required_read").map((asset) => asset.path),
|
|
163
|
+
]).filter(([, paths]) => paths.length > 0))
|
|
164
|
+
: undefined;
|
|
165
|
+
const per_workflow = assets.per_workflow
|
|
166
|
+
? Object.fromEntries(Object.entries(assets.per_workflow).map(([workflow, wfAssets]) => [
|
|
167
|
+
workflow,
|
|
168
|
+
wfAssets.filter((asset) => asset.type === "required_read").map((asset) => asset.path),
|
|
169
|
+
]).filter(([, paths]) => paths.length > 0))
|
|
170
|
+
: undefined;
|
|
171
|
+
if ((!mandatory || mandatory.length === 0) && !per_role && !per_workflow)
|
|
172
|
+
return undefined;
|
|
173
|
+
return { mandatory, per_role, per_workflow };
|
|
174
|
+
}
|
|
175
|
+
function resolvePostCheckCommand(check) {
|
|
176
|
+
const normalized = check.trim();
|
|
177
|
+
const builtins = {
|
|
178
|
+
vitest_run: "npm test",
|
|
179
|
+
tsc_no_errors: "npx tsc --noEmit",
|
|
180
|
+
dart_analyze: "dart analyze",
|
|
181
|
+
flutter_test: "flutter test",
|
|
182
|
+
};
|
|
183
|
+
return builtins[normalized] ?? normalized;
|
|
184
|
+
}
|
|
153
185
|
/**
|
|
154
186
|
* Compile activated DNA into a Constraint IR.
|
|
155
187
|
*/
|
|
@@ -158,6 +190,7 @@ export function compileDNA(activated, cascaded) {
|
|
|
158
190
|
const toolFilters = [];
|
|
159
191
|
const gates = [];
|
|
160
192
|
const validators = [];
|
|
193
|
+
const verifierSpecs = [];
|
|
161
194
|
const injections = [];
|
|
162
195
|
for (const [name, gene] of Object.entries(activated.genes)) {
|
|
163
196
|
directives.push(...geneToDirectives(name, gene));
|
|
@@ -210,15 +243,67 @@ export function compileDNA(activated, cascaded) {
|
|
|
210
243
|
}
|
|
211
244
|
// Build step_checkpoints from all workflow steps that have checkpoints
|
|
212
245
|
const stepCheckpoints = [];
|
|
213
|
-
const allWorkflows = Object.
|
|
214
|
-
for (const wf of allWorkflows) {
|
|
246
|
+
const allWorkflows = Object.entries(cascaded?.workflows ?? {});
|
|
247
|
+
for (const [workflowKey, wf] of allWorkflows) {
|
|
215
248
|
for (const step of wf.steps ?? []) {
|
|
249
|
+
const roleGeneHints = cascaded?.roles?.[step.role]?.activates_genes ?? [];
|
|
216
250
|
if (step.checkpoints && step.checkpoints.length > 0) {
|
|
217
251
|
stepCheckpoints.push({
|
|
218
252
|
step_role: step.role,
|
|
219
253
|
step_id: step.id,
|
|
220
254
|
checkpoints: [...step.checkpoints],
|
|
221
255
|
});
|
|
256
|
+
for (const checkpoint of step.checkpoints) {
|
|
257
|
+
verifierSpecs.push({
|
|
258
|
+
id: `${workflowKey}:${step.id}:checkpoint:${checkpoint.assert}`,
|
|
259
|
+
when: "post_step",
|
|
260
|
+
severity: checkpoint.action ?? "block",
|
|
261
|
+
kind: "checkpoint",
|
|
262
|
+
workflow_name: workflowKey,
|
|
263
|
+
step_id: step.id,
|
|
264
|
+
step_role: step.role,
|
|
265
|
+
source_genes: roleGeneHints.length > 0 ? [...roleGeneHints] : undefined,
|
|
266
|
+
checkpoint,
|
|
267
|
+
source: "workflow_checkpoint",
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (step.completion && step.completion.length > 0) {
|
|
272
|
+
for (let i = 0; i < step.completion.length; i++) {
|
|
273
|
+
verifierSpecs.push({
|
|
274
|
+
id: `${workflowKey}:${step.id}:completion:${i}`,
|
|
275
|
+
when: "pre_handoff",
|
|
276
|
+
severity: "block",
|
|
277
|
+
kind: "completion",
|
|
278
|
+
workflow_name: workflowKey,
|
|
279
|
+
step_id: step.id,
|
|
280
|
+
step_role: step.role,
|
|
281
|
+
source_genes: roleGeneHints.length > 0 ? [...roleGeneHints] : undefined,
|
|
282
|
+
completion: step.completion[i],
|
|
283
|
+
source: "workflow_completion",
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const rolePostChecks = cascaded?.roles?.[step.role]?.post_checks ?? [];
|
|
288
|
+
for (let i = 0; i < rolePostChecks.length; i++) {
|
|
289
|
+
const resolvedCommand = resolvePostCheckCommand(rolePostChecks[i]);
|
|
290
|
+
verifierSpecs.push({
|
|
291
|
+
id: `${workflowKey}:${step.id}:post-check:${i}`,
|
|
292
|
+
when: "post_step",
|
|
293
|
+
severity: "warn",
|
|
294
|
+
kind: "checkpoint",
|
|
295
|
+
workflow_name: workflowKey,
|
|
296
|
+
step_id: step.id,
|
|
297
|
+
step_role: step.role,
|
|
298
|
+
source_genes: roleGeneHints.length > 0 ? [...roleGeneHints] : undefined,
|
|
299
|
+
checkpoint: {
|
|
300
|
+
assert: `role_post_check_${i}`,
|
|
301
|
+
command: resolvedCommand,
|
|
302
|
+
message: `Post-check failed: ${rolePostChecks[i]}`,
|
|
303
|
+
action: "warn",
|
|
304
|
+
},
|
|
305
|
+
source: "role_post_check",
|
|
306
|
+
});
|
|
222
307
|
}
|
|
223
308
|
}
|
|
224
309
|
}
|
|
@@ -275,11 +360,14 @@ export function compileDNA(activated, cascaded) {
|
|
|
275
360
|
// Sort directives by priority
|
|
276
361
|
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
277
362
|
directives.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
|
|
363
|
+
const legacyContextFiles = cascaded?.context_files;
|
|
364
|
+
const compiledContextFiles = legacyContextFiles ?? legibilityAssetsToContextFiles(cascaded?.legibility_assets);
|
|
278
365
|
return {
|
|
279
366
|
prompt_directives: directives,
|
|
280
367
|
tool_filters: toolFilters,
|
|
281
368
|
pre_execution_gates: gates,
|
|
282
369
|
post_execution_validators: validators,
|
|
370
|
+
verifier_specs: verifierSpecs.length > 0 ? verifierSpecs : undefined,
|
|
283
371
|
context_injections: injections,
|
|
284
372
|
source_dna_ids: activated.source_ids,
|
|
285
373
|
compiled_at: new Date().toISOString(),
|
|
@@ -290,6 +378,8 @@ export function compileDNA(activated, cascaded) {
|
|
|
290
378
|
roles_scope_map: rolesScopeMap.length > 0 ? rolesScopeMap : undefined,
|
|
291
379
|
step_checkpoints: stepCheckpoints.length > 0 ? stepCheckpoints : undefined,
|
|
292
380
|
workflows_ir: workflowsIR.length > 0 ? workflowsIR : undefined,
|
|
293
|
-
|
|
381
|
+
legibility_assets: cascaded?.legibility_assets,
|
|
382
|
+
context_files: compiledContextFiles,
|
|
383
|
+
verifier_policy: cascaded?.verifier_policy,
|
|
294
384
|
};
|
|
295
385
|
}
|
|
@@ -11,6 +11,7 @@ export interface CompileWorkflowOptions {
|
|
|
11
11
|
mermaid?: boolean;
|
|
12
12
|
/** Roles for scope overlap analysis (needed for isolation: auto) */
|
|
13
13
|
roles?: Record<string, RoleDef>;
|
|
14
|
+
workflow_key?: string;
|
|
14
15
|
}
|
|
15
16
|
export interface CompileWorkflowError {
|
|
16
17
|
path: string;
|
|
@@ -443,6 +443,7 @@ export function compileWorkflow(workflow, options) {
|
|
|
443
443
|
retry,
|
|
444
444
|
compiled_at: new Date().toISOString(),
|
|
445
445
|
source_workflow: workflow.name,
|
|
446
|
+
workflow_key: options?.workflow_key ?? workflow.name,
|
|
446
447
|
};
|
|
447
448
|
// 10. Optional Mermaid diagram
|
|
448
449
|
if (options?.mermaid) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Role scope: "Role '<name>'" → gene "scope:<role>"
|
|
11
11
|
* - Gates: "(gene: <name>)" pattern or fallback to "gate"
|
|
12
12
|
*/
|
|
13
|
-
import type { TraceEntry } from "../hooks/state.js";
|
|
13
|
+
import type { TraceEntry, VerifierResultEntry } from "../hooks/state.js";
|
|
14
14
|
import type { ExecutionOutcome } from "./types.js";
|
|
15
15
|
/**
|
|
16
16
|
* Extract the gene name from a trace entry's reason string.
|
|
@@ -19,12 +19,11 @@ import type { ExecutionOutcome } from "./types.js";
|
|
|
19
19
|
export declare function extractGeneFromReason(reason: string | undefined): string | null;
|
|
20
20
|
/**
|
|
21
21
|
* Convert trace entries into ExecutionOutcomes for the evolution engine.
|
|
22
|
-
*
|
|
23
|
-
* Groups block/warn traces by gene, creates one outcome per unique
|
|
24
|
-
* (gene, decision) pair. Allow decisions are not converted since
|
|
25
|
-
* they don't carry gene information.
|
|
22
|
+
* Backward-compatible wrapper over the normalized signals path.
|
|
26
23
|
*/
|
|
27
24
|
export declare function traceToOutcomes(traces: TraceEntry[], dnaId?: string): ExecutionOutcome[];
|
|
25
|
+
export declare function verifierResultsToOutcomes(results: VerifierResultEntry[], dnaId?: string): ExecutionOutcome[];
|
|
26
|
+
export declare function mergeOutcomes(traces: TraceEntry[], verifierResults: VerifierResultEntry[], dnaId?: string): ExecutionOutcome[];
|
|
28
27
|
/**
|
|
29
28
|
* Summarize trace-to-outcome conversion for human review.
|
|
30
29
|
*/
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Role scope: "Role '<name>'" → gene "scope:<role>"
|
|
11
11
|
* - Gates: "(gene: <name>)" pattern or fallback to "gate"
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { signalsToOutcomes, traceToSignals, verifierResultsToSignals } from "../signals/index.js";
|
|
14
14
|
/**
|
|
15
15
|
* Extract the gene name from a trace entry's reason string.
|
|
16
16
|
* Returns null if no gene can be determined.
|
|
@@ -19,69 +19,34 @@ export function extractGeneFromReason(reason) {
|
|
|
19
19
|
if (!reason)
|
|
20
20
|
return null;
|
|
21
21
|
// Pattern: (gene: <name>) — from tool filters and gates
|
|
22
|
-
const geneMatch = reason.match(/\(gene:\s*(
|
|
22
|
+
const geneMatch = reason.match(/\(gene:\s*([A-Za-z0-9:_-]+)\)/);
|
|
23
23
|
if (geneMatch)
|
|
24
24
|
return geneMatch[1];
|
|
25
25
|
// Pattern: Role '<name>' — from scope enforcement
|
|
26
|
-
const roleMatch = reason.match(/Role '(
|
|
26
|
+
const roleMatch = reason.match(/Role '([A-Za-z0-9:_-]+)'/);
|
|
27
27
|
if (roleMatch)
|
|
28
28
|
return `scope:${roleMatch[1]}`;
|
|
29
29
|
// Pattern: source_gene in gate messages
|
|
30
|
-
const sourceMatch = reason.match(/source_gene:\s*(
|
|
30
|
+
const sourceMatch = reason.match(/source_gene:\s*([A-Za-z0-9:_-]+)/);
|
|
31
31
|
if (sourceMatch)
|
|
32
32
|
return sourceMatch[1];
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Convert trace entries into ExecutionOutcomes for the evolution engine.
|
|
37
|
-
*
|
|
38
|
-
* Groups block/warn traces by gene, creates one outcome per unique
|
|
39
|
-
* (gene, decision) pair. Allow decisions are not converted since
|
|
40
|
-
* they don't carry gene information.
|
|
37
|
+
* Backward-compatible wrapper over the normalized signals path.
|
|
41
38
|
*/
|
|
42
39
|
export function traceToOutcomes(traces, dnaId = "default") {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (trace.reason && existing.details.length < 3) {
|
|
54
|
-
const short = trace.reason.split("\n")[0].slice(0, 80);
|
|
55
|
-
existing.details.push(short);
|
|
56
|
-
}
|
|
57
|
-
geneFeedback.set(gene, existing);
|
|
58
|
-
}
|
|
59
|
-
// Convert to ExecutionOutcomes
|
|
60
|
-
const outcomes = [];
|
|
61
|
-
const totalEvents = traces.length;
|
|
62
|
-
const timestamp = new Date().toISOString();
|
|
63
|
-
for (const [gene, stats] of geneFeedback) {
|
|
64
|
-
const feedback = [];
|
|
65
|
-
// Each block counts as a "hindered" feedback
|
|
66
|
-
for (let i = 0; i < stats.hindered; i++) {
|
|
67
|
-
feedback.push({
|
|
68
|
-
gene,
|
|
69
|
-
effect: "hindered",
|
|
70
|
-
detail: stats.details[i] ?? `blocked ${stats.hindered} times`,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
outcomes.push({
|
|
74
|
-
id: `trace_${randomUUID().slice(0, 8)}`,
|
|
75
|
-
timestamp,
|
|
76
|
-
dna_id: dnaId,
|
|
77
|
-
action: `enforcement: ${gene} blocked ${stats.hindered}/${totalEvents} events`,
|
|
78
|
-
active_genes: [gene],
|
|
79
|
-
constraints_applied: [gene],
|
|
80
|
-
success: false, // Blocks indicate friction
|
|
81
|
-
constraint_feedback: feedback,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
return outcomes;
|
|
40
|
+
return signalsToOutcomes(traceToSignals(traces), dnaId);
|
|
41
|
+
}
|
|
42
|
+
export function verifierResultsToOutcomes(results, dnaId = "default") {
|
|
43
|
+
return signalsToOutcomes(verifierResultsToSignals(results), dnaId);
|
|
44
|
+
}
|
|
45
|
+
export function mergeOutcomes(traces, verifierResults, dnaId = "default") {
|
|
46
|
+
return signalsToOutcomes([
|
|
47
|
+
...traceToSignals(traces),
|
|
48
|
+
...verifierResultsToSignals(verifierResults),
|
|
49
|
+
], dnaId);
|
|
85
50
|
}
|
|
86
51
|
/**
|
|
87
52
|
* Summarize trace-to-outcome conversion for human review.
|
|
@@ -89,11 +54,22 @@ export function traceToOutcomes(traces, dnaId = "default") {
|
|
|
89
54
|
export function summarizeConversion(outcomes) {
|
|
90
55
|
if (outcomes.length === 0)
|
|
91
56
|
return "No actionable gene feedback found in traces.";
|
|
92
|
-
const lines = ["
|
|
93
|
-
for (const
|
|
94
|
-
const gene =
|
|
95
|
-
const
|
|
96
|
-
|
|
57
|
+
const lines = ["Signals → Epigenetic conversion:"];
|
|
58
|
+
for (const outcome of outcomes) {
|
|
59
|
+
const gene = outcome.active_genes[0];
|
|
60
|
+
const helped = outcome.constraint_feedback.filter((item) => item.effect === "helped").length;
|
|
61
|
+
const hindered = outcome.constraint_feedback.filter((item) => item.effect === "hindered").length;
|
|
62
|
+
const neutral = outcome.constraint_feedback.filter((item) => item.effect === "neutral").length;
|
|
63
|
+
if (hindered > 0 && helped === 0 && neutral === 0) {
|
|
64
|
+
lines.push(` ${gene}: ${hindered} block${hindered > 1 ? "s" : ""} → will suppress`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (helped > 0 && hindered === 0 && neutral === 0) {
|
|
68
|
+
lines.push(` ${gene}: ${helped} success${helped > 1 ? "es" : ""} → will amplify`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const direction = outcome.success ? "amplify" : "suppress";
|
|
72
|
+
lines.push(` ${gene}: ${helped} positive, ${hindered} negative, ${neutral} neutral → net ${direction}`);
|
|
97
73
|
}
|
|
98
74
|
return lines.join("\n");
|
|
99
75
|
}
|
package/dist/hooks/cli.d.ts
CHANGED
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Fail-open: all errors → { continue: true, suppressOutput: true }
|
|
16
16
|
*/
|
|
17
|
-
import type { ConstraintIR } from "../schema/types.js";
|
|
17
|
+
import type { ConstraintIR, VerifierSpec } from "../schema/types.js";
|
|
18
|
+
import type { HookOutput } from "./protocol.js";
|
|
18
19
|
import { blockOutput } from "./protocol.js";
|
|
19
20
|
import { readWorkflowState } from "./state.js";
|
|
21
|
+
import type { VerifierResultEntry } from "./state.js";
|
|
20
22
|
export interface SessionSummary {
|
|
21
23
|
total: number;
|
|
22
24
|
blocks: number;
|
|
@@ -37,6 +39,21 @@ export declare function computeSummary(traces: Array<{
|
|
|
37
39
|
reason?: string;
|
|
38
40
|
}>): SessionSummary;
|
|
39
41
|
export declare function formatSummary(s: SessionSummary): string | null;
|
|
42
|
+
export declare function appendStopVerifierWarnings(output: HookOutput, verifierResults: VerifierResultEntry[], currentStep?: string): HookOutput;
|
|
43
|
+
export declare function runVerifiersForTest(projectDir: string, ir: ConstraintIR, workflowState: {
|
|
44
|
+
workflow: string;
|
|
45
|
+
current_step: string;
|
|
46
|
+
current_role: string;
|
|
47
|
+
}, when: VerifierSpec["when"], sessionId?: string, options?: {
|
|
48
|
+
commandTimeoutMs?: number;
|
|
49
|
+
}): Promise<VerifierResultEntry[]>;
|
|
50
|
+
export declare function runStopVerifiersForTest(projectDir: string, ir: ConstraintIR, workflowState: {
|
|
51
|
+
workflow: string;
|
|
52
|
+
current_step: string;
|
|
53
|
+
current_role: string;
|
|
54
|
+
}, sessionId?: string, options?: {
|
|
55
|
+
commandTimeoutMs?: number;
|
|
56
|
+
}): Promise<VerifierResultEntry[]>;
|
|
40
57
|
/**
|
|
41
58
|
* Evaluate PreToolUse gates in priority order:
|
|
42
59
|
* 1. Workflow Boundary — block Skill() after workflow completed
|