intentdna 1.7.4 → 1.8.0

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 (152) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/LICENSE +201 -0
  4. package/README.md +143 -194
  5. package/dist/assets/candidates.d.ts +124 -0
  6. package/dist/assets/candidates.js +1249 -0
  7. package/dist/assets/catalog.d.ts +101 -0
  8. package/dist/assets/catalog.js +311 -0
  9. package/dist/assets/evidence.d.ts +93 -0
  10. package/dist/assets/evidence.js +391 -0
  11. package/dist/assets/index.d.ts +4 -0
  12. package/dist/assets/index.js +4 -0
  13. package/dist/assets/metadata.d.ts +19 -0
  14. package/dist/assets/metadata.js +227 -0
  15. package/dist/cli/commands/assets.d.ts +65 -0
  16. package/dist/cli/commands/assets.js +2871 -0
  17. package/dist/cli/commands/auth.d.ts +322 -0
  18. package/dist/cli/commands/auth.js +1592 -0
  19. package/dist/cli/commands/cloud-auth-guidance.d.ts +2 -0
  20. package/dist/cli/commands/cloud-auth-guidance.js +4 -0
  21. package/dist/cli/commands/cloud.d.ts +1359 -0
  22. package/dist/cli/commands/cloud.js +8364 -0
  23. package/dist/cli/commands/evolve.js +18 -2
  24. package/dist/cli/commands/generate.js +25 -2
  25. package/dist/cli/commands/guard.d.ts +1 -1
  26. package/dist/cli/commands/guard.js +1 -1
  27. package/dist/cli/commands/guide.d.ts +15 -0
  28. package/dist/cli/commands/guide.js +1747 -0
  29. package/dist/cli/commands/init.d.ts +12 -0
  30. package/dist/cli/commands/init.js +95 -60
  31. package/dist/cli/commands/org.d.ts +16 -0
  32. package/dist/cli/commands/org.js +2393 -0
  33. package/dist/cli/commands/release.d.ts +59 -0
  34. package/dist/cli/commands/release.js +716 -0
  35. package/dist/cli/commands/report.d.ts +82 -0
  36. package/dist/cli/commands/report.js +1516 -0
  37. package/dist/cli/commands/run.d.ts +6 -1
  38. package/dist/cli/commands/run.js +95 -26
  39. package/dist/cli/commands/sync.d.ts +8 -1
  40. package/dist/cli/commands/sync.js +436 -141
  41. package/dist/cli/commands/templates.d.ts +64 -0
  42. package/dist/cli/commands/templates.js +2151 -1
  43. package/dist/cli/commands/workflow.d.ts +26 -0
  44. package/dist/cli/commands/workflow.js +3053 -0
  45. package/dist/cli/index.js +719 -42
  46. package/dist/compiler/cascade.js +4 -9
  47. package/dist/compiler/compile.js +1 -0
  48. package/dist/governance/evidence-capture-attribution.d.ts +12 -0
  49. package/dist/governance/evidence-capture-attribution.js +46 -0
  50. package/dist/governance/index.d.ts +2 -0
  51. package/dist/governance/index.js +1 -0
  52. package/dist/hooks/cli.d.ts +6 -0
  53. package/dist/hooks/cli.js +168 -12
  54. package/dist/hooks/enforce.d.ts +1 -0
  55. package/dist/hooks/protocol.d.ts +1 -1
  56. package/dist/hooks/protocol.js +1 -1
  57. package/dist/hooks/state.d.ts +41 -0
  58. package/dist/hooks/state.js +126 -2
  59. package/dist/index.d.ts +3 -0
  60. package/dist/index.js +3 -0
  61. package/dist/mcp/tools-state.js +2 -0
  62. package/dist/organization/index.d.ts +121 -0
  63. package/dist/organization/index.js +413 -0
  64. package/dist/runtime/claude-sync-target.d.ts +49 -0
  65. package/dist/runtime/claude-sync-target.js +280 -0
  66. package/dist/runtime/codex-adapter.d.ts +254 -0
  67. package/dist/runtime/codex-adapter.js +1204 -0
  68. package/dist/runtime/codex-sync-target.d.ts +33 -0
  69. package/dist/runtime/codex-sync-target.js +241 -0
  70. package/dist/runtime/index.d.ts +8 -0
  71. package/dist/runtime/index.js +4 -0
  72. package/dist/runtime/output-artifact-registry.d.ts +1 -1
  73. package/dist/runtime/output-artifact-registry.js +2 -0
  74. package/dist/runtime/settings-adapter.d.ts +1 -1
  75. package/dist/runtime/settings-adapter.js +1 -1
  76. package/dist/runtime/skill-adapter.d.ts +5 -1
  77. package/dist/runtime/skill-adapter.js +120 -34
  78. package/dist/runtime/sync-artifact-registry.d.ts +99 -0
  79. package/dist/runtime/sync-artifact-registry.js +194 -0
  80. package/dist/runtime/sync-target-plan.d.ts +45 -0
  81. package/dist/runtime/sync-target-plan.js +19 -0
  82. package/dist/schema/controller-registry.d.ts +8 -1
  83. package/dist/schema/controller-registry.js +8 -0
  84. package/dist/schema/types.d.ts +35 -23
  85. package/dist/schema/types.js +10 -1
  86. package/dist/schema/validate.js +22 -22
  87. package/dist/schema/validators/controllers.js +38 -20
  88. package/dist/schema/workflow-authoring-contract.d.ts +29 -0
  89. package/dist/schema/workflow-authoring-contract.js +109 -0
  90. package/dist/schema/yaml-parser.js +18 -0
  91. package/dist/templates/api-backend.dna.yaml +19 -0
  92. package/dist/templates/brainstorming-first.dna.yaml +19 -0
  93. package/dist/templates/code-cleanup.dna.yaml +19 -0
  94. package/dist/templates/code-review-pipeline.dna.yaml +18 -0
  95. package/dist/templates/documentation-writer.dna.yaml +19 -0
  96. package/dist/templates/flutter-behavior-lock.dna.yaml +19 -0
  97. package/dist/templates/flutter-refactoring-rescue.dna.yaml +19 -0
  98. package/dist/templates/flutter-rewrite-diagnosis-review.dna.yaml +210 -0
  99. package/dist/templates/flutter-rewrite-fix-review-loop.dna.yaml +260 -0
  100. package/dist/templates/flutter-rewrite-new.dna.yaml +506 -0
  101. package/dist/templates/flutter-rewrite.dna.yaml +20 -0
  102. package/dist/templates/frontend-quality.dna.yaml +19 -0
  103. package/dist/templates/full-pipeline.dna.yaml +20 -0
  104. package/dist/templates/marketplace.d.ts +92 -0
  105. package/dist/templates/marketplace.js +502 -0
  106. package/dist/templates/metadata.d.ts +25 -0
  107. package/dist/templates/metadata.js +136 -3
  108. package/dist/templates/mobile-dev.dna.yaml +19 -0
  109. package/dist/templates/multi-agent-handoff-coordination.dna.yaml +156 -0
  110. package/dist/templates/multi-perspective-review.dna.yaml +19 -0
  111. package/dist/templates/persistent-executor.dna.yaml +19 -0
  112. package/dist/templates/qa-loop.dna.yaml +18 -0
  113. package/dist/templates/release-evidence-gate.dna.yaml +131 -0
  114. package/dist/templates/requirements-gate.dna.yaml +19 -0
  115. package/dist/templates/research-orchestration.dna.yaml +19 -0
  116. package/dist/templates/root-cause-analysis.dna.yaml +19 -0
  117. package/dist/templates/safe-refactoring.dna.yaml +321 -0
  118. package/dist/templates/secure-dev.dna.yaml +19 -0
  119. package/dist/templates/subagent-parallel.dna.yaml +19 -0
  120. package/dist/templates/systematic-debugging.dna.yaml +19 -0
  121. package/dist/templates/tdd-strict.dna.yaml +19 -0
  122. package/dist/templates/ui-visual-regression-qa.dna.yaml +133 -0
  123. package/dist/templates/workflow-author.dna.yaml +302 -0
  124. package/dist/templates/workflow-blocked-scope-triage.dna.yaml +242 -0
  125. package/dist/templates/workflow-completion-audit.dna.yaml +254 -0
  126. package/dist/templates/workflow-cross-boundary-resource-approval.dna.yaml +249 -0
  127. package/dist/templates/workflow-evidence-review.dna.yaml +198 -0
  128. package/dist/templates/workflow-module-inventory.dna.yaml +257 -0
  129. package/dist/templates/workflow-runtime-smoke-review.dna.yaml +252 -0
  130. package/dist/templates/workflow-verification-command-routing.dna.yaml +278 -0
  131. package/dist/workflow/authoring-packet.d.ts +67 -0
  132. package/dist/workflow/authoring-packet.js +218 -0
  133. package/dist/workflow/draft.d.ts +159 -0
  134. package/dist/workflow/draft.js +800 -0
  135. package/dist/workflow/index.d.ts +3 -0
  136. package/dist/workflow/index.js +3 -0
  137. package/dist/workflow/suggestion-review.d.ts +65 -0
  138. package/dist/workflow/suggestion-review.js +245 -0
  139. package/package.json +1 -1
  140. package/spec/README.md +15 -48
  141. package/spec/codex-adapter-contract.md +176 -0
  142. package/spec/sync-target-plan.md +158 -0
  143. package/spec/workflow-capability-ir.md +153 -0
  144. package/spec/agent-dispatch-fix.md +0 -116
  145. package/spec/control-plane-convergence-handoff-2026-04-24.md +0 -432
  146. package/spec/flutter-rewrite-template-optimization.md +0 -301
  147. package/spec/foundation-hardening.md +0 -178
  148. package/spec/hooks-infra-harness-hardening.md +0 -741
  149. package/spec/multi-config.md +0 -172
  150. package/spec/parallel-isolation.md +0 -268
  151. package/spec/template-version-namespace-fix.md +0 -653
  152. package/spec/workflow-pipeline.md +0 -101
@@ -0,0 +1,99 @@
1
+ import type { SyncOutputArtifactId } from "./output-artifact-registry.js";
2
+ import type { PlannedArtifact, SyncTarget } from "./sync-target-plan.js";
3
+ export declare const SYNC_ARTIFACT_REGISTRY_PATH = ".dna/sync/registry.json";
4
+ export declare const SYNC_ARTIFACT_REGISTRY_SCHEMA = "intentdna.sync_artifact_registry.v1";
5
+ export type SyncArtifactOwnership = PlannedArtifact["ownership"] | "unknown";
6
+ export type SyncArtifactStatus = "ok" | "missing" | "drifted" | "untracked";
7
+ export interface SyncArtifactRegistryTemplate {
8
+ sourceDnaIds: string[];
9
+ configFiles: string[];
10
+ compiledIrHash?: string;
11
+ }
12
+ export interface SyncArtifactRegistryEntry {
13
+ artifact: SyncOutputArtifactId;
14
+ kind: string;
15
+ path: string;
16
+ relativePath: string;
17
+ target: SyncTarget | "markdown" | "generic";
18
+ adapter?: SyncTarget;
19
+ installMode?: string;
20
+ ownership: SyncArtifactOwnership;
21
+ trackedByLock: boolean;
22
+ sha256?: string;
23
+ missingAtSync?: boolean;
24
+ }
25
+ export interface SyncArtifactRegistry {
26
+ schema: typeof SYNC_ARTIFACT_REGISTRY_SCHEMA;
27
+ syncedAt: string;
28
+ projectDir: string;
29
+ dnaVersion?: string;
30
+ mode: "write";
31
+ template: SyncArtifactRegistryTemplate;
32
+ artifacts: SyncArtifactRegistryEntry[];
33
+ }
34
+ export interface SyncArtifactRegistryInputEntry {
35
+ artifact: SyncOutputArtifactId;
36
+ path: string;
37
+ kind?: string;
38
+ target?: SyncArtifactRegistryEntry["target"];
39
+ adapter?: SyncTarget;
40
+ installMode?: string;
41
+ ownership?: SyncArtifactOwnership;
42
+ trackedByLock?: boolean;
43
+ }
44
+ export interface WriteSyncArtifactRegistryOptions {
45
+ projectDir: string;
46
+ dnaVersion?: string;
47
+ template: SyncArtifactRegistryTemplate;
48
+ artifacts: SyncArtifactRegistryInputEntry[];
49
+ }
50
+ export interface SyncArtifactInspection extends SyncArtifactRegistryEntry {
51
+ status: SyncArtifactStatus;
52
+ currentSha256?: string;
53
+ reason: string;
54
+ }
55
+ export interface SyncArtifactRegistryInspection {
56
+ registry: SyncArtifactRegistry | null;
57
+ registryPath: string;
58
+ artifacts: SyncArtifactInspection[];
59
+ totals: {
60
+ artifacts: number;
61
+ ok: number;
62
+ missing: number;
63
+ drifted: number;
64
+ untracked: number;
65
+ };
66
+ }
67
+ export interface RemoveSyncArtifactsOptions {
68
+ projectDir?: string;
69
+ dryRun?: boolean;
70
+ }
71
+ export interface RemoveSyncArtifactsResult {
72
+ schema_version: "intentdna.sync_remove_result.v1";
73
+ mode: "sync_registry_remove";
74
+ registryPath: string;
75
+ removed: string[];
76
+ skipped: Array<{
77
+ path: string;
78
+ reason: string;
79
+ }>;
80
+ preservation: {
81
+ note: string;
82
+ kept: Array<{
83
+ path: string;
84
+ reason: string;
85
+ explanation: string;
86
+ }>;
87
+ nextCommands: string[];
88
+ };
89
+ dryRun: boolean;
90
+ totals: {
91
+ removable: number;
92
+ skipped: number;
93
+ };
94
+ boundary: string;
95
+ }
96
+ export declare function writeSyncArtifactRegistry(options: WriteSyncArtifactRegistryOptions): Promise<SyncArtifactRegistry>;
97
+ export declare function readSyncArtifactRegistry(projectDir?: string): Promise<SyncArtifactRegistry | null>;
98
+ export declare function inspectSyncArtifactRegistry(projectDir?: string): Promise<SyncArtifactRegistryInspection>;
99
+ export declare function removeSyncArtifacts(options?: RemoveSyncArtifactsOptions): Promise<RemoveSyncArtifactsResult>;
@@ -0,0 +1,194 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, readFile, realpath, rm, stat, writeFile } from "node:fs/promises";
3
+ import { dirname, relative, resolve } from "node:path";
4
+ export const SYNC_ARTIFACT_REGISTRY_PATH = ".dna/sync/registry.json";
5
+ export const SYNC_ARTIFACT_REGISTRY_SCHEMA = "intentdna.sync_artifact_registry.v1";
6
+ function fileSha256(content) {
7
+ return createHash("sha256").update(content, "utf-8").digest("hex");
8
+ }
9
+ async function computeFileHash(path) {
10
+ try {
11
+ return fileSha256(await readFile(path, "utf-8"));
12
+ }
13
+ catch {
14
+ return undefined;
15
+ }
16
+ }
17
+ function registryPath(projectDir) {
18
+ return resolve(projectDir, SYNC_ARTIFACT_REGISTRY_PATH);
19
+ }
20
+ async function canonicalPath(path) {
21
+ return realpath(path).catch(() => resolve(path));
22
+ }
23
+ async function normalizeEntry(projectDir, entry) {
24
+ const canonicalProjectDir = await canonicalPath(projectDir);
25
+ const absolutePath = resolve(projectDir, entry.path);
26
+ const canonicalArtifactPath = await canonicalPath(absolutePath);
27
+ return {
28
+ artifact: entry.artifact,
29
+ kind: entry.kind ?? entry.artifact,
30
+ path: canonicalArtifactPath,
31
+ relativePath: relative(canonicalProjectDir, canonicalArtifactPath) || ".",
32
+ target: entry.target ?? entry.adapter ?? "generic",
33
+ adapter: entry.adapter,
34
+ installMode: entry.installMode,
35
+ ownership: entry.ownership ?? "unknown",
36
+ trackedByLock: entry.trackedByLock ?? true,
37
+ };
38
+ }
39
+ function uniqueArtifacts(artifacts) {
40
+ const byKey = new Map();
41
+ for (const artifact of artifacts) {
42
+ byKey.set(`${artifact.artifact}:${artifact.path}:${artifact.kind}`, artifact);
43
+ }
44
+ return [...byKey.values()].sort((a, b) => a.target.localeCompare(b.target) ||
45
+ a.relativePath.localeCompare(b.relativePath) ||
46
+ a.kind.localeCompare(b.kind));
47
+ }
48
+ export async function writeSyncArtifactRegistry(options) {
49
+ const projectDir = await canonicalPath(options.projectDir);
50
+ const artifacts = uniqueArtifacts(await Promise.all(options.artifacts.map((entry) => normalizeEntry(projectDir, entry))));
51
+ const withHashes = [];
52
+ for (const artifact of artifacts) {
53
+ const sha256 = await computeFileHash(artifact.path);
54
+ withHashes.push(sha256 ? { ...artifact, sha256 } : { ...artifact, missingAtSync: true });
55
+ }
56
+ const registry = {
57
+ schema: SYNC_ARTIFACT_REGISTRY_SCHEMA,
58
+ syncedAt: new Date().toISOString(),
59
+ projectDir,
60
+ dnaVersion: options.dnaVersion,
61
+ mode: "write",
62
+ template: options.template,
63
+ artifacts: withHashes,
64
+ };
65
+ const path = registryPath(options.projectDir);
66
+ await mkdir(dirname(path), { recursive: true });
67
+ await writeFile(path, JSON.stringify(registry, null, 2) + "\n", "utf-8");
68
+ return registry;
69
+ }
70
+ export async function readSyncArtifactRegistry(projectDir = process.cwd()) {
71
+ try {
72
+ const raw = await readFile(registryPath(projectDir), "utf-8");
73
+ const parsed = JSON.parse(raw);
74
+ if (parsed.schema !== SYNC_ARTIFACT_REGISTRY_SCHEMA || !Array.isArray(parsed.artifacts))
75
+ return null;
76
+ return parsed;
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ }
82
+ async function inspectEntry(entry) {
83
+ if (!entry.sha256) {
84
+ const exists = await stat(entry.path).then(() => true, () => false);
85
+ return {
86
+ ...entry,
87
+ status: exists ? "untracked" : "missing",
88
+ reason: exists ? "No sync-time hash recorded for this artifact." : "Artifact did not exist when inspected.",
89
+ };
90
+ }
91
+ const currentSha256 = await computeFileHash(entry.path);
92
+ if (!currentSha256) {
93
+ return { ...entry, status: "missing", reason: "Artifact path is missing." };
94
+ }
95
+ if (currentSha256 !== entry.sha256) {
96
+ return { ...entry, status: "drifted", currentSha256, reason: "Artifact hash differs from last sync." };
97
+ }
98
+ return { ...entry, status: "ok", currentSha256, reason: "Artifact matches last sync." };
99
+ }
100
+ export async function inspectSyncArtifactRegistry(projectDir = process.cwd()) {
101
+ const registry = await readSyncArtifactRegistry(projectDir);
102
+ const artifacts = registry ? await Promise.all(registry.artifacts.map(inspectEntry)) : [];
103
+ const totals = {
104
+ artifacts: artifacts.length,
105
+ ok: artifacts.filter((entry) => entry.status === "ok").length,
106
+ missing: artifacts.filter((entry) => entry.status === "missing").length,
107
+ drifted: artifacts.filter((entry) => entry.status === "drifted").length,
108
+ untracked: artifacts.filter((entry) => entry.status === "untracked").length,
109
+ };
110
+ return { registry, registryPath: registryPath(projectDir), artifacts, totals };
111
+ }
112
+ function isSafeToRemove(entry) {
113
+ if (entry.status !== "ok")
114
+ return false;
115
+ if (!entry.sha256 || entry.currentSha256 !== entry.sha256)
116
+ return false;
117
+ return entry.ownership === "intentdna_owned" || entry.ownership === "diagnostic";
118
+ }
119
+ function removalSkipExplanation(entry) {
120
+ if (entry.status === "drifted") {
121
+ return "Preserved because the file changed after the last sync; review the diff or rerun dna sync before cleanup.";
122
+ }
123
+ if (entry.status === "missing") {
124
+ return "No file was deleted because the registry path is already missing.";
125
+ }
126
+ if (entry.status === "untracked") {
127
+ return "Preserved because no sync-time hash proves IntentDNA still owns the current file.";
128
+ }
129
+ if (entry.ownership === "merge_preserve_user") {
130
+ return "Preserved because the artifact is a merge-preserve user configuration surface.";
131
+ }
132
+ if (entry.ownership === "owned_block") {
133
+ return "Preserved because IntentDNA owns only an injected block inside this user file.";
134
+ }
135
+ if (entry.ownership === "unknown") {
136
+ return "Preserved because ownership is unknown.";
137
+ }
138
+ return "Preserved because this artifact is outside the safe remove set.";
139
+ }
140
+ export async function removeSyncArtifacts(options = {}) {
141
+ const projectDir = options.projectDir ?? process.cwd();
142
+ const inspection = await inspectSyncArtifactRegistry(projectDir);
143
+ const removed = [];
144
+ const skipped = [];
145
+ const kept = [];
146
+ for (const artifact of inspection.artifacts) {
147
+ if (!isSafeToRemove(artifact)) {
148
+ const reason = `${artifact.ownership}/${artifact.status}`;
149
+ skipped.push({ path: artifact.path, reason });
150
+ kept.push({ path: artifact.path, reason, explanation: removalSkipExplanation(artifact) });
151
+ continue;
152
+ }
153
+ if (!options.dryRun)
154
+ await rm(artifact.path, { recursive: true, force: true });
155
+ removed.push(artifact.path);
156
+ }
157
+ if (inspection.registry && !options.dryRun && skipped.length === 0) {
158
+ await rm(inspection.registryPath, { force: true });
159
+ removed.push(inspection.registryPath);
160
+ }
161
+ else if (inspection.registry && !options.dryRun) {
162
+ const keptPaths = new Set(skipped.map((entry) => entry.path));
163
+ const preservedRegistry = {
164
+ ...inspection.registry,
165
+ artifacts: inspection.registry.artifacts.filter((artifact) => keptPaths.has(artifact.path)),
166
+ };
167
+ await writeFile(inspection.registryPath, JSON.stringify(preservedRegistry, null, 2) + "\n", "utf-8");
168
+ }
169
+ else if (inspection.registry && skipped.length === 0) {
170
+ removed.push(inspection.registryPath);
171
+ }
172
+ return {
173
+ schema_version: "intentdna.sync_remove_result.v1",
174
+ mode: "sync_registry_remove",
175
+ registryPath: inspection.registryPath,
176
+ removed,
177
+ skipped,
178
+ preservation: {
179
+ note: "Safe remove preserves user-owned, merge-preserve, owned-block, drifted, missing, untracked, and unknown-ownership registry entries.",
180
+ kept,
181
+ nextCommands: [
182
+ "dna sync status",
183
+ "dna sync doctor",
184
+ "dna sync remove --dry-run",
185
+ ],
186
+ },
187
+ dryRun: options.dryRun === true,
188
+ totals: {
189
+ removable: removed.length,
190
+ skipped: skipped.length,
191
+ },
192
+ boundary: "remove only deletes hash-matching IntentDNA-owned/diagnostic artifacts from the sync registry; merge-preserve/user-owned files are skipped",
193
+ };
194
+ }
@@ -0,0 +1,45 @@
1
+ import type { SyncOutputArtifactId } from "./output-artifact-registry.js";
2
+ export type SyncTarget = "claude_code" | "codex_cli";
3
+ export type SyncTargetMode = "write" | "remove" | "dry_run";
4
+ export type CapabilitySupportStatus = "supported" | "partial" | "unknown" | "unsupported";
5
+ export type EvidenceClassification = "enterprise_evidence" | "diagnostic_only" | "unsupported";
6
+ export interface PlannedArtifact {
7
+ kind: string;
8
+ path: string;
9
+ ownership: "intentdna_owned" | "owned_block" | "merge_preserve_user" | "diagnostic";
10
+ trackedByLock: boolean;
11
+ outputArtifact?: SyncOutputArtifactId;
12
+ }
13
+ export interface CapabilityReportEntry {
14
+ status: CapabilitySupportStatus;
15
+ surfaces: string[];
16
+ evidenceRefs: string[];
17
+ notes: string[];
18
+ }
19
+ export interface AdapterCapabilityReport {
20
+ adapter: SyncTarget;
21
+ runtime?: string;
22
+ capabilities: Record<string, CapabilityReportEntry>;
23
+ unsupported: string[];
24
+ }
25
+ export interface EvidencePlan {
26
+ primary: "RuntimeDecisionEvent" | "none";
27
+ diagnosticArtifacts: string[];
28
+ classification: EvidenceClassification;
29
+ notes: string[];
30
+ }
31
+ export interface SyncTargetPlan {
32
+ target: SyncTarget;
33
+ mode?: SyncTargetMode;
34
+ plannedArtifacts: PlannedArtifact[];
35
+ capabilityReport?: AdapterCapabilityReport;
36
+ evidencePlan: EvidencePlan;
37
+ warnings: string[];
38
+ unsupported: string[];
39
+ }
40
+ export interface SyncTargetTrackedOutput {
41
+ artifact: SyncOutputArtifactId;
42
+ path: string;
43
+ }
44
+ export declare function trackedOutputsForSyncTargetPlan(plan: SyncTargetPlan): SyncTargetTrackedOutput[];
45
+ export declare function formatSyncTargetPlanDiagnostics(plan: SyncTargetPlan): string;
@@ -0,0 +1,19 @@
1
+ export function trackedOutputsForSyncTargetPlan(plan) {
2
+ return plan.plannedArtifacts
3
+ .filter((artifact) => typeof artifact.outputArtifact === "string" && artifact.outputArtifact.length > 0)
4
+ .map((artifact) => ({ artifact: artifact.outputArtifact, path: artifact.path }));
5
+ }
6
+ export function formatSyncTargetPlanDiagnostics(plan) {
7
+ const lines = [];
8
+ if (plan.warnings.length > 0) {
9
+ lines.push(`${plan.target} warnings:`);
10
+ for (const warning of plan.warnings)
11
+ lines.push(` - ${warning}`);
12
+ }
13
+ if (plan.unsupported.length > 0) {
14
+ lines.push(`${plan.target} unsupported gaps:`);
15
+ for (const gap of plan.unsupported)
16
+ lines.push(` - ${gap}`);
17
+ }
18
+ return lines.length > 0 ? `${lines.join("\n")}\n` : "";
19
+ }
@@ -5,7 +5,7 @@ export interface ControllerKindMetadata {
5
5
  kind: ControllerKind;
6
6
  label: string;
7
7
  supportsSkillGeneration: boolean;
8
- requiredRoleKeys: readonly ControllerRoleKey[];
8
+ requiredRoleKeys: readonly string[];
9
9
  diagnosisRoleKeys: readonly ControllerRoleKey[];
10
10
  fixRoleKeys: readonly ControllerRoleKey[];
11
11
  }
@@ -23,6 +23,13 @@ export declare const CONTROLLER_KIND_REGISTRY: readonly [{
23
23
  readonly requiredRoleKeys: readonly ["analyzer", "analysis_reviewer", "surgeon", "fix_reviewer", "test_runner"];
24
24
  readonly diagnosisRoleKeys: readonly ["analyzer", "analysis_reviewer"];
25
25
  readonly fixRoleKeys: readonly ["surgeon", "fix_reviewer", "test_runner"];
26
+ }, {
27
+ readonly kind: "sequential";
28
+ readonly label: "Sequential workflow gate";
29
+ readonly supportsSkillGeneration: false;
30
+ readonly requiredRoleKeys: readonly [];
31
+ readonly diagnosisRoleKeys: readonly [];
32
+ readonly fixRoleKeys: readonly [];
26
33
  }];
27
34
  export declare function getControllerKindMetadata(kind: unknown): ControllerKindMetadata | undefined;
28
35
  export declare function isSupportedControllerKind(kind: unknown): kind is ControllerKind;
@@ -22,6 +22,14 @@ export const CONTROLLER_KIND_REGISTRY = [
22
22
  diagnosisRoleKeys: ["analyzer", "analysis_reviewer"],
23
23
  fixRoleKeys: ["surgeon", "fix_reviewer", "test_runner"],
24
24
  },
25
+ {
26
+ kind: "sequential",
27
+ label: "Sequential workflow gate",
28
+ supportsSkillGeneration: false,
29
+ requiredRoleKeys: [],
30
+ diagnosisRoleKeys: [],
31
+ fixRoleKeys: [],
32
+ },
25
33
  ];
26
34
  const CONTROLLER_KIND_BY_ID = new Map(CONTROLLER_KIND_REGISTRY.map((entry) => [entry.kind, entry]));
27
35
  export function getControllerKindMetadata(kind) {
@@ -96,7 +96,8 @@ export interface VerifierCommandPolicy {
96
96
  allow_builtin_asserts?: string[];
97
97
  }
98
98
  /** Handoff artifact type — what kind of artifact is passed between steps */
99
- export type HandoffType = "file" | "directory" | "test_result" | "git_commit" | "summary" | "state";
99
+ export declare const HANDOFF_TYPE_VALUES: readonly ["file", "directory", "test_result", "git_commit", "summary", "state"];
100
+ export type HandoffType = typeof HANDOFF_TYPE_VALUES[number];
100
101
  /** A single artifact consumed or produced by a workflow step */
101
102
  export interface HandoffArtifact {
102
103
  type: HandoffType;
@@ -142,14 +143,18 @@ export interface StepHandoff {
142
143
  export interface TransitionDef {
143
144
  from: string;
144
145
  to: string;
145
- condition: "pass" | "fail" | "always" | "error";
146
+ condition: TransitionCondition;
146
147
  }
148
+ export declare const TRANSITION_CONDITION_VALUES: readonly ["pass", "fail", "always", "error"];
149
+ export type TransitionCondition = typeof TRANSITION_CONDITION_VALUES[number];
147
150
  /** Retry policy for the workflow */
148
151
  export interface RetryPolicy {
149
152
  max_retries: number;
150
153
  retry_from?: string;
151
- backoff?: "none" | "linear" | "exponential";
154
+ backoff?: RetryBackoff;
152
155
  }
156
+ export declare const RETRY_BACKOFF_VALUES: readonly ["none", "linear", "exponential"];
157
+ export type RetryBackoff = typeof RETRY_BACKOFF_VALUES[number];
153
158
  /** Predefined checkpoint assertion types — compiler knows how to translate each to shell */
154
159
  export type CheckpointAssertType = "clean_working_tree" | "no_test_regression" | "lint_passing" | "build_passing";
155
160
  /** Step checkpoint — condition that must be met before transitioning to next step */
@@ -157,8 +162,18 @@ export interface StepCheckpoint {
157
162
  assert: CheckpointAssertType | string;
158
163
  command?: string;
159
164
  message: string;
160
- action?: "block" | "warn";
161
- }
165
+ action?: CheckpointAction;
166
+ }
167
+ export declare const CHECKPOINT_ACTION_VALUES: readonly ["block", "warn"];
168
+ export type CheckpointAction = typeof CHECKPOINT_ACTION_VALUES[number];
169
+ export declare const WORKFLOW_ISOLATION_VALUES: readonly ["none", "worktree", "auto"];
170
+ export type WorkflowIsolation = typeof WORKFLOW_ISOLATION_VALUES[number];
171
+ export declare const WORKFLOW_STEP_FAILURE_VALUES: readonly ["retry_with_feedback", "handoff", "skip"];
172
+ export type WorkflowStepFailureAction = typeof WORKFLOW_STEP_FAILURE_VALUES[number];
173
+ export declare const WORKFLOW_HANDOFF_EXHAUSTED_VALUES: readonly ["skip", "stop"];
174
+ export type WorkflowHandoffExhaustedAction = typeof WORKFLOW_HANDOFF_EXHAUSTED_VALUES[number];
175
+ export declare const WORKFLOW_MERGE_STRATEGY_VALUES: readonly ["escalate"];
176
+ export type WorkflowMergeStrategy = typeof WORKFLOW_MERGE_STRATEGY_VALUES[number];
162
177
  /** Completion check for a workflow step */
163
178
  export interface CompletionCheck {
164
179
  file_exists?: string;
@@ -181,17 +196,17 @@ export interface WorkflowStepDef {
181
196
  completion?: CompletionCheck[];
182
197
  checkpoints?: StepCheckpoint[];
183
198
  handoff?: StepHandoff;
184
- isolation?: "none" | "worktree" | "auto";
199
+ isolation?: WorkflowIsolation;
185
200
  enforce?: {
186
201
  read_only?: boolean;
187
202
  relax_after_iteration?: number;
188
203
  additional_write_paths?: string[];
189
204
  };
190
205
  max_attempts?: number;
191
- on_fail?: "retry_with_feedback" | "handoff" | "skip";
206
+ on_fail?: WorkflowStepFailureAction;
192
207
  handoff_to?: string;
193
208
  max_handoffs?: number;
194
- on_handoff_exhausted?: "skip" | "stop";
209
+ on_handoff_exhausted?: WorkflowHandoffExhaustedAction;
195
210
  blocked_items_path?: string;
196
211
  }
197
212
  /** Top-level workflow definition */
@@ -204,10 +219,10 @@ export interface WorkflowDef {
204
219
  max_rounds?: number;
205
220
  produces?: HandoffArtifact[];
206
221
  consumes?: HandoffArtifact[];
207
- default_isolation?: "none" | "worktree" | "auto";
208
- merge_strategy?: "escalate";
222
+ default_isolation?: WorkflowIsolation;
223
+ merge_strategy?: WorkflowMergeStrategy;
209
224
  }
210
- export type ControllerKind = "fix_loop" | "flutter_rewrite_fix_loop";
225
+ export type ControllerKind = "fix_loop" | "flutter_rewrite_fix_loop" | "sequential";
211
226
  export interface ControllerPolicyDef {
212
227
  diagnosis_reuse_criteria?: string[];
213
228
  progress_provenance_checks?: string[];
@@ -223,18 +238,13 @@ export interface ControllerDef {
223
238
  kind: ControllerKind;
224
239
  max_rounds: number;
225
240
  progress_artifact_path: string;
226
- diagnosis_workflow: string;
227
- fix_workflow: string;
228
- diagnosis_artifact_path: string;
229
- diagnosis_review_artifact_path: string;
230
- review_artifact_path: string;
231
- roles: {
232
- analyzer: string;
233
- analysis_reviewer: string;
234
- surgeon: string;
235
- fix_reviewer: string;
236
- test_runner: string;
237
- };
241
+ workflow?: string;
242
+ diagnosis_workflow?: string;
243
+ fix_workflow?: string;
244
+ diagnosis_artifact_path?: string;
245
+ diagnosis_review_artifact_path?: string;
246
+ review_artifact_path?: string;
247
+ roles: Record<string, string>;
238
248
  stop_format: "configured_stop_report" | "zh_three_part";
239
249
  policy?: ControllerPolicyDef;
240
250
  }
@@ -479,6 +489,7 @@ export interface StepEnforceRule {
479
489
  export interface WorkflowIR {
480
490
  workflow_name: string;
481
491
  namespace: string;
492
+ step_ids?: string[];
482
493
  step_checkpoints: StepCheckpointIR[];
483
494
  active_roles: string[];
484
495
  handoff_chain: HandoffChainEntry[];
@@ -502,6 +513,7 @@ export interface CompletedArtifactEntry {
502
513
  /** Runtime workflow state written to .dna/state/workflow.json */
503
514
  export interface WorkflowState {
504
515
  active: boolean;
516
+ workflow_asset?: string;
505
517
  workflow: string;
506
518
  current_step: string;
507
519
  current_role: string;
@@ -7,4 +7,13 @@
7
7
  * Layer 3: Regulatory Regions (context-dependent expression)
8
8
  * Layer 4: Epigenetic Markers (experience-driven modulation)
9
9
  */
10
- export {};
10
+ // ── Workflow System ─────────────────────────────────────────
11
+ /** Handoff artifact type — what kind of artifact is passed between steps */
12
+ export const HANDOFF_TYPE_VALUES = ["file", "directory", "test_result", "git_commit", "summary", "state"];
13
+ export const TRANSITION_CONDITION_VALUES = ["pass", "fail", "always", "error"];
14
+ export const RETRY_BACKOFF_VALUES = ["none", "linear", "exponential"];
15
+ export const CHECKPOINT_ACTION_VALUES = ["block", "warn"];
16
+ export const WORKFLOW_ISOLATION_VALUES = ["none", "worktree", "auto"];
17
+ export const WORKFLOW_STEP_FAILURE_VALUES = ["retry_with_feedback", "handoff", "skip"];
18
+ export const WORKFLOW_HANDOFF_EXHAUSTED_VALUES = ["skip", "stop"];
19
+ export const WORKFLOW_MERGE_STRATEGY_VALUES = ["escalate"];
@@ -8,6 +8,7 @@
8
8
  * - No circular inheritance
9
9
  */
10
10
  import { isAbsolute } from "node:path";
11
+ import { CHECKPOINT_ACTION_VALUES, HANDOFF_TYPE_VALUES, RETRY_BACKOFF_VALUES, TRANSITION_CONDITION_VALUES, WORKFLOW_HANDOFF_EXHAUSTED_VALUES, WORKFLOW_ISOLATION_VALUES, WORKFLOW_MERGE_STRATEGY_VALUES, WORKFLOW_STEP_FAILURE_VALUES, } from "./types.js";
11
12
  import { validateController } from "./validators/controllers.js";
12
13
  const SAFE_GENERATED_IDENTIFIER = /^[A-Za-z0-9_-]+$/;
13
14
  const SAFE_GENERATED_DISPLAY_NAME = /^[A-Za-z0-9][A-Za-z0-9 _-]*$/;
@@ -503,25 +504,22 @@ function validateWorkflow(workflow, roleNames, pathPrefix) {
503
504
  }
504
505
  // on_fail: must be a valid enum value
505
506
  if (step.on_fail !== undefined) {
506
- const validOnFail = ["retry_with_feedback", "handoff", "skip"];
507
- if (!validOnFail.includes(step.on_fail)) {
508
- errors.push({ path: `${stepPath}.on_fail`, message: `must be one of: ${validOnFail.join(", ")}` });
507
+ if (!WORKFLOW_STEP_FAILURE_VALUES.includes(step.on_fail)) {
508
+ errors.push({ path: `${stepPath}.on_fail`, message: `must be one of: ${WORKFLOW_STEP_FAILURE_VALUES.join(", ")}` });
509
509
  }
510
510
  }
511
511
  // on_handoff_exhausted: must be a valid enum value
512
512
  if (step.on_handoff_exhausted !== undefined) {
513
- const validExhausted = ["skip", "stop"];
514
- if (!validExhausted.includes(step.on_handoff_exhausted)) {
515
- errors.push({ path: `${stepPath}.on_handoff_exhausted`, message: `must be one of: ${validExhausted.join(", ")}` });
513
+ if (!WORKFLOW_HANDOFF_EXHAUSTED_VALUES.includes(step.on_handoff_exhausted)) {
514
+ errors.push({ path: `${stepPath}.on_handoff_exhausted`, message: `must be one of: ${WORKFLOW_HANDOFF_EXHAUSTED_VALUES.join(", ")}` });
516
515
  }
517
516
  }
518
517
  // isolation: must be a valid enum value
519
518
  if (step.isolation !== undefined) {
520
- const validIsolation = ["none", "worktree", "auto"];
521
- if (!validIsolation.includes(step.isolation)) {
519
+ if (!WORKFLOW_ISOLATION_VALUES.includes(step.isolation)) {
522
520
  errors.push({
523
521
  path: `${stepPath}.isolation`,
524
- message: `must be one of: ${validIsolation.join(", ")}`,
522
+ message: `must be one of: ${WORKFLOW_ISOLATION_VALUES.join(", ")}`,
525
523
  });
526
524
  }
527
525
  }
@@ -540,6 +538,12 @@ function validateWorkflow(workflow, roleNames, pathPrefix) {
540
538
  for (let j = 0; j < artifacts.length; j++) {
541
539
  const artifact = artifacts[j];
542
540
  const artifactPath = `${stepPath}.handoff.${direction}[${j}]`;
541
+ if (!HANDOFF_TYPE_VALUES.includes(artifact.type)) {
542
+ errors.push({ path: `${artifactPath}.type`, message: `must be one of: ${HANDOFF_TYPE_VALUES.join(", ")}` });
543
+ }
544
+ if (!artifact.description) {
545
+ errors.push({ path: `${artifactPath}.description`, message: "handoff artifact requires 'description'" });
546
+ }
543
547
  if (artifact.required !== false && artifact.type !== "git_commit" && !artifact.path && !artifact.name && !artifact.artifact_id) {
544
548
  errors.push({ path: artifactPath, message: "required handoff artifacts must define path, name, or artifact_id" });
545
549
  }
@@ -567,7 +571,7 @@ function validateWorkflow(workflow, roleNames, pathPrefix) {
567
571
  if (!cp.message) {
568
572
  errors.push({ path: `${cpPath}.message`, message: "checkpoint requires 'message'" });
569
573
  }
570
- if (cp.action && cp.action !== "block" && cp.action !== "warn") {
574
+ if (cp.action && !CHECKPOINT_ACTION_VALUES.includes(cp.action)) {
571
575
  errors.push({ path: `${cpPath}.action`, message: "checkpoint action must be 'block' or 'warn'" });
572
576
  }
573
577
  }
@@ -607,26 +611,23 @@ function validateWorkflow(workflow, roleNames, pathPrefix) {
607
611
  }
608
612
  // Validate default_isolation
609
613
  if (workflow.default_isolation !== undefined) {
610
- const validIsolation = ["none", "worktree", "auto"];
611
- if (!validIsolation.includes(workflow.default_isolation)) {
614
+ if (!WORKFLOW_ISOLATION_VALUES.includes(workflow.default_isolation)) {
612
615
  errors.push({
613
616
  path: `${path}.default_isolation`,
614
- message: `must be one of: ${validIsolation.join(", ")}`,
617
+ message: `must be one of: ${WORKFLOW_ISOLATION_VALUES.join(", ")}`,
615
618
  });
616
619
  }
617
620
  }
618
621
  // Validate merge_strategy
619
622
  if (workflow.merge_strategy !== undefined) {
620
- const validStrategies = ["escalate"];
621
- if (!validStrategies.includes(workflow.merge_strategy)) {
623
+ if (!WORKFLOW_MERGE_STRATEGY_VALUES.includes(workflow.merge_strategy)) {
622
624
  errors.push({
623
625
  path: `${path}.merge_strategy`,
624
- message: `must be one of: ${validStrategies.join(", ")}`,
626
+ message: `must be one of: ${WORKFLOW_MERGE_STRATEGY_VALUES.join(", ")}`,
625
627
  });
626
628
  }
627
629
  }
628
630
  // Validate transitions
629
- const validConditions = ["pass", "fail", "always", "error"];
630
631
  for (let i = 0; i < (workflow.transitions?.length ?? 0); i++) {
631
632
  const t = workflow.transitions[i];
632
633
  const tPath = `${path}.transitions[${i}]`;
@@ -642,10 +643,10 @@ function validateWorkflow(workflow, roleNames, pathPrefix) {
642
643
  else if (!stepIds.has(t.to)) {
643
644
  errors.push({ path: `${tPath}.to`, message: `transition references unknown step '${t.to}'` });
644
645
  }
645
- if (!t.condition || !validConditions.includes(t.condition)) {
646
+ if (!t.condition || !TRANSITION_CONDITION_VALUES.includes(t.condition)) {
646
647
  errors.push({
647
648
  path: `${tPath}.condition`,
648
- message: `transition condition must be one of: ${validConditions.join(", ")}`,
649
+ message: `transition condition must be one of: ${TRANSITION_CONDITION_VALUES.join(", ")}`,
649
650
  });
650
651
  }
651
652
  }
@@ -665,11 +666,10 @@ function validateWorkflow(workflow, roleNames, pathPrefix) {
665
666
  message: `retry_from references unknown step '${rp.retry_from}'`,
666
667
  });
667
668
  }
668
- const validBackoff = ["none", "linear", "exponential"];
669
- if (rp.backoff && !validBackoff.includes(rp.backoff)) {
669
+ if (rp.backoff && !RETRY_BACKOFF_VALUES.includes(rp.backoff)) {
670
670
  errors.push({
671
671
  path: `${rpPath}.backoff`,
672
- message: `backoff must be one of: ${validBackoff.join(", ")}`,
672
+ message: `backoff must be one of: ${RETRY_BACKOFF_VALUES.join(", ")}`,
673
673
  });
674
674
  }
675
675
  }