intentdna 1.7.5 → 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 +703 -36
  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 +5 -0
  53. package/dist/hooks/cli.js +149 -5
  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,1249 @@
1
+ import { listWorkflowAssets, } from "./catalog.js";
2
+ import { buildWorkflowAssetEvidenceIndex, } from "./evidence.js";
3
+ function compactEvidenceModule(module) {
4
+ const remaining = module.remainingCount > 0 ? ` remaining=${module.remainingCount}` : "";
5
+ const blocked = module.blockedCount > 0 ? ` blocked=${module.blockedCount}` : "";
6
+ const red = module.newRedCount > 0 ? ` new_red=${module.newRedCount}` : "";
7
+ return `${module.module}: ${module.verdict}${module.moduleComplete ? " complete" : " incomplete"}${remaining}${blocked}${red}`;
8
+ }
9
+ function compactEvidenceCapture(capture) {
10
+ const result = capture.result ? `/${capture.result}` : "";
11
+ const scope = [capture.workflowId, capture.stepId].filter((value) => value && value !== "unknown").join(":");
12
+ const prefix = `${capture.capturedAt.slice(0, 10)} ${capture.fact}${result}${scope ? ` ${scope}` : ""}`;
13
+ return `${prefix}: ${capture.summary}`.slice(0, 240);
14
+ }
15
+ function c5GapCaptures(index) {
16
+ return index.captureFacts.filter((capture) => capture.fact === "blocker" ||
17
+ capture.fact === "manual_intervention" ||
18
+ capture.fact === "retry" ||
19
+ capture.result === "blocked" ||
20
+ capture.result === "failure" ||
21
+ capture.result === "unsupported");
22
+ }
23
+ export function activeEvidenceModules(index) {
24
+ return index.modules.filter((module) => module.verdict !== "PASS" ||
25
+ !module.moduleComplete ||
26
+ module.remainingCount > 0 ||
27
+ module.newRedCount > 0);
28
+ }
29
+ function totalFixed(index) {
30
+ return index.modules.reduce((sum, module) => sum + module.fixedCount, 0);
31
+ }
32
+ function sourceModules(index) {
33
+ return index.modules.map((module) => module.module);
34
+ }
35
+ function activeFollowupNonReplacementNote(sourceAsset, followupAsset) {
36
+ return `${followupAsset} is a saved draft for unfinished ${sourceAsset} evidence; it does not replace, delete, or auto-adopt ${sourceAsset}.`;
37
+ }
38
+ function activeFollowupContinuityBoundary(sourceAsset, followupAsset) {
39
+ return [
40
+ activeFollowupNonReplacementNote(sourceAsset, followupAsset),
41
+ `${sourceAsset} remains the original workflow asset.`,
42
+ "flutter-rewrite-fix-review-loop remains the reusable fix loop asset.",
43
+ "Use the saved draft only to review unfinished dogfood evidence before any explicit adoption, organization binding, rollout, or runtime sync.",
44
+ ].join(" ");
45
+ }
46
+ function activeFollowupPlainLanguage(sourceAsset, followupAsset) {
47
+ return {
48
+ label: "saved unfinished-work draft",
49
+ purpose: "Make unfinished dogfood evidence explicit for manager review before stability, rollout, sync, publication readiness, or evolution claims.",
50
+ workflowImprovement: "Adds a concrete evidence checkpoint to the existing workflow route so active unfinished work is inspected, diffed, and accepted before anyone treats the source asset as stable.",
51
+ preserves: [
52
+ `${sourceAsset} remains the original workflow asset.`,
53
+ "flutter-rewrite-fix-review-loop remains the reusable fix loop asset.",
54
+ `${followupAsset} is only a review draft until a manager accepts it through diff, adoption, bind, and rollout gates.`,
55
+ ],
56
+ requiredBeforeUse: [
57
+ `dna assets diff ${sourceAsset} ${followupAsset}`,
58
+ `record explicit manager rationale for dna assets diff ${sourceAsset} ${followupAsset}`,
59
+ `dna assets adoption ${followupAsset}`,
60
+ `dna org bind --asset ${followupAsset} --status active --sync-target codex`,
61
+ "dna org rollout",
62
+ ],
63
+ };
64
+ }
65
+ function capturedRunFactPlainLanguage(sourceAsset) {
66
+ return {
67
+ label: "captured run facts review",
68
+ purpose: "Turn local hook-observed failures, blockers, retries, unsupported paths, or human interventions into a review queue before changing the workflow.",
69
+ workflowImprovement: "Keeps real run friction visible in workflow mining so the next edit starts from evidence instead of guesswork.",
70
+ preserves: [
71
+ `${sourceAsset} remains unchanged until a reviewed edit plan or authored draft is explicitly saved and accepted.`,
72
+ "C5 facts stay local evidence; they do not become policy, adoption, rollout, sync, or runtime authority.",
73
+ "C4 RuntimeDecisionEvent remains the provenance source for runtime decisions.",
74
+ ],
75
+ requiredBeforeUse: [
76
+ `dna assets evidence ${sourceAsset} --json`,
77
+ `dna workflow suggestions ${sourceAsset} --json`,
78
+ "record explicit manager rationale before any accepted asset change",
79
+ `dna assets diff ${sourceAsset} <reviewed-candidate>`,
80
+ "explicit adoption, organization binding, rollout, and runtime sync only after review",
81
+ ],
82
+ };
83
+ }
84
+ function c5CaptureBootstrapCommands(assetName) {
85
+ return [
86
+ `dna init --template ${assetName}`,
87
+ "dna sync --codex --dry-run",
88
+ "dna sync --codex",
89
+ `dna workflow suggestions ${assetName} --json`,
90
+ `dna workflow mine ${assetName} --json`,
91
+ ];
92
+ }
93
+ function c5CaptureBootstrapPlainLanguage(sourceAsset) {
94
+ return {
95
+ label: "local C5 evidence bootstrap",
96
+ purpose: "Make the empty evidence state actionable by telling the user how to run the workflow locally before mining improvements.",
97
+ workflowImprovement: "Prevents workflow mining from feeling like an internal dogfood report when there are no local hook-captured facts yet.",
98
+ preserves: [
99
+ `${sourceAsset} remains unchanged; this candidate does not create, delete, replace, adopt, bind, or sync an asset by itself.`,
100
+ "Local init/sync are explicit later commands that install runtime artifacts only when the user runs them.",
101
+ "C5 facts still become suggestions and review queues only; they never become policy or runtime authority automatically.",
102
+ ],
103
+ requiredBeforeUse: c5CaptureBootstrapCommands(sourceAsset),
104
+ };
105
+ }
106
+ function sourceWorkflows(asset, ids) {
107
+ const available = new Set(asset.workflows.map((workflow) => workflow.id));
108
+ return ids.filter((id) => available.has(id));
109
+ }
110
+ function candidateReviewCommands(params) {
111
+ return [...new Set([
112
+ `dna assets lineage ${params.sourceAsset}`,
113
+ `dna assets evidence ${params.sourceAsset}`,
114
+ params.nextCommand,
115
+ `dna assets diff ${params.sourceAsset} ${params.candidateAssetName}`,
116
+ `record explicit manager rationale for dna assets diff ${params.sourceAsset} ${params.candidateAssetName}`,
117
+ `dna assets adoption ${params.candidateAssetName}`,
118
+ `dna org bind --asset ${params.candidateAssetName} --status active --sync-target codex`,
119
+ "dna org rollout",
120
+ ])];
121
+ }
122
+ function reviewAction(command, reason) {
123
+ return { kind: "review", command, reason };
124
+ }
125
+ function templateIdForAsset(assetName) {
126
+ return `template_${assetName.replaceAll("-", "_")}`;
127
+ }
128
+ function candidateActionPath(candidate) {
129
+ const createsDraft = candidate.action.kind !== "review";
130
+ const sameAsset = candidate.sourceAsset === candidate.candidateAssetName;
131
+ const steps = [
132
+ {
133
+ id: "source_lineage",
134
+ label: "Inspect source asset lineage",
135
+ command: `dna assets lineage ${candidate.sourceAsset}`,
136
+ purpose: "Confirm the source workflow asset, cascade, evidence, and evolution route before acting on the candidate.",
137
+ writes: false,
138
+ status: "ready",
139
+ },
140
+ {
141
+ id: "source_evidence",
142
+ label: "Inspect dogfood evidence",
143
+ command: `dna assets evidence ${candidate.sourceAsset}`,
144
+ purpose: "Check the dogfood modules and active blockers that justify this candidate.",
145
+ writes: false,
146
+ status: "ready",
147
+ },
148
+ {
149
+ id: "candidate_action",
150
+ label: createsDraft ? "Create reviewable candidate draft" : "Review existing candidate asset",
151
+ command: candidate.nextCommand,
152
+ purpose: createsDraft
153
+ ? "Turn evidence into an explicit workflow asset draft or edit-plan version before diff, adoption, rollout, sync, or evolution."
154
+ : "Review the existing or built-in candidate asset before organization binding, rollout, sync, or evolution.",
155
+ writes: createsDraft,
156
+ status: "current",
157
+ },
158
+ ];
159
+ if (createsDraft) {
160
+ steps.push({
161
+ id: "candidate_inspect",
162
+ label: "Inspect candidate draft",
163
+ command: `dna assets inspect ${candidate.candidateAssetName} --evidence`,
164
+ purpose: "Read the saved candidate asset draft and linked evidence before comparing it.",
165
+ writes: false,
166
+ status: "later",
167
+ }, {
168
+ id: "candidate_governance",
169
+ label: "Review candidate cascade and governance",
170
+ command: `dna assets governance ${candidate.candidateAssetName}`,
171
+ purpose: "Check cascade inheritance, controllers, evidence posture, and evolution posture before adoption.",
172
+ writes: false,
173
+ status: "later",
174
+ }, {
175
+ id: "candidate_diff",
176
+ label: "Compare source and candidate",
177
+ command: `dna assets diff ${candidate.sourceAsset} ${candidate.candidateAssetName}`,
178
+ purpose: "Review what changed before adoption or organization binding.",
179
+ writes: false,
180
+ status: "later",
181
+ }, {
182
+ id: "adoption_review",
183
+ label: "Review adoption readiness",
184
+ command: `dna assets adoption ${candidate.candidateAssetName}`,
185
+ purpose: "Check workflow shape, cascade, evidence, configuration, and governance before adoption.",
186
+ writes: false,
187
+ status: "later",
188
+ });
189
+ }
190
+ else if (!sameAsset) {
191
+ steps.push({
192
+ id: "candidate_inspect",
193
+ label: "Inspect candidate asset",
194
+ command: `dna assets inspect ${candidate.candidateAssetName} --evidence`,
195
+ purpose: "Inspect the existing candidate asset before adoption or organization binding.",
196
+ writes: false,
197
+ status: "ready",
198
+ }, {
199
+ id: "candidate_governance",
200
+ label: "Review candidate cascade and governance",
201
+ command: `dna assets governance ${candidate.candidateAssetName}`,
202
+ purpose: "Check cascade inheritance, controllers, evidence posture, and evolution posture before adoption.",
203
+ writes: false,
204
+ status: "ready",
205
+ });
206
+ }
207
+ steps.push({
208
+ id: "evolution_preview",
209
+ label: "Preview self-evolution markers",
210
+ command: `dna evolve ${templateIdForAsset(candidate.candidateAssetName)}`,
211
+ purpose: "Review outcome-derived marker changes without persisting them; mutation stays behind explicit dna sync --evolve after human review.",
212
+ writes: false,
213
+ status: "later",
214
+ }, {
215
+ id: "organization_bind",
216
+ label: "Bind accepted asset to organization",
217
+ command: `dna org bind --asset ${candidate.candidateAssetName} --status active --sync-target codex`,
218
+ purpose: "Record the accepted candidate asset in the organization route before runtime distribution.",
219
+ writes: true,
220
+ status: "later",
221
+ }, {
222
+ id: "rollout_review",
223
+ label: "Review rollout readiness",
224
+ command: "dna org rollout",
225
+ purpose: "Check target-level ready, partial, and blocked distribution before runtime sync.",
226
+ writes: false,
227
+ status: "later",
228
+ });
229
+ return steps;
230
+ }
231
+ function activeFollowupActionPath(route) {
232
+ const steps = [
233
+ {
234
+ id: "source_lineage",
235
+ label: "Inspect source asset lineage",
236
+ command: `dna assets lineage ${route.sourceAsset}`,
237
+ purpose: "Confirm the source workflow asset, cascade, evidence, and evolution route before acting on the saved draft.",
238
+ writes: false,
239
+ status: "ready",
240
+ },
241
+ {
242
+ id: "source_evidence",
243
+ label: "Inspect dogfood evidence",
244
+ command: `dna assets evidence ${route.sourceAsset}`,
245
+ purpose: "Check the dogfood modules and active blockers that justify this saved draft.",
246
+ writes: false,
247
+ status: "ready",
248
+ },
249
+ ];
250
+ if (route.status === "draft_missing") {
251
+ steps.push({
252
+ id: "candidate_action",
253
+ label: "Save unfinished-work draft",
254
+ command: route.createCommand,
255
+ purpose: `${activeFollowupNonReplacementNote(route.sourceAsset, route.candidateAssetName)} Create it before diff, adoption, rollout, sync, or evolution.`,
256
+ writes: true,
257
+ status: "current",
258
+ }, {
259
+ id: "candidate_inspect",
260
+ label: "Inspect unfinished-work draft",
261
+ command: route.inspectCommand,
262
+ purpose: "Read the saved unfinished-work draft and linked evidence before comparing it with the unchanged source asset.",
263
+ writes: false,
264
+ status: "later",
265
+ }, {
266
+ id: "candidate_governance",
267
+ label: "Review candidate cascade and governance",
268
+ command: route.governanceCommand,
269
+ purpose: "Check cascade inheritance, controllers, evidence posture, and evolution posture before adoption.",
270
+ writes: false,
271
+ status: "later",
272
+ }, {
273
+ id: "candidate_diff",
274
+ label: "Compare source and follow-up",
275
+ command: route.diffCommand,
276
+ purpose: "Review what changed before adoption or organization binding; this comparison is not a deletion or replacement of the source asset.",
277
+ writes: false,
278
+ status: "later",
279
+ }, {
280
+ id: "manager_rationale",
281
+ label: "Record manager rationale",
282
+ command: `record explicit manager rationale for ${route.diffCommand}`,
283
+ purpose: "Capture why the diff is acceptable before adoption, organization binding, rollout, or sync.",
284
+ writes: false,
285
+ status: "later",
286
+ }, {
287
+ id: "adoption_review",
288
+ label: "Review adoption readiness",
289
+ command: route.adoptionCommand,
290
+ purpose: "Check workflow shape, cascade, evidence, configuration, and governance before adoption.",
291
+ writes: false,
292
+ status: "later",
293
+ });
294
+ }
295
+ else if (route.status === "draft_incomplete") {
296
+ steps.push({
297
+ id: "candidate_inspect",
298
+ label: "Inspect incomplete unfinished-work draft",
299
+ command: route.inspectCommand,
300
+ purpose: "The unfinished-work draft exists but is missing required workflow, controller, cascade, or evidence signals.",
301
+ writes: false,
302
+ status: "current",
303
+ }, {
304
+ id: "candidate_governance",
305
+ label: "Review candidate cascade and governance",
306
+ command: route.governanceCommand,
307
+ purpose: "Identify the missing governance signals before diff, adoption, rollout, or sync.",
308
+ writes: false,
309
+ status: "later",
310
+ }, {
311
+ id: "candidate_diff",
312
+ label: "Compare source and saved draft",
313
+ command: route.diffCommand,
314
+ purpose: "Review what changed after the unfinished-work draft becomes complete enough to compare.",
315
+ writes: false,
316
+ status: "later",
317
+ }, {
318
+ id: "manager_rationale",
319
+ label: "Record manager rationale",
320
+ command: `record explicit manager rationale for ${route.diffCommand}`,
321
+ purpose: "Capture why the diff is acceptable before adoption, organization binding, rollout, or sync.",
322
+ writes: false,
323
+ status: "later",
324
+ }, {
325
+ id: "adoption_review",
326
+ label: "Review adoption readiness",
327
+ command: route.adoptionCommand,
328
+ purpose: "Check workflow shape, cascade, evidence, configuration, and governance before adoption.",
329
+ writes: false,
330
+ status: "later",
331
+ });
332
+ }
333
+ else if (route.status === "draft_structural_drift") {
334
+ steps.push({
335
+ id: "candidate_action",
336
+ label: "Repair preserving unfinished-work draft",
337
+ command: route.currentCommand,
338
+ purpose: "The unfinished-work draft exists but drops source workflows; regenerate it from the source asset so review compares a preserving patch, not a feature removal.",
339
+ writes: true,
340
+ status: "current",
341
+ }, {
342
+ id: "candidate_inspect",
343
+ label: "Inspect repaired unfinished-work draft",
344
+ command: route.inspectCommand,
345
+ purpose: "Read the repaired unfinished-work draft and linked evidence before comparing it with the unchanged source asset.",
346
+ writes: false,
347
+ status: "later",
348
+ }, {
349
+ id: "candidate_governance",
350
+ label: "Review candidate cascade and governance",
351
+ command: route.governanceCommand,
352
+ purpose: "Check cascade inheritance, controllers, evidence posture, and evolution posture before adoption.",
353
+ writes: false,
354
+ status: "later",
355
+ }, {
356
+ id: "candidate_diff",
357
+ label: "Compare source and saved draft",
358
+ command: route.diffCommand,
359
+ purpose: "Verify the repaired draft preserves source workflows and only changes the intended unfinished-work checkpoint.",
360
+ writes: false,
361
+ status: "later",
362
+ }, {
363
+ id: "manager_rationale",
364
+ label: "Record manager rationale",
365
+ command: `record explicit manager rationale for ${route.diffCommand}`,
366
+ purpose: "Capture why the repaired diff is acceptable before adoption, organization binding, rollout, or sync.",
367
+ writes: false,
368
+ status: "later",
369
+ }, {
370
+ id: "adoption_review",
371
+ label: "Review adoption readiness",
372
+ command: route.adoptionCommand,
373
+ purpose: "Check workflow shape, cascade, evidence, configuration, and governance before adoption.",
374
+ writes: false,
375
+ status: "later",
376
+ });
377
+ }
378
+ else {
379
+ steps.push({
380
+ id: "candidate_inspect",
381
+ label: "Inspect unfinished-work draft",
382
+ command: route.inspectCommand,
383
+ purpose: "Read the saved unfinished-work draft and linked evidence when deeper review is needed; the source asset remains available.",
384
+ writes: false,
385
+ status: "ready",
386
+ }, {
387
+ id: "candidate_governance",
388
+ label: "Review candidate cascade and governance",
389
+ command: route.governanceCommand,
390
+ purpose: "Check cascade inheritance, controllers, evidence posture, and evolution posture before adoption.",
391
+ writes: false,
392
+ status: "ready",
393
+ }, {
394
+ id: "candidate_diff",
395
+ label: "Compare source and saved draft",
396
+ command: route.diffCommand,
397
+ purpose: "Review what changed before adoption or organization binding; this is a review gate, not an automatic replacement.",
398
+ writes: false,
399
+ status: "current",
400
+ }, {
401
+ id: "manager_rationale",
402
+ label: "Record manager rationale",
403
+ command: `record explicit manager rationale for ${route.diffCommand}`,
404
+ purpose: "Capture why the diff is acceptable before adoption, organization binding, rollout, or sync.",
405
+ writes: false,
406
+ status: "later",
407
+ }, {
408
+ id: "adoption_review",
409
+ label: "Review adoption readiness",
410
+ command: route.adoptionCommand,
411
+ purpose: "Check workflow shape, cascade, evidence, configuration, and governance before adoption.",
412
+ writes: false,
413
+ status: "later",
414
+ });
415
+ }
416
+ steps.push({
417
+ id: "evolution_preview",
418
+ label: "Preview self-evolution markers",
419
+ command: `dna evolve ${templateIdForAsset(route.candidateAssetName)}`,
420
+ purpose: "Review outcome-derived marker changes without persisting them; mutation stays behind explicit dna sync --evolve after human review.",
421
+ writes: false,
422
+ status: "later",
423
+ }, {
424
+ id: "organization_bind",
425
+ label: "Bind accepted asset to organization",
426
+ command: route.bindCommand,
427
+ purpose: "Record the accepted unfinished-work draft in the organization route before runtime distribution; binding is explicit and does not erase the source asset.",
428
+ writes: true,
429
+ status: "later",
430
+ }, {
431
+ id: "rollout_review",
432
+ label: "Review rollout readiness",
433
+ command: route.rolloutCommand,
434
+ purpose: "Check target-level ready, partial, and blocked distribution before runtime sync.",
435
+ writes: false,
436
+ status: "later",
437
+ });
438
+ return steps;
439
+ }
440
+ function capturedRunFactsActionPath(params) {
441
+ return [
442
+ {
443
+ id: "source_lineage",
444
+ label: "Inspect source asset lineage",
445
+ command: `dna assets lineage ${params.sourceAsset}`,
446
+ purpose: "Confirm which workflow asset the local captured facts matched before acting on them.",
447
+ writes: false,
448
+ status: "ready",
449
+ },
450
+ {
451
+ id: "source_evidence",
452
+ label: "Inspect captured local evidence",
453
+ command: params.evidenceCommand,
454
+ purpose: "Review the C5 JSONL facts, redacted summaries, evidence refs, and matched workflow scope before drafting any change.",
455
+ writes: false,
456
+ status: "current",
457
+ },
458
+ {
459
+ id: "candidate_action",
460
+ label: "Review workflow suggestions",
461
+ command: params.suggestionsCommand,
462
+ purpose: "Translate captured run facts into human-reviewable suggestions without writing templates, policy, org bindings, or runtime artifacts.",
463
+ writes: false,
464
+ status: "later",
465
+ },
466
+ {
467
+ id: "manager_rationale",
468
+ label: "Record manager rationale",
469
+ command: `record explicit manager rationale for any C5-derived change to ${params.sourceAsset}`,
470
+ purpose: "Capture why a C5-derived workflow change is acceptable before diff, adoption, organization binding, rollout, or sync.",
471
+ writes: false,
472
+ status: "later",
473
+ },
474
+ {
475
+ id: "adoption_review",
476
+ label: "Review adoption readiness",
477
+ command: `dna assets adoption ${params.sourceAsset}`,
478
+ purpose: "Keep adoption separate from evidence review; C5 facts alone do not make the current asset adopted or rollout-ready.",
479
+ writes: false,
480
+ status: "later",
481
+ },
482
+ ];
483
+ }
484
+ function c5CaptureBootstrapActionPath(sourceAsset) {
485
+ return [
486
+ {
487
+ id: "source_lineage",
488
+ label: "Inspect source asset lineage",
489
+ command: `dna assets lineage ${sourceAsset}`,
490
+ purpose: "Confirm which workflow asset should govern the local run before creating new C5 evidence.",
491
+ writes: false,
492
+ status: "ready",
493
+ },
494
+ {
495
+ id: "source_evidence",
496
+ label: "Inspect current local evidence",
497
+ command: `dna assets evidence ${sourceAsset} --json`,
498
+ purpose: "Confirm the empty C5 capture state before running local setup or sync commands.",
499
+ writes: false,
500
+ status: "ready",
501
+ },
502
+ {
503
+ id: "candidate_action",
504
+ label: "Review empty C5 suggestions",
505
+ command: `dna workflow suggestions ${sourceAsset} --json`,
506
+ purpose: "Read the no-facts guidance without writing templates, policy, organization bindings, runtime artifacts, or cloud reports.",
507
+ writes: false,
508
+ status: "current",
509
+ },
510
+ {
511
+ id: "local_init",
512
+ label: "Initialize local workflow asset",
513
+ command: `dna init --template ${sourceAsset}`,
514
+ purpose: "Create local DNA configuration only when this asset should govern the project.",
515
+ writes: true,
516
+ status: "later",
517
+ },
518
+ {
519
+ id: "sync_dry_run",
520
+ label: "Preview local runtime sync",
521
+ command: "dna sync --codex --dry-run",
522
+ purpose: "Check the harness artifacts that would be installed before writing runtime files.",
523
+ writes: false,
524
+ status: "later",
525
+ },
526
+ {
527
+ id: "sync_apply",
528
+ label: "Install local runtime artifacts",
529
+ command: "dna sync --codex",
530
+ purpose: "Install local Codex artifacts so the harness can run under dna/dna-hook runtime truth.",
531
+ writes: true,
532
+ status: "later",
533
+ },
534
+ {
535
+ id: "rerun_suggestions",
536
+ label: "Review evidence-backed suggestions",
537
+ command: `dna workflow suggestions ${sourceAsset} --json`,
538
+ purpose: "Return to the read-only suggestion loop after the local run records C5 facts.",
539
+ writes: false,
540
+ status: "later",
541
+ },
542
+ {
543
+ id: "rerun_mine",
544
+ label: "Mine review-only candidates",
545
+ command: `dna workflow mine ${sourceAsset} --json`,
546
+ purpose: "Mine candidates from captured facts without saving, adopting, binding, syncing, uploading, or creating runtime authority.",
547
+ writes: false,
548
+ status: "later",
549
+ },
550
+ ];
551
+ }
552
+ function withActionPath(candidate) {
553
+ return {
554
+ ...candidate,
555
+ actionPath: candidateActionPath(candidate),
556
+ };
557
+ }
558
+ function draftAction(params) {
559
+ return {
560
+ kind: "draft",
561
+ goal: params.goal,
562
+ example: params.example,
563
+ strictness: "high",
564
+ saveTemplate: params.saveTemplate,
565
+ command: params.command ?? `dna workflow draft "${params.goal}" --example ${params.example} --strictness high --save-template ${params.saveTemplate}`,
566
+ scaffold: true,
567
+ deprecatedPrimaryCreationPath: "dna workflow authoring-packet",
568
+ };
569
+ }
570
+ function editPlanAction(params) {
571
+ return {
572
+ kind: "edit_plan",
573
+ instruction: params.instruction,
574
+ fromTemplate: params.fromTemplate,
575
+ workflow: params.workflow,
576
+ operation: params.operation,
577
+ targetStep: params.targetStep,
578
+ saveTemplate: params.saveTemplate,
579
+ command: params.command ?? `dna workflow edit-plan "${params.instruction}" --from-template ${params.fromTemplate} --workflow ${params.workflow} --operation ${params.operation} --target-step ${params.targetStep} --save-template ${params.saveTemplate}`,
580
+ };
581
+ }
582
+ function buildCandidateReviewPacket(params) {
583
+ const active = params.candidates.find((candidate) => candidate.type === "active_evidence_followup");
584
+ const first = active ?? params.candidates[0];
585
+ if (!first) {
586
+ return {
587
+ currentState: params.focusAsset ? `no_candidates:${params.focusAsset}` : "no_candidates:all_assets",
588
+ primaryAction: "dna assets library",
589
+ decision: "no_candidates",
590
+ reason: "No dogfood-backed workflow candidates are currently available. Start from the asset library or describe a new workflow intake.",
591
+ allowedNow: [
592
+ "Inspect available workflow assets: dna assets library.",
593
+ "Describe a new working method: dna workflow intake \"Describe the workflow you want to govern\".",
594
+ ],
595
+ blockedNow: [
596
+ "Runtime sync is blocked until a reviewed asset exists, is organization-bound, configured, and rollout-ready.",
597
+ "Evolution marker mutation is blocked until there is reviewed evidence and an accepted change path.",
598
+ ],
599
+ reviewPath: params.nextCommands,
600
+ };
601
+ }
602
+ const activeEvidence = params.summary.activeModules > 0;
603
+ const capturedRunFacts = first.type === "captured_run_fact_review";
604
+ const sourceAsset = active?.sourceAsset ?? first.sourceAsset;
605
+ const activeFollowupNote = activeEvidence
606
+ ? activeFollowupNonReplacementNote(sourceAsset, active?.candidateAssetName ?? first.candidateAssetName)
607
+ : undefined;
608
+ return {
609
+ currentState: activeEvidence
610
+ ? `active_evidence:${sourceAsset}`
611
+ : capturedRunFacts
612
+ ? `captured_run_facts:${sourceAsset}`
613
+ : `candidate_review:${params.focusAsset ?? "all_assets"}`,
614
+ primaryAction: first.nextCommand,
615
+ decision: activeEvidence ? "review_active_evidence_first" : "review_candidates",
616
+ reason: activeEvidence
617
+ ? `Active dogfood evidence must be saved and reviewed as unfinished work, or routed to next-run focus, before baseline promotion, evolution marker mutation, rollout, or runtime sync. ${activeFollowupNote}`
618
+ : capturedRunFacts
619
+ ? "Captured local C5 run facts must be reviewed before changing the workflow asset. They can create suggestions, but never automatic policy, template, adoption, rollout, sync, or runtime authority."
620
+ : "Dogfood evidence is ready to be reviewed as reusable workflow assets or baseline promotion candidates before organization rollout.",
621
+ allowedNow: [
622
+ `Inspect source lineage: dna assets lineage ${sourceAsset}.`,
623
+ capturedRunFacts
624
+ ? `Inspect captured C5 evidence: dna assets evidence ${sourceAsset} --json.`
625
+ : `Inspect dogfood evidence: dna assets evidence ${sourceAsset}.`,
626
+ `Run the primary candidate action: ${first.nextCommand}.`,
627
+ "Review extracted reusable assets through adoption before org binding.",
628
+ ],
629
+ blockedNow: [
630
+ activeEvidence
631
+ ? "Baseline promotion is blocked until active dogfood evidence is reviewed through the saved unfinished-work draft; the source asset stays intact while this happens."
632
+ : capturedRunFacts
633
+ ? "C5 evidence review cannot directly promote, adopt, bind, sync, or mutate the workflow asset."
634
+ : "Baseline promotion still requires adoption review before org binding.",
635
+ "Runtime sync is blocked until the selected asset is reviewed, adopted, organization-bound, configured if needed, and rollout-ready.",
636
+ "Evolution marker mutation is blocked until evidence-backed suggestions are reviewed and accepted.",
637
+ "Vercel deployment is not a substitute for workflow candidate review, organization rollout, or runtime sync.",
638
+ ],
639
+ reviewPath: first.reviewCommands.length > 0 ? first.reviewCommands : params.nextCommands,
640
+ };
641
+ }
642
+ function fromCandidateCommand(params) {
643
+ return `dna workflow from-candidate ${params.id} --from-template ${params.sourceAsset} --save-template ${params.saveTemplate}`;
644
+ }
645
+ function candidateBacklogNextCommands(candidates) {
646
+ const active = candidates.find((candidate) => candidate.type === "active_evidence_followup");
647
+ const remaining = active ? candidates.filter((candidate) => candidate.id !== active.id) : candidates;
648
+ const commands = [
649
+ ...new Set([
650
+ ...(active ? [active.nextCommand, ...active.reviewCommands] : []),
651
+ ...remaining.map((candidate) => candidate.nextCommand),
652
+ ...remaining.flatMap((candidate) => candidate.reviewCommands),
653
+ ]),
654
+ ];
655
+ const limited = commands.slice(0, 12);
656
+ if (commands.includes("dna org rollout") && !limited.includes("dna org rollout")) {
657
+ limited[limited.length - 1] = "dna org rollout";
658
+ }
659
+ return limited;
660
+ }
661
+ function blockedScopeTriageModules(index) {
662
+ return index.modules.filter((module) => {
663
+ const signal = `${module.verdict} ${module.review ?? ""} ${module.failureReason ?? ""} ${module.nextRoundFocus ?? ""}`.toLowerCase();
664
+ return module.blockedCount > 0 ||
665
+ module.verdict === "BLOCKED" ||
666
+ module.verdict === "REQUEST_CHANGES" ||
667
+ signal.includes("blocked_by_review") ||
668
+ signal.includes("needs_human") ||
669
+ signal.includes("outside_diagnosis") ||
670
+ signal.includes("human approval");
671
+ });
672
+ }
673
+ function crossBoundaryResourceModules(index) {
674
+ return index.modules.filter((module) => {
675
+ const signal = `${module.module} ${module.verdict} ${module.review ?? ""} ${module.failureReason ?? ""} ${module.nextRoundFocus ?? ""}`.toLowerCase();
676
+ return signal.includes("i18n") ||
677
+ signal.includes("localization") ||
678
+ signal.includes("translation") ||
679
+ signal.includes("shared") ||
680
+ signal.includes("cross-boundary") ||
681
+ signal.includes("cross boundary") ||
682
+ signal.includes("outside_diagnosis") ||
683
+ signal.includes("outside diagnosis") ||
684
+ signal.includes("outside_scope") ||
685
+ signal.includes("outside scope");
686
+ });
687
+ }
688
+ export function activeEvidenceFollowupRoute(asset) {
689
+ const candidateId = `active-followup-${asset}`;
690
+ const assetName = `${asset}-active-followup`;
691
+ return {
692
+ candidateId,
693
+ assetName,
694
+ command: fromCandidateCommand({ id: candidateId, sourceAsset: asset, saveTemplate: assetName }),
695
+ };
696
+ }
697
+ function isReviewableFollowupAsset(asset) {
698
+ return asset.status.hasWorkflow &&
699
+ asset.status.hasController &&
700
+ asset.status.hasCascade &&
701
+ asset.status.hasEvidence;
702
+ }
703
+ function missingSourceWorkflowIds(sourceAsset, candidateAsset) {
704
+ if (!sourceAsset)
705
+ return [];
706
+ const candidateWorkflowIds = new Set(candidateAsset.workflows.map((workflow) => workflow.id));
707
+ return sourceAsset.workflows
708
+ .map((workflow) => workflow.id)
709
+ .filter((workflowId) => !candidateWorkflowIds.has(workflowId));
710
+ }
711
+ function withCurrentFollowupEvidence(asset, sourceAsset, evidenceIndex) {
712
+ if (!asset || !evidenceIndex)
713
+ return asset;
714
+ const active = activeEvidenceModules(evidenceIndex);
715
+ if (active.length === 0)
716
+ return asset;
717
+ const description = `Saved unfinished-work draft from active ${sourceAsset} evidence: ${active.length} active module(s): ${active.slice(0, 5).map(compactEvidenceModule).join("; ")}`;
718
+ return {
719
+ ...asset,
720
+ evidence: asset.evidence.map((link) => link.label === `Active dogfood evidence: ${sourceAsset}`
721
+ ? { ...link, description }
722
+ : link),
723
+ };
724
+ }
725
+ export function activeEvidenceFollowupReviewRoute(sourceAsset, assets, evidenceIndex) {
726
+ const route = activeEvidenceFollowupRoute(sourceAsset);
727
+ const sourceAssetSummary = assets.find((asset) => asset.name === sourceAsset);
728
+ const candidateAsset = withCurrentFollowupEvidence(assets.find((asset) => asset.name === route.assetName), sourceAsset, evidenceIndex);
729
+ const active = evidenceIndex ? activeEvidenceModules(evidenceIndex) : [];
730
+ const currentEvidence = active.length > 0
731
+ ? `${active.length} active module(s): ${active.slice(0, 5).map(compactEvidenceModule).join("; ")}`
732
+ : undefined;
733
+ const inspectCommand = `dna assets inspect ${route.assetName} --evidence`;
734
+ const governanceCommand = `dna assets governance ${route.assetName}`;
735
+ const diffCommand = `dna assets diff ${sourceAsset} ${route.assetName}`;
736
+ const rationaleCommand = `record explicit manager rationale for ${diffCommand}`;
737
+ const adoptionCommand = `dna assets adoption ${route.assetName}`;
738
+ const bindCommand = `dna org bind --asset ${route.assetName} --status active --sync-target codex`;
739
+ const rolloutCommand = "dna org rollout";
740
+ const sourceCommands = [
741
+ `dna assets lineage ${sourceAsset}`,
742
+ `dna assets evidence ${sourceAsset}`,
743
+ ];
744
+ const candidateReviewCommands = [
745
+ inspectCommand,
746
+ governanceCommand,
747
+ diffCommand,
748
+ rationaleCommand,
749
+ adoptionCommand,
750
+ bindCommand,
751
+ rolloutCommand,
752
+ ];
753
+ if (!candidateAsset) {
754
+ return {
755
+ candidateId: route.candidateId,
756
+ sourceAsset,
757
+ candidateAssetName: route.assetName,
758
+ status: "draft_missing",
759
+ currentEvidence,
760
+ createCommand: route.command,
761
+ inspectCommand,
762
+ governanceCommand,
763
+ diffCommand,
764
+ adoptionCommand,
765
+ bindCommand,
766
+ rolloutCommand,
767
+ currentCommand: route.command,
768
+ currentWrites: true,
769
+ reason: `No unfinished-work draft exists yet, so open dogfood evidence must first be saved for review. ${activeFollowupNonReplacementNote(sourceAsset, route.assetName)}`,
770
+ reviewCommands: [
771
+ ...sourceCommands,
772
+ route.command,
773
+ ...candidateReviewCommands,
774
+ ],
775
+ };
776
+ }
777
+ const missingWorkflows = missingSourceWorkflowIds(sourceAssetSummary, candidateAsset);
778
+ if (missingWorkflows.length > 0) {
779
+ const repairCommand = `${route.command} --force`;
780
+ return {
781
+ candidateId: route.candidateId,
782
+ sourceAsset,
783
+ candidateAssetName: route.assetName,
784
+ status: "draft_structural_drift",
785
+ candidateAsset,
786
+ currentEvidence,
787
+ createCommand: route.command,
788
+ inspectCommand,
789
+ governanceCommand,
790
+ diffCommand,
791
+ adoptionCommand,
792
+ bindCommand,
793
+ rolloutCommand,
794
+ currentCommand: repairCommand,
795
+ currentWrites: true,
796
+ reason: `The unfinished-work draft exists but drops source workflow(s): ${missingWorkflows.join(", ")}; regenerate it as a preserving draft before review. ${activeFollowupNonReplacementNote(sourceAsset, route.assetName)}`,
797
+ reviewCommands: [
798
+ ...sourceCommands,
799
+ repairCommand,
800
+ ...candidateReviewCommands,
801
+ ],
802
+ };
803
+ }
804
+ if (!isReviewableFollowupAsset(candidateAsset)) {
805
+ return {
806
+ candidateId: route.candidateId,
807
+ sourceAsset,
808
+ candidateAssetName: route.assetName,
809
+ status: "draft_incomplete",
810
+ candidateAsset,
811
+ currentEvidence,
812
+ createCommand: route.command,
813
+ inspectCommand,
814
+ governanceCommand,
815
+ diffCommand,
816
+ adoptionCommand,
817
+ bindCommand,
818
+ rolloutCommand,
819
+ currentCommand: inspectCommand,
820
+ currentWrites: false,
821
+ reason: `The unfinished-work draft exists, but it is missing one or more workflow, controller, cascade, or evidence signals; inspect it before adoption. ${activeFollowupNonReplacementNote(sourceAsset, route.assetName)}`,
822
+ reviewCommands: [
823
+ inspectCommand,
824
+ governanceCommand,
825
+ diffCommand,
826
+ rationaleCommand,
827
+ adoptionCommand,
828
+ bindCommand,
829
+ rolloutCommand,
830
+ ...sourceCommands,
831
+ ],
832
+ };
833
+ }
834
+ return {
835
+ candidateId: route.candidateId,
836
+ sourceAsset,
837
+ candidateAssetName: route.assetName,
838
+ status: "draft_reviewable",
839
+ candidateAsset,
840
+ currentEvidence,
841
+ createCommand: route.command,
842
+ inspectCommand,
843
+ governanceCommand,
844
+ diffCommand,
845
+ adoptionCommand,
846
+ bindCommand,
847
+ rolloutCommand,
848
+ currentCommand: diffCommand,
849
+ currentWrites: false,
850
+ reason: `The unfinished-work draft already exists with workflow, controller, cascade, and evidence signals; review its diff before adoption and organization binding. ${activeFollowupNonReplacementNote(sourceAsset, route.assetName)}`,
851
+ reviewCommands: [
852
+ diffCommand,
853
+ rationaleCommand,
854
+ adoptionCommand,
855
+ bindCommand,
856
+ rolloutCommand,
857
+ inspectCommand,
858
+ governanceCommand,
859
+ ...sourceCommands,
860
+ ],
861
+ };
862
+ }
863
+ function buildCandidatesForAsset(asset, index, assets) {
864
+ const c5Gaps = c5GapCaptures(index);
865
+ if (index.totals.modules === 0 && c5Gaps.length === 0)
866
+ return [];
867
+ const candidates = [];
868
+ const active = activeEvidenceModules(index);
869
+ const modules = sourceModules(index);
870
+ const fixed = totalFixed(index);
871
+ const evidenceOpen = active.length > 0;
872
+ const passEvidence = `${index.totals.pass}/${index.totals.modules} PASS, active=${active.length}, fixed=${fixed}, remaining=${index.totals.remaining}`;
873
+ const blockedScopeModules = blockedScopeTriageModules(index);
874
+ const crossBoundaryModules = crossBoundaryResourceModules(index);
875
+ if (active.length > 0) {
876
+ const route = activeEvidenceFollowupReviewRoute(asset.name, assets, index);
877
+ const action = route.currentWrites
878
+ ? editPlanAction({
879
+ instruction: "Require active dogfood evidence follow-up before baseline promotion, organization binding, rollout, or runtime sync. If progress JSON still has BLOCKED, REQUEST_CHANGES, remaining work, outside-diagnosis scope, or needs-human review, route through suggestions and blocked-scope triage before completion claims.",
880
+ fromTemplate: asset.name,
881
+ workflow: "fix",
882
+ operation: "tighten_boundary",
883
+ targetStep: "verify_report",
884
+ saveTemplate: route.candidateAssetName,
885
+ command: route.currentCommand,
886
+ })
887
+ : reviewAction(route.currentCommand, route.reason);
888
+ candidates.push({
889
+ id: route.candidateId,
890
+ type: "active_evidence_followup",
891
+ priority: "high",
892
+ title: "Review unfinished dogfood work as a saved draft, not a template replacement",
893
+ candidateAssetName: route.candidateAssetName,
894
+ sourceAsset: asset.name,
895
+ evidence: `${active.length} active module(s): ${active.slice(0, 5).map(compactEvidenceModule).join("; ")}`,
896
+ rationale: `Open evidence should first be saved as a reviewed unfinished-work draft or routed to next-run focus. ${activeFollowupNonReplacementNote(asset.name, route.candidateAssetName)} Extracting reusable workflows before closing active work would weaken the evidence chain.`,
897
+ sourceModules: active.map((module) => module.module),
898
+ sourceWorkflows: asset.workflows.map((workflow) => workflow.id),
899
+ nextCommand: route.currentCommand,
900
+ reviewCommands: route.reviewCommands,
901
+ actionPath: activeFollowupActionPath(route),
902
+ mutationBoundary: `Candidate backlog is read-only; active evidence can produce an explicit reviewable unfinished-work draft through dna workflow from-candidate, not automatic template or policy mutation. ${activeFollowupNonReplacementNote(asset.name, route.candidateAssetName)}`,
903
+ plainLanguage: activeFollowupPlainLanguage(asset.name, route.candidateAssetName),
904
+ continuityBoundary: activeFollowupContinuityBoundary(asset.name, route.candidateAssetName),
905
+ action,
906
+ });
907
+ }
908
+ if (c5Gaps.length > 0) {
909
+ const evidenceCommand = `dna assets evidence ${asset.name} --json`;
910
+ const suggestionsCommand = `dna workflow suggestions ${asset.name} --json`;
911
+ candidates.push({
912
+ id: `review-captured-run-facts-${asset.name}`,
913
+ type: "captured_run_fact_review",
914
+ priority: active.length > 0 ? "medium" : "high",
915
+ title: "Review captured local run facts before changing the workflow asset",
916
+ candidateAssetName: asset.name,
917
+ sourceAsset: asset.name,
918
+ evidence: c5Gaps.slice(0, 5).map(compactEvidenceCapture).join("; "),
919
+ rationale: "C5 hook evidence shows blocked, failed, retry, unsupported, or human-intervention facts. Mine should surface those facts for review, but the evidence must not mutate policy, templates, adoption, rollout, sync, or runtime authority automatically.",
920
+ sourceModules: [],
921
+ sourceWorkflows: sourceWorkflows(asset, c5Gaps.map((capture) => capture.workflowId)),
922
+ nextCommand: evidenceCommand,
923
+ reviewCommands: [
924
+ `dna assets lineage ${asset.name}`,
925
+ evidenceCommand,
926
+ suggestionsCommand,
927
+ `record explicit manager rationale for any C5-derived change to ${asset.name}`,
928
+ ],
929
+ actionPath: capturedRunFactsActionPath({
930
+ sourceAsset: asset.name,
931
+ evidenceCommand,
932
+ suggestionsCommand,
933
+ }),
934
+ mutationBoundary: "Candidate backlog is read-only; captured C5 facts create a review queue and suggestions only, not automatic template edits, policy mutation, adoption, organization binding, rollout, runtime sync, cloud upload, or runtime authority.",
935
+ plainLanguage: capturedRunFactPlainLanguage(asset.name),
936
+ action: reviewAction(evidenceCommand, "Review captured local facts before drafting any workflow change."),
937
+ });
938
+ }
939
+ if (index.totals.modules === 0)
940
+ return candidates;
941
+ const baselineNextCommand = evidenceOpen ? `dna assets suggestions ${asset.name}` : `dna assets adoption ${asset.name}`;
942
+ candidates.push(withActionPath({
943
+ id: `stabilize-${asset.name}`,
944
+ type: "stabilize_current_asset",
945
+ priority: evidenceOpen ? "medium" : "high",
946
+ title: evidenceOpen
947
+ ? "Prepare the current dogfood workflow for baseline promotion after active evidence closes"
948
+ : "Promote the proven dogfood workflow as the current governed baseline",
949
+ candidateAssetName: asset.name,
950
+ sourceAsset: asset.name,
951
+ evidence: passEvidence,
952
+ rationale: evidenceOpen
953
+ ? "Most dogfood modules are already green, but active evidence still blocks direct promotion. Keep the stable baseline candidate visible while routing current work through suggestions first."
954
+ : "The dogfood loop reached a stable green state. The first valuable asset is the proven workflow itself, adopted through review before organization rollout.",
955
+ sourceModules: modules,
956
+ sourceWorkflows: asset.workflows.map((workflow) => workflow.id),
957
+ nextCommand: baselineNextCommand,
958
+ reviewCommands: [
959
+ `dna assets lineage ${asset.name}`,
960
+ `dna assets lifecycle ${asset.name}`,
961
+ ...(evidenceOpen ? [`dna assets suggestions ${asset.name}`] : []),
962
+ `dna assets adoption ${asset.name}`,
963
+ `dna org bind --asset ${asset.name} --status active --sync-target codex`,
964
+ "dna org rollout",
965
+ ],
966
+ mutationBoundary: "Adoption review is read-only; organization binding and runtime sync remain explicit later commands.",
967
+ action: reviewAction(baselineNextCommand, evidenceOpen
968
+ ? "Active evidence blocks baseline adoption until suggestions are reviewed."
969
+ : "Baseline promotion is an adoption review, not a workflow draft."),
970
+ }));
971
+ const behaviorWorkflows = sourceWorkflows(asset, ["behavior-lock"]);
972
+ if (behaviorWorkflows.length > 0) {
973
+ const candidateAssetName = asset.name.startsWith("flutter-") ? "flutter-behavior-lock" : `${asset.name}-behavior-lock`;
974
+ const nextCommand = `dna assets adoption ${candidateAssetName}`;
975
+ candidates.push(withActionPath({
976
+ id: `extract-behavior-lock-${asset.name}`,
977
+ type: "extract_workflow",
978
+ priority: "high",
979
+ title: "Extract behavior locking as a reusable workflow asset",
980
+ candidateAssetName,
981
+ sourceAsset: asset.name,
982
+ evidence: passEvidence,
983
+ rationale: "Dogfood repeatedly depended on locking source behavior before repair. This deserves a smaller reusable workflow users can choose before a full rewrite.",
984
+ sourceModules: modules,
985
+ sourceWorkflows: behaviorWorkflows,
986
+ nextCommand,
987
+ reviewCommands: candidateReviewCommands({ sourceAsset: asset.name, candidateAssetName, nextCommand }),
988
+ mutationBoundary: "Extraction is a review path only; existing or drafted assets still need diff/adoption review before org binding.",
989
+ action: reviewAction(nextCommand, "This candidate already maps to an existing asset; review adoption instead of creating a duplicate draft."),
990
+ }));
991
+ }
992
+ const rescueWorkflows = sourceWorkflows(asset, ["behavior-lock", "diagnosis", "fix"]);
993
+ if (asset.name.startsWith("flutter-") && rescueWorkflows.length >= 2) {
994
+ const candidateAssetName = "flutter-refactoring-rescue";
995
+ const id = `extract-refactoring-rescue-${asset.name}`;
996
+ const nextCommand = `dna assets adoption ${candidateAssetName}`;
997
+ candidates.push(withActionPath({
998
+ id,
999
+ type: "extract_workflow",
1000
+ priority: "high",
1001
+ title: "Review the Flutter refactoring rescue workflow for migrated-but-broken projects",
1002
+ candidateAssetName,
1003
+ sourceAsset: asset.name,
1004
+ evidence: `${passEvidence}; dogfood route used behavior-lock, diagnosis, serial fix rounds, and rescue_small_steps for migrated Flutter chains.`,
1005
+ rationale: "Dogfood showed a different user entrypoint from a full rewrite: the project is migrated-but-broken, with framework migration already present but feature chains still failing. That route needs a small, comfortable rescue asset where users fix one chain at a time, treat v1 as the oracle, and verify each change before moving on.",
1006
+ sourceModules: modules,
1007
+ sourceWorkflows: rescueWorkflows,
1008
+ nextCommand,
1009
+ reviewCommands: [
1010
+ `dna assets lineage ${asset.name}`,
1011
+ `dna assets evidence ${asset.name}`,
1012
+ `dna assets lineage ${candidateAssetName}`,
1013
+ `dna assets inspect ${candidateAssetName} --evidence`,
1014
+ `dna assets governance ${candidateAssetName}`,
1015
+ nextCommand,
1016
+ `dna org bind --asset ${candidateAssetName} --status active --sync-target codex`,
1017
+ "dna org rollout",
1018
+ ],
1019
+ mutationBoundary: "Refactoring rescue already exists as a built-in asset; this path is review/adoption only and keeps the dogfood rescue route separate from generic rewrite promotion.",
1020
+ action: reviewAction(nextCommand, "Refactoring rescue has already been extracted; review/adopt the asset instead of generating a duplicate draft."),
1021
+ }));
1022
+ }
1023
+ if (crossBoundaryModules.length > 0) {
1024
+ const candidateAssetName = "workflow-cross-boundary-resource-approval";
1025
+ const nextCommand = `dna assets adoption ${candidateAssetName}`;
1026
+ candidates.push(withActionPath({
1027
+ id: `extract-cross-boundary-resource-approval-${asset.name}`,
1028
+ type: "evidence_infrastructure",
1029
+ priority: "high",
1030
+ title: "Review cross-boundary resource approval for shared or localization work",
1031
+ candidateAssetName,
1032
+ sourceAsset: asset.name,
1033
+ evidence: crossBoundaryModules.slice(0, 5).map((module) => `${compactEvidenceModule(module)}${module.failureReason ? ` failure=${module.failureReason}` : ""}`).join("; "),
1034
+ rationale: "Dogfood evidence includes work that crosses the current module, diagnosis, localization, or shared-resource boundary. That should become an explicit approval route before implementation, baseline promotion, rollout, runtime sync, or completion claims.",
1035
+ sourceModules: crossBoundaryModules.map((module) => module.module),
1036
+ sourceWorkflows: asset.workflows.map((workflow) => workflow.id),
1037
+ nextCommand,
1038
+ reviewCommands: [
1039
+ `dna assets lineage ${asset.name}`,
1040
+ `dna assets evidence ${asset.name}`,
1041
+ `dna assets lineage ${candidateAssetName}`,
1042
+ `dna assets inspect ${candidateAssetName} --evidence`,
1043
+ `dna assets governance ${candidateAssetName}`,
1044
+ nextCommand,
1045
+ `dna org bind --asset ${candidateAssetName} --status active --sync-target codex`,
1046
+ "dna org rollout",
1047
+ ],
1048
+ mutationBoundary: "Cross-boundary resource approval already exists as a built-in asset; this path is review/adoption only and does not authorize shared, localization, config, policy, or outside-diagnosis changes without explicit approval.",
1049
+ action: reviewAction(nextCommand, "Cross-boundary resource approval has already been extracted; review/adopt the asset instead of generating a duplicate draft."),
1050
+ }));
1051
+ }
1052
+ if (blockedScopeModules.length > 0) {
1053
+ const candidateAssetName = "workflow-blocked-scope-triage";
1054
+ const nextCommand = `dna assets adoption ${candidateAssetName}`;
1055
+ candidates.push(withActionPath({
1056
+ id: `extract-blocked-scope-triage-${asset.name}`,
1057
+ type: "evidence_infrastructure",
1058
+ priority: "high",
1059
+ title: "Review blocked-scope triage for needs-human or outside-diagnosis evidence",
1060
+ candidateAssetName,
1061
+ sourceAsset: asset.name,
1062
+ evidence: blockedScopeModules.slice(0, 5).map((module) => `${compactEvidenceModule(module)}${module.failureReason ? ` failure=${module.failureReason}` : ""}`).join("; "),
1063
+ rationale: "Dogfood evidence includes blocked, review-gated, needs-human, or outside-diagnosis work. That should become an explicit manager-readable triage route before baseline promotion, adoption, rollout, runtime sync, or completion claims.",
1064
+ sourceModules: blockedScopeModules.map((module) => module.module),
1065
+ sourceWorkflows: asset.workflows.map((workflow) => workflow.id),
1066
+ nextCommand,
1067
+ reviewCommands: [
1068
+ `dna assets lineage ${asset.name}`,
1069
+ `dna assets evidence ${asset.name}`,
1070
+ `dna assets lineage ${candidateAssetName}`,
1071
+ `dna assets inspect ${candidateAssetName} --evidence`,
1072
+ `dna assets governance ${candidateAssetName}`,
1073
+ nextCommand,
1074
+ `dna org bind --asset ${candidateAssetName} --status active --sync-target codex`,
1075
+ "dna org rollout",
1076
+ ],
1077
+ mutationBoundary: "Blocked-scope triage already exists as a built-in asset; this path is review/adoption only and does not auto-approve human, outside-diagnosis, deferred, skipped, or blocked work.",
1078
+ action: reviewAction(nextCommand, "Blocked-scope triage has already been extracted; review/adopt the asset instead of generating a duplicate draft."),
1079
+ }));
1080
+ }
1081
+ const diagnosisWorkflows = sourceWorkflows(asset, ["diagnosis"]);
1082
+ if (diagnosisWorkflows.length > 0) {
1083
+ const candidateAssetName = `${asset.name}-diagnosis-review`;
1084
+ const id = `extract-diagnosis-review-${asset.name}`;
1085
+ const nextCommand = `dna assets adoption ${candidateAssetName}`;
1086
+ candidates.push(withActionPath({
1087
+ id,
1088
+ type: "extract_workflow",
1089
+ priority: "medium",
1090
+ title: "Review the reusable diagnosis plus reviewer gate for module-level rewrites",
1091
+ candidateAssetName,
1092
+ sourceAsset: asset.name,
1093
+ evidence: passEvidence,
1094
+ rationale: "The dogfood workflow separated diagnosis from implementation and review. That pattern has now been extracted as a reusable built-in workflow asset for risky rewrites where the model must classify before editing.",
1095
+ sourceModules: modules,
1096
+ sourceWorkflows: diagnosisWorkflows,
1097
+ nextCommand,
1098
+ reviewCommands: [
1099
+ `dna assets lineage ${asset.name}`,
1100
+ `dna assets evidence ${asset.name}`,
1101
+ `dna assets lineage ${candidateAssetName}`,
1102
+ `dna assets inspect ${candidateAssetName} --evidence`,
1103
+ `dna assets governance ${candidateAssetName}`,
1104
+ nextCommand,
1105
+ `dna org bind --asset ${candidateAssetName} --status active --sync-target codex`,
1106
+ "dna org rollout",
1107
+ ],
1108
+ mutationBoundary: "Diagnosis review workflow has already been extracted into a built-in asset; this path is review/adoption only and does not generate a duplicate draft.",
1109
+ action: reviewAction(nextCommand, "Diagnosis review has already been extracted; review/adopt the asset instead of generating a duplicate draft."),
1110
+ }));
1111
+ }
1112
+ const fixWorkflows = sourceWorkflows(asset, ["fix"]);
1113
+ if (fixWorkflows.length > 0) {
1114
+ const candidateAssetName = `${asset.name}-fix-review-loop`;
1115
+ const id = `extract-fix-review-loop-${asset.name}`;
1116
+ const nextCommand = `dna assets adoption ${candidateAssetName}`;
1117
+ candidates.push(withActionPath({
1118
+ id,
1119
+ type: "extract_workflow",
1120
+ priority: "medium",
1121
+ title: "Review the reusable fix-review-verify loop as a smaller workflow asset",
1122
+ candidateAssetName,
1123
+ sourceAsset: asset.name,
1124
+ evidence: passEvidence,
1125
+ rationale: "The full rewrite asset contains a repeatable inner loop: implement from approved diagnosis, review against v1 behavior, then verify with evidence. That loop has now been extracted as a reusable built-in workflow asset for future refactors outside the full migration path.",
1126
+ sourceModules: modules,
1127
+ sourceWorkflows: fixWorkflows,
1128
+ nextCommand,
1129
+ reviewCommands: [
1130
+ `dna assets lineage ${asset.name}`,
1131
+ `dna assets evidence ${asset.name}`,
1132
+ `dna assets lineage ${candidateAssetName}`,
1133
+ `dna assets inspect ${candidateAssetName} --evidence`,
1134
+ `dna assets governance ${candidateAssetName}`,
1135
+ nextCommand,
1136
+ `dna org bind --asset ${candidateAssetName} --status active --sync-target codex`,
1137
+ "dna org rollout",
1138
+ ],
1139
+ mutationBoundary: "Fix-review loop workflow has already been extracted into a built-in asset; this path is review/adoption only and does not generate a duplicate draft.",
1140
+ action: reviewAction(nextCommand, "Fix-review loop has already been extracted; review/adopt the asset instead of generating a duplicate draft."),
1141
+ }));
1142
+ }
1143
+ const evidenceCandidateName = "workflow-evidence-review";
1144
+ const evidenceCandidateId = `extract-evidence-review-${asset.name}`;
1145
+ const nextCommand = "dna assets adoption workflow-evidence-review";
1146
+ candidates.push(withActionPath({
1147
+ id: evidenceCandidateId,
1148
+ type: "evidence_infrastructure",
1149
+ priority: "medium",
1150
+ title: "Reuse the built-in evidence package review workflow infrastructure",
1151
+ candidateAssetName: evidenceCandidateName,
1152
+ sourceAsset: asset.name,
1153
+ evidence: `${index.totals.modules} module progress JSON file(s), ${index.modules.reduce((sum, module) => sum + module.evidencePaths.length, 0)} linked evidence path(s)`,
1154
+ rationale: "The dogfood proof is not just code changes; it is diagnosis, review verdict, test logs, and progress JSON. That evidence packaging is now the reusable workflow-evidence-review asset, so this candidate should review and adopt it rather than draft a duplicate template.",
1155
+ sourceModules: modules,
1156
+ sourceWorkflows: asset.workflows.map((workflow) => workflow.id),
1157
+ nextCommand,
1158
+ reviewCommands: [
1159
+ `dna assets lineage ${asset.name}`,
1160
+ `dna assets evidence ${asset.name}`,
1161
+ "dna assets lineage workflow-evidence-review",
1162
+ "dna assets inspect workflow-evidence-review --evidence",
1163
+ "dna assets governance workflow-evidence-review",
1164
+ nextCommand,
1165
+ "dna org bind --asset workflow-evidence-review --status active --sync-target codex",
1166
+ "dna org rollout",
1167
+ ],
1168
+ mutationBoundary: "Evidence workflow infrastructure already exists as a built-in asset; this path is review/adoption only and keeps Obsidian/wiki as internal memory, not runtime evidence.",
1169
+ action: reviewAction(nextCommand, "Evidence review has already been extracted into workflow-evidence-review; review/adopt the asset instead of generating a duplicate draft."),
1170
+ }));
1171
+ if (index.totals.captureFacts === 0) {
1172
+ const suggestionsCommand = `dna workflow suggestions ${asset.name} --json`;
1173
+ const bootstrapCommands = c5CaptureBootstrapCommands(asset.name);
1174
+ candidates.push({
1175
+ id: `collect-local-c5-facts-${asset.name}`,
1176
+ type: "captured_run_fact_bootstrap",
1177
+ priority: active.length > 0 ? "low" : "medium",
1178
+ title: "Run the workflow locally before mining C5 improvements",
1179
+ candidateAssetName: asset.name,
1180
+ sourceAsset: asset.name,
1181
+ evidence: "0 local C5 capture facts found under .dna/evidence/capture for this asset.",
1182
+ rationale: "Workflow mining needs local run facts before it can produce evidence-backed improvement candidates from real usage. This candidate keeps the empty state actionable without creating, deleting, replacing, adopting, binding, syncing, uploading, or mutating any workflow asset automatically.",
1183
+ sourceModules: [],
1184
+ sourceWorkflows: asset.workflows.map((workflow) => workflow.id),
1185
+ nextCommand: suggestionsCommand,
1186
+ reviewCommands: [
1187
+ `dna assets lineage ${asset.name}`,
1188
+ `dna assets evidence ${asset.name} --json`,
1189
+ ...bootstrapCommands,
1190
+ ],
1191
+ actionPath: c5CaptureBootstrapActionPath(asset.name),
1192
+ mutationBoundary: "Candidate backlog is read-only. The listed init/sync commands are explicit local follow-up actions; evidence still creates suggestions only and never edits workflow assets, mutates policy, adopts templates, binds organizations, uploads reports, or creates runtime authority.",
1193
+ plainLanguage: c5CaptureBootstrapPlainLanguage(asset.name),
1194
+ action: reviewAction(suggestionsCommand, "Review the empty C5 evidence path before running local init/sync to create facts."),
1195
+ });
1196
+ }
1197
+ return candidates;
1198
+ }
1199
+ export async function buildWorkflowAssetCandidateBacklog(opts) {
1200
+ const assets = await listWorkflowAssets({ projectDir: opts.projectDir });
1201
+ const selected = opts.name
1202
+ ? assets.filter((asset) => asset.name === opts.name || asset.displayName === opts.name || asset.namespace === opts.name)
1203
+ : assets;
1204
+ if (opts.name && selected.length === 0)
1205
+ return undefined;
1206
+ const evidenceIndexes = [];
1207
+ for (const asset of selected) {
1208
+ const index = await buildWorkflowAssetEvidenceIndex(asset.name, { projectDir: opts.projectDir });
1209
+ if (index)
1210
+ evidenceIndexes.push({ asset, index });
1211
+ }
1212
+ const candidates = evidenceIndexes.flatMap(({ asset, index }) => buildCandidatesForAsset(asset, index, assets));
1213
+ const nextCommands = candidates.length > 0
1214
+ ? candidateBacklogNextCommands(candidates)
1215
+ : [
1216
+ "dna assets library",
1217
+ "dna workflow intake \"Describe the workflow you want to govern\"",
1218
+ "dna workflow authoring-packet \"Describe the workflow you want to govern\" --json",
1219
+ "dna workflow draft \"Describe the workflow you want to govern\" --example dev_pipeline --strictness high --save-template <asset-name> # scaffold fallback only",
1220
+ ];
1221
+ const summary = {
1222
+ assetsScanned: selected.length,
1223
+ dogfoodAssets: selected.filter((asset) => asset.status.evidencePosture === "dogfood_linked" || asset.status.managementStatus === "dogfood_ready").length,
1224
+ evidenceModules: evidenceIndexes.reduce((sum, entry) => sum + entry.index.totals.modules, 0),
1225
+ pass: evidenceIndexes.reduce((sum, entry) => sum + entry.index.totals.pass, 0),
1226
+ activeModules: evidenceIndexes.reduce((sum, entry) => sum + activeEvidenceModules(entry.index).length, 0),
1227
+ captureFacts: evidenceIndexes.reduce((sum, entry) => sum + entry.index.totals.captureFacts, 0),
1228
+ captureGaps: evidenceIndexes.reduce((sum, entry) => sum + c5GapCaptures(entry.index).length, 0),
1229
+ candidates: candidates.length,
1230
+ };
1231
+ return {
1232
+ mode: "workflow_candidate_backlog",
1233
+ scope: opts.name ? "asset" : "all_assets",
1234
+ focusAsset: opts.name,
1235
+ summary,
1236
+ reviewPacket: buildCandidateReviewPacket({
1237
+ focusAsset: opts.name,
1238
+ summary,
1239
+ candidates,
1240
+ nextCommands,
1241
+ }),
1242
+ candidates,
1243
+ nextCommands,
1244
+ mutation: {
1245
+ writes: false,
1246
+ note: "Candidate backlog is read-only. It does not create templates, edit workflow assets, write org profiles, mutate evolution markers, sync runtime artifacts, or treat Obsidian/wiki as product truth.",
1247
+ },
1248
+ };
1249
+ }