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,2393 @@
1
+ /**
2
+ * dna org — local organization/project asset management.
3
+ */
4
+ import { bindOrganizationAsset, createOrganizationProfile, isOrganizationAssetStatus, readOrganizationProfile, reviewOrganization, } from "../../organization/index.js";
5
+ import { activeEvidenceFollowupReviewRoute, activeEvidenceFollowupRoute, buildWorkflowAssetEvidenceIndex, findWorkflowAsset, listWorkflowAssets } from "../../assets/index.js";
6
+ import { WORKFLOW_ASSET_IDS } from "../../templates/metadata.js";
7
+ import { createConfigFromTemplate } from "./init.js";
8
+ function commandQuote(value) {
9
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
10
+ }
11
+ function commandValue(value) {
12
+ return /^[A-Za-z0-9_./:-]+$/.test(value) ? value : commandQuote(value);
13
+ }
14
+ function syncTargetFromDescription(description) {
15
+ const lower = description?.toLowerCase() ?? "";
16
+ if (lower.includes("codex"))
17
+ return "codex";
18
+ if (lower.includes("claude"))
19
+ return "claude";
20
+ return undefined;
21
+ }
22
+ function preferredProjectName(opts) {
23
+ return opts.project?.trim() || opts.description?.trim() || "My Project";
24
+ }
25
+ async function preferredAssetName(opts) {
26
+ if (opts.asset?.trim())
27
+ return opts.asset.trim();
28
+ const assets = await listWorkflowAssets({ projectDir: opts.projectDir });
29
+ const description = opts.description?.toLowerCase() ?? "";
30
+ const describedAsset = assets.find((asset) => description.includes(asset.name.toLowerCase()));
31
+ return describedAsset?.name
32
+ ?? assets.find((asset) => asset.status.managementStatus === "dogfood_ready")?.name
33
+ ?? assets.find((asset) => asset.name === WORKFLOW_ASSET_IDS.default)?.name
34
+ ?? assets[0]?.name
35
+ ?? WORKFLOW_ASSET_IDS.default;
36
+ }
37
+ function profileSyncTargets(profile) {
38
+ if (!profile)
39
+ return [];
40
+ return [...new Set(profile.projects.flatMap((project) => project.syncTargets))];
41
+ }
42
+ function projectHasAsset(profile, projectName, asset) {
43
+ if (!profile)
44
+ return false;
45
+ const project = profile.projects.find((entry) => entry.name === projectName || entry.id === projectName) ?? profile.projects[0];
46
+ return project?.assets.some((binding) => binding.asset === asset) === true;
47
+ }
48
+ function projectHasSyncTarget(profile, projectName, target) {
49
+ if (!profile)
50
+ return false;
51
+ const project = profile.projects.find((entry) => entry.name === projectName || entry.id === projectName) ?? profile.projects[0];
52
+ return project?.syncTargets.includes(target) === true;
53
+ }
54
+ function orgInitCommand(params) {
55
+ return `dna org init --name ${commandValue(params.name)} --project ${commandValue(params.project)} --asset ${params.asset} --sync-target ${params.syncTarget}`;
56
+ }
57
+ function orgBindCommand(params) {
58
+ const project = params.project ? ` --project ${commandValue(params.project)}` : "";
59
+ return `dna org bind --asset ${params.asset}${project} --status ${params.status} --sync-target ${params.syncTarget}`;
60
+ }
61
+ function rolloutCommandForTarget(target) {
62
+ const normalized = target.toLowerCase();
63
+ if (normalized === "codex" || normalized === "codex_cli")
64
+ return "dna sync --codex";
65
+ return "dna sync";
66
+ }
67
+ function buildOrgReleaseGate() {
68
+ return {
69
+ status: "conditional",
70
+ command: "dna release deploy-plan <changed-files>",
71
+ requiredWhen: [
72
+ "apps/online public, embed, management, or static handoff surface changed.",
73
+ "apps/dashboard changed and needs external deployed review.",
74
+ "public static assets, public articles, launch pages, or preview pages changed.",
75
+ ],
76
+ notRequiredFor: [
77
+ "dna CLI/core/runtime changes.",
78
+ "docs, references, wiki, and internal Obsidian project memory changes.",
79
+ "Organization rollout or runtime sync when no web/public deployed surface changed.",
80
+ ],
81
+ deployAndVerifyWhen: "Only after dna release deploy-plan returns deployment_required, or deployment_conditional is explicitly confirmed for external review.",
82
+ verification: [
83
+ "Run the local build/test commands printed by dna release deploy-plan.",
84
+ "Deploy with the printed Vercel CLI command and capture the deployment URL.",
85
+ "Wait for readiness with the printed Vercel inspect --wait command.",
86
+ "Verify the changed deployed routes with the printed Vercel curl commands before claiming release completion.",
87
+ ],
88
+ boundary: "Release gate is read-only here; org review and rollout do not deploy, sync runtime artifacts, mutate dashboard state, or replace dna release deploy-plan.",
89
+ };
90
+ }
91
+ function buildOrgOperatingRoute(params) {
92
+ const asset = params.asset || WORKFLOW_ASSET_IDS.default;
93
+ const syncTarget = params.syncTarget || "codex";
94
+ const followup = activeEvidenceFollowupRoute(asset);
95
+ const followupCommands = new Set([
96
+ followup.command,
97
+ `dna assets inspect ${followup.assetName} --evidence`,
98
+ `dna assets governance ${followup.assetName}`,
99
+ `dna assets diff ${asset} ${followup.assetName}`,
100
+ `dna assets adoption ${followup.assetName}`,
101
+ `dna org bind --asset ${followup.assetName} --status active --sync-target ${syncTarget}`,
102
+ ]);
103
+ const usingFollowup = followupCommands.has(params.currentCommand) || params.nextAsset === followup.assetName;
104
+ const reviewingFollowup = usingFollowup && params.currentCommand !== followup.command;
105
+ const nextVersion = params.nextAsset ?? (usingFollowup ? followup.assetName : `${asset}-v2`);
106
+ const editCommand = usingFollowup && !reviewingFollowup
107
+ ? followup.command
108
+ : `dna workflow edit-plan "Describe the reviewed workflow change" --from-template ${asset} --save-template ${nextVersion}`;
109
+ const syncCommand = rolloutCommandForTarget(syncTarget);
110
+ const organizeCommand = params.currentStage === "organize" && params.currentCommand.startsWith("dna org bind ")
111
+ ? params.currentCommand
112
+ : `dna org bind --asset ${nextVersion} --status active --sync-target ${syncTarget}`;
113
+ return {
114
+ spine: ["describe", "asset", "edit", "review", "organize", "configure", "rollout", "sync", "deploy", "improve"],
115
+ currentStage: params.currentStage,
116
+ currentCommand: params.currentCommand,
117
+ principle: "Users describe a working method; dna turns it into a governed workflow asset, routes it through manager review, binds it to an organization, then syncs runtime surfaces only after gates pass.",
118
+ phases: [
119
+ {
120
+ id: "describe",
121
+ label: "Describe work",
122
+ command: `dna workflow intake "Describe the working method you want to govern"`,
123
+ purpose: "Start from natural language rather than a canvas or harness-specific setup.",
124
+ writes: false,
125
+ owner: "user",
126
+ },
127
+ {
128
+ id: "asset",
129
+ label: "Choose or inspect asset",
130
+ command: `dna assets lineage ${asset}`,
131
+ purpose: "Understand the asset's origin, cascade, evidence, and evolution route before adoption.",
132
+ writes: false,
133
+ owner: "dna_core",
134
+ },
135
+ {
136
+ id: "edit",
137
+ label: usingFollowup
138
+ ? reviewingFollowup ? "Unfinished-work draft ready for review" : "Save unfinished-work draft"
139
+ : "Draft workflow change",
140
+ command: editCommand,
141
+ purpose: usingFollowup
142
+ ? reviewingFollowup
143
+ ? "The unfinished-work draft exists; keep generation closed and continue through diff/adoption review without replacing the source asset."
144
+ : "Save open dogfood evidence as a reviewable unfinished-work draft before organization binding or sync; the source asset stays intact."
145
+ : "Create a reviewable workflow asset version without mutating organization state.",
146
+ writes: usingFollowup ? !reviewingFollowup : true,
147
+ owner: "dna_core",
148
+ },
149
+ {
150
+ id: "review",
151
+ label: "Review asset version",
152
+ command: `dna assets diff ${asset} ${nextVersion}`,
153
+ purpose: "Let a manager review workflow and governance changes before adoption.",
154
+ writes: false,
155
+ owner: "manager",
156
+ },
157
+ {
158
+ id: "organize",
159
+ label: "Bind to organization",
160
+ command: organizeCommand,
161
+ purpose: "Record the accepted asset version in the organization route before rollout.",
162
+ writes: true,
163
+ owner: "manager",
164
+ },
165
+ {
166
+ id: "configure",
167
+ label: "Configure declared assets",
168
+ command: "dna org configure-plan",
169
+ purpose: "List or apply explicit dna init commands for organization-declared assets missing project config.",
170
+ writes: false,
171
+ owner: "dna_core",
172
+ },
173
+ {
174
+ id: "rollout",
175
+ label: "Review rollout",
176
+ command: "dna org rollout",
177
+ purpose: "Check target-level ready, partial, and blocked distribution plans before runtime sync.",
178
+ writes: false,
179
+ owner: "manager",
180
+ },
181
+ {
182
+ id: "sync",
183
+ label: "Sync runtime",
184
+ command: syncCommand,
185
+ purpose: "Generate Codex or Claude runtime artifacts only after organization gates pass.",
186
+ writes: true,
187
+ owner: "runtime",
188
+ },
189
+ {
190
+ id: "deploy",
191
+ label: "Deploy public surface",
192
+ command: "dna release deploy-plan",
193
+ purpose: "Use Vercel CLI deployment and deployed URL verification only for web/public surface changes.",
194
+ writes: false,
195
+ owner: "release",
196
+ },
197
+ {
198
+ id: "improve",
199
+ label: "Improve from evidence",
200
+ command: usingFollowup ? params.currentCommand : `dna assets suggestions ${asset}`,
201
+ purpose: usingFollowup
202
+ ? "Feed dogfood evidence into a concrete reviewed unfinished-work draft before promotion."
203
+ : "Feed dogfood evidence back into reviewed workflow asset improvements instead of mutating policy automatically.",
204
+ writes: false,
205
+ owner: "dna_core",
206
+ },
207
+ ],
208
+ boundaries: [
209
+ "dna CLI/core/runtime owns workflow assets, organization bindings, rollout gates, and sync decisions.",
210
+ "Dashboard is a read-only projection of this route, not a workflow builder or policy source of truth.",
211
+ "Obsidian/wiki is internal project memory for decisions and references, not product runtime state.",
212
+ "Claude Code and Codex are harness targets; governance logic should not be reimplemented inside those tools.",
213
+ "Vercel deployment is conditional and separate from organization rollout and runtime sync.",
214
+ ],
215
+ };
216
+ }
217
+ function buildDistributionPlan(targets, assets) {
218
+ return targets.map((target) => {
219
+ const targetAssets = assets.filter((asset) => asset.syncTargets.includes(target));
220
+ const readyAssets = targetAssets
221
+ .filter((asset) => asset.syncReady)
222
+ .map((asset) => ({ project: asset.project, asset: asset.asset }));
223
+ const blockedAssets = targetAssets
224
+ .filter((asset) => !asset.syncReady)
225
+ .map((asset) => ({
226
+ project: asset.project,
227
+ asset: asset.asset,
228
+ reason: asset.reason,
229
+ nextCommand: asset.nextCommand,
230
+ }));
231
+ const status = readyAssets.length > 0 && blockedAssets.length === 0
232
+ ? "ready"
233
+ : readyAssets.length > 0
234
+ ? "partial"
235
+ : "blocked";
236
+ return {
237
+ target,
238
+ status,
239
+ command: rolloutCommandForTarget(target),
240
+ readyAssets,
241
+ blockedAssets,
242
+ };
243
+ });
244
+ }
245
+ function orgRouteAssetRank(gate) {
246
+ if (gate.evidenceGate === "open")
247
+ return 0;
248
+ if (gate.bindingStatus === "active")
249
+ return 1;
250
+ if (gate.asset === WORKFLOW_ASSET_IDS.default)
251
+ return 2;
252
+ return 3;
253
+ }
254
+ function managerRisk(params) {
255
+ if (params.hasOpenEvidence)
256
+ return "open_evidence";
257
+ if (params.hasMissingConfiguration)
258
+ return "missing_configuration";
259
+ if (params.hasInactiveBinding)
260
+ return "inactive_binding";
261
+ if (params.decision === "ready_to_rollout")
262
+ return "ready_for_sync";
263
+ return "unknown";
264
+ }
265
+ function managerAllowedNow(params) {
266
+ if (params.decision === "ready_to_rollout") {
267
+ return [
268
+ "Review rollout readiness with dna org rollout.",
269
+ "Run the target sync command only after rollout stays ready.",
270
+ "Use dna release deploy-plan only if a web/public deployed surface changed.",
271
+ ];
272
+ }
273
+ if (params.decision === "review_evidence_before_rollout") {
274
+ const createCommand = activeEvidenceFollowupRoute(params.primaryAsset).command;
275
+ return [
276
+ params.primaryCommand === createCommand
277
+ ? `Save the unfinished-work draft: ${params.primaryCommand}.`
278
+ : `Review the saved unfinished-work draft: ${params.primaryCommand}.`,
279
+ "Review the draft diff before explicit adoption; the source asset is not replaced automatically.",
280
+ "Keep rollout and sync blocked until evidence is closed or explicitly triaged.",
281
+ ];
282
+ }
283
+ if (params.decision === "activate_bindings_before_rollout") {
284
+ return [
285
+ `Activate the configured organization binding: ${params.primaryCommand}.`,
286
+ "Return to dna org review after activation.",
287
+ "Run dna org rollout before any runtime sync command.",
288
+ ];
289
+ }
290
+ return [
291
+ "Review missing project configuration with dna org configure-plan.",
292
+ "Apply configuration only after the plan is acceptable.",
293
+ "Return to dna org review before rollout or sync.",
294
+ ];
295
+ }
296
+ function managerBlockedNow(params) {
297
+ const blocked = [];
298
+ if (params.hasOpenEvidence)
299
+ blocked.push("Runtime sync is blocked while dogfood evidence remains open.");
300
+ if (params.hasMissingConfiguration)
301
+ blocked.push("Runtime sync is blocked while organization-declared assets are not configured in the project.");
302
+ if (params.hasInactiveBinding)
303
+ blocked.push("Runtime sync is blocked while configured assets are not active in the organization binding.");
304
+ if (params.blockedAssets > 0)
305
+ blocked.push("Organization rollout is blocked for at least one declared asset.");
306
+ if (params.decision !== "ready_to_rollout")
307
+ blocked.push("Vercel deployment is not a substitute for org rollout or runtime sync.");
308
+ return blocked.length > 0 ? [...new Set(blocked)] : ["No manager-level blockers detected; still verify rollout before sync."];
309
+ }
310
+ function managerDecisionReason(params) {
311
+ if (params.decision === "review_evidence_before_rollout") {
312
+ if (params.dogfoodRemaining === 0) {
313
+ return "Dogfood modules show remaining=0, but an unfinished-work follow-up draft still needs diff/adoption review before rollout.";
314
+ }
315
+ return `Dogfood evidence has remaining=${params.dogfoodRemaining}; review or save an unfinished-work draft before rollout.`;
316
+ }
317
+ if (params.decision === "configure_assets_before_rollout") {
318
+ return `${params.blockedAssets} declared asset(s) are blocked and at least one needs project configuration before rollout.`;
319
+ }
320
+ if (params.decision === "activate_bindings_before_rollout") {
321
+ return `${params.blockedAssets} declared asset(s) are configured but still need manager activation before rollout.`;
322
+ }
323
+ return `${params.readyAssets}/${params.declaredAssets} declared asset(s) are ready; rollout review can decide whether to sync.`;
324
+ }
325
+ function buildUnfinishedWorkReview(params) {
326
+ const primaryFollowupMatch = /^(.*)-active-followup$/.exec(params.primaryAsset);
327
+ if (params.decision !== "review_evidence_before_rollout" && !primaryFollowupMatch)
328
+ return undefined;
329
+ const openGate = params.assets.find((asset) => asset.evidenceGate === "open")
330
+ ?? params.assets.find((asset) => asset.asset === params.primaryAsset);
331
+ const openFollowupMatch = openGate ? /^(.*)-active-followup$/.exec(openGate.asset) : undefined;
332
+ const sourceAsset = openFollowupMatch?.[1] ?? primaryFollowupMatch?.[1] ?? openGate?.asset ?? params.primaryAsset;
333
+ const route = activeEvidenceFollowupRoute(sourceAsset);
334
+ const normalizedCurrentCommand = params.primaryAsset === route.assetName && params.primaryCommand !== route.command && params.primaryCommand !== `${route.command} --force`
335
+ ? `dna assets diff ${sourceAsset} ${route.assetName}`
336
+ : params.primaryCommand;
337
+ const draftStatus = params.primaryCommand === route.command
338
+ ? "save_required"
339
+ : params.primaryCommand === `${route.command} --force`
340
+ ? "repair_required"
341
+ : params.primaryCommand === `dna assets inspect ${route.assetName} --evidence`
342
+ ? "inspect_required"
343
+ : "diff_review_required";
344
+ const diffCommand = `dna assets diff ${sourceAsset} ${route.assetName}`;
345
+ return {
346
+ mode: "unfinished_work_review",
347
+ status: draftStatus,
348
+ sourceAsset,
349
+ draftAsset: route.assetName,
350
+ plainLanguage: {
351
+ label: "saved unfinished-work draft",
352
+ purpose: "Make unfinished dogfood evidence explicit for manager review before stability, rollout, sync, publication readiness, or evolution claims.",
353
+ 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.",
354
+ preserves: [
355
+ `${sourceAsset} remains the original workflow asset.`,
356
+ `${WORKFLOW_ASSET_IDS.rewriteFixReviewLoop} remains the reusable fix loop asset.`,
357
+ `${route.assetName} is only a review draft until a manager accepts it through diff, adoption, bind, and rollout gates.`,
358
+ ],
359
+ requiredBeforeUse: [
360
+ diffCommand,
361
+ "record explicit manager rationale for the diff when needed",
362
+ `dna assets adoption ${route.assetName}`,
363
+ `dna org bind --asset ${route.assetName} --status active --sync-target codex`,
364
+ "dna org rollout",
365
+ ],
366
+ },
367
+ currentCommand: normalizedCurrentCommand,
368
+ writes: normalizedCurrentCommand === route.command || normalizedCurrentCommand === `${route.command} --force`,
369
+ purpose: "Capture open dogfood evidence as an explicit manager-review handoff before rollout, publication readiness, runtime sync, or evolution.",
370
+ localAuthority: "manager_review_handoff_only",
371
+ notReplacement: true,
372
+ sourceAssetPreserved: true,
373
+ reviewCommands: [
374
+ route.command,
375
+ `dna assets inspect ${route.assetName} --evidence`,
376
+ diffCommand,
377
+ `record explicit manager rationale for ${diffCommand}`,
378
+ `dna assets adoption ${route.assetName}`,
379
+ `dna org bind --asset ${route.assetName} --status active --sync-target codex`,
380
+ "dna org rollout",
381
+ ],
382
+ boundaries: [
383
+ `${route.assetName} is a saved unfinished-work draft for ${sourceAsset}; it does not replace, delete, or auto-adopt ${sourceAsset}.`,
384
+ `${sourceAsset} remains the original workflow asset until a manager accepts a diff, adopts the draft, updates organization binding, and reruns rollout.`,
385
+ "The reusable fix loop remains a separate workflow asset; unfinished-work review does not remove or collapse existing fix-loop behavior.",
386
+ "This review package is local manager-review evidence only; it does not publish org_private assets or grant runtime authority.",
387
+ ],
388
+ notAllowedToImply: [
389
+ "source template deletion",
390
+ "source template replacement",
391
+ "automatic adoption",
392
+ "runtime sync approval",
393
+ "org_private SaaS publication",
394
+ "workflow execution",
395
+ ],
396
+ };
397
+ }
398
+ function assetFamilyName(asset) {
399
+ return asset.replace(/-active-followup$/, "");
400
+ }
401
+ function isSameAssetFamily(a, b) {
402
+ return assetFamilyName(a) === assetFamilyName(b);
403
+ }
404
+ function buildAssetPortfolio(assetGates) {
405
+ const active = assetGates
406
+ .filter((gate) => gate.bindingStatus === "active")
407
+ .map((gate) => ({
408
+ project: gate.project,
409
+ asset: gate.asset,
410
+ version: gate.assetVersion,
411
+ targets: gate.syncTargets,
412
+ rollout: gate.syncReady ? "ready" : "blocked",
413
+ evidence: gate.evidenceGate,
414
+ evidencePosture: gate.evidencePosture,
415
+ reviewRefs: gate.reviewRefs,
416
+ reviewEvidence: gate.reviewEvidence,
417
+ reviewCommand: gate.reviewCommand,
418
+ }));
419
+ const activeByProject = new Map();
420
+ for (const gate of assetGates) {
421
+ if (gate.bindingStatus !== "active")
422
+ continue;
423
+ const current = activeByProject.get(gate.project) ?? [];
424
+ current.push(gate.asset);
425
+ activeByProject.set(gate.project, current.sort((a, b) => a.localeCompare(b)));
426
+ }
427
+ const stale = assetGates
428
+ .filter((gate) => gate.bindingStatus !== "active" && activeByProject.get(gate.project)?.some((activeAsset) => isSameAssetFamily(gate.asset, activeAsset)))
429
+ .map((gate) => {
430
+ const activeAsset = activeByProject.get(gate.project)?.find((item) => isSameAssetFamily(gate.asset, item)) ?? gate.asset;
431
+ return {
432
+ project: gate.project,
433
+ asset: gate.asset,
434
+ version: gate.assetVersion,
435
+ reason: `Active asset ${activeAsset} is already bound for ${gate.project}; compare this ${gate.bindingStatus} version before promotion, pause, or removal.`,
436
+ nextCommand: `dna assets diff ${gate.asset} ${activeAsset}`,
437
+ reviewCommand: gate.reviewCommand,
438
+ reviewEvidence: gate.reviewEvidence,
439
+ };
440
+ });
441
+ const blocked = assetGates
442
+ .filter((gate) => !gate.syncReady)
443
+ .map((gate) => ({
444
+ project: gate.project,
445
+ asset: gate.asset,
446
+ version: gate.assetVersion,
447
+ reason: gate.reason,
448
+ nextCommand: gate.nextCommand,
449
+ evidence: gate.evidenceGate,
450
+ evidencePosture: gate.evidencePosture,
451
+ reviewCommand: gate.reviewCommand,
452
+ reviewEvidence: gate.reviewEvidence,
453
+ }));
454
+ const evidenceGaps = assetGates
455
+ .filter((gate) => gate.evidenceGate !== "clear")
456
+ .map((gate) => ({
457
+ project: gate.project,
458
+ asset: gate.asset,
459
+ version: gate.assetVersion,
460
+ evidenceGate: gate.evidenceGate,
461
+ evidencePosture: gate.evidencePosture,
462
+ reviewRefs: gate.reviewRefs,
463
+ reason: gate.evidenceGate === "open"
464
+ ? "Open evidence must be reviewed or saved as unfinished work before broad rollout."
465
+ : "No linked evidence was found; adoption can continue only as an explicitly reviewed manager decision.",
466
+ nextCommand: gate.evidenceGate === "open" ? gate.nextCommand : gate.reviewCommand,
467
+ }));
468
+ const upgradeRisks = [
469
+ ...stale.map((item) => ({
470
+ project: item.project,
471
+ asset: item.asset,
472
+ version: item.version,
473
+ reason: item.reason,
474
+ nextCommand: item.nextCommand,
475
+ reviewCommand: item.reviewCommand,
476
+ reviewEvidence: item.reviewEvidence,
477
+ })),
478
+ ...assetGates
479
+ .filter((gate) => gate.bindingStatus !== "active" && !stale.some((item) => item.project === gate.project && item.asset === gate.asset))
480
+ .map((gate) => ({
481
+ project: gate.project,
482
+ asset: gate.asset,
483
+ version: gate.assetVersion,
484
+ reason: gate.evidenceGate === "open"
485
+ ? `Asset binding is ${gate.bindingStatus}, but open evidence must be reviewed before manager activation or runtime distribution.`
486
+ : `Asset binding is ${gate.bindingStatus}; manager review must activate it before runtime distribution.`,
487
+ nextCommand: gate.evidenceGate === "open"
488
+ ? gate.nextCommand
489
+ : `dna org bind --asset ${gate.asset} --status active --sync-target ${gate.syncTargets[0] ?? "codex"}`,
490
+ reviewCommand: gate.reviewCommand,
491
+ reviewEvidence: gate.reviewEvidence,
492
+ })),
493
+ ...assetGates
494
+ .filter((gate) => gate.evidenceGate === "open")
495
+ .map((gate) => ({
496
+ project: gate.project,
497
+ asset: gate.asset,
498
+ version: gate.assetVersion,
499
+ reason: "Open dogfood evidence can change the recommended asset version before rollout.",
500
+ nextCommand: gate.nextCommand,
501
+ reviewCommand: gate.reviewCommand,
502
+ reviewEvidence: gate.reviewEvidence,
503
+ })),
504
+ ];
505
+ const uniqueUpgradeRisks = [...new Map(upgradeRisks.map((risk) => [`${risk.project}:${risk.asset}:${risk.reason}`, risk])).values()];
506
+ const rolloutMaturity = assetGates.length === 0
507
+ ? "empty"
508
+ : active.some((item) => item.rollout === "ready") && blocked.length === 0
509
+ ? "active_ready"
510
+ : active.length > 0
511
+ ? "active_blocked"
512
+ : evidenceGaps.some((gap) => gap.evidenceGate === "open")
513
+ ? "review_required"
514
+ : assetGates.some((gate) => gate.reason.includes("binding is not active"))
515
+ ? "activation_needed"
516
+ : "configuration_needed";
517
+ return {
518
+ active,
519
+ stale,
520
+ blocked,
521
+ evidenceGaps,
522
+ upgradeRisks: uniqueUpgradeRisks,
523
+ rolloutMaturity,
524
+ };
525
+ }
526
+ function buildManagerChoices(params) {
527
+ const syncCommand = params.readyTargets[0]?.command ?? rolloutCommandForTarget(params.primaryTarget ?? "codex");
528
+ const evidenceCommand = params.hasOpenEvidence ? params.primaryCommand : `dna assets suggestions ${params.primaryAsset}`;
529
+ const evidenceWrites = params.hasOpenEvidence
530
+ ? params.primaryCommand === activeEvidenceFollowupRoute(params.primaryAsset).command
531
+ : false;
532
+ const evidenceStatus = params.hasOpenEvidence ? "recommended" : "available";
533
+ const configureStatus = params.hasOpenEvidence
534
+ ? "blocked"
535
+ : params.hasMissingConfiguration
536
+ ? "recommended"
537
+ : "available";
538
+ const rolloutStatus = params.decision === "ready_to_rollout" ? "recommended" : "blocked";
539
+ const syncStatus = params.decision === "ready_to_rollout" ? "available" : "blocked";
540
+ const blockReason = params.hasOpenEvidence
541
+ ? "Open dogfood evidence should be reviewed through a follow-up continuation before configuration, rollout, or runtime sync."
542
+ : params.hasMissingConfiguration
543
+ ? "Declared workflow assets need project configuration before rollout or runtime sync."
544
+ : params.hasInactiveBinding
545
+ ? "Configured workflow assets need manager activation before rollout or runtime sync."
546
+ : "Declared workflow assets are not ready for rollout or runtime sync yet.";
547
+ return [
548
+ {
549
+ id: "handle_evidence",
550
+ label: "Handle evidence",
551
+ command: evidenceCommand,
552
+ purpose: "Turn dogfood evidence into a reviewed workflow-asset improvement path.",
553
+ writes: evidenceWrites,
554
+ status: evidenceStatus,
555
+ reason: params.hasOpenEvidence
556
+ ? "Dogfood evidence is still open, so this is the safest first manager action."
557
+ : "No open dogfood evidence blocks rollout; evidence suggestions remain available for improvement work.",
558
+ next: params.hasOpenEvidence
559
+ ? `Review the follow-up continuation with dna assets diff ${params.primaryAsset} ${activeEvidenceFollowupRoute(params.primaryAsset).assetName}, then return to dna org review.`
560
+ : "Review suggestions, draft a follow-up only if useful, then return to dna org review.",
561
+ },
562
+ {
563
+ id: "configure_assets",
564
+ label: "Configure assets",
565
+ command: "dna org configure-plan",
566
+ purpose: "Inspect or apply missing project configuration for organization-declared workflow assets.",
567
+ writes: false,
568
+ status: configureStatus,
569
+ reason: params.hasOpenEvidence
570
+ ? "Configuration waits behind the open evidence gate."
571
+ : params.hasMissingConfiguration
572
+ ? "At least one declared workflow asset is not configured in the project yet."
573
+ : "No missing project configuration is blocking rollout.",
574
+ next: params.hasOpenEvidence
575
+ ? params.primaryCommand
576
+ : params.hasMissingConfiguration
577
+ ? "If the plan is acceptable, run dna org configure-plan --apply, then dna org review."
578
+ : params.hasInactiveBinding
579
+ ? params.primaryCommand
580
+ : "Run dna org rollout to review distribution readiness.",
581
+ },
582
+ {
583
+ id: "review_rollout",
584
+ label: "Review rollout",
585
+ command: "dna org rollout",
586
+ purpose: "Check per-target ready, partial, and blocked distribution before runtime sync.",
587
+ writes: false,
588
+ status: rolloutStatus,
589
+ reason: params.decision === "ready_to_rollout"
590
+ ? "Declared assets are ready enough for manager rollout review."
591
+ : blockReason,
592
+ next: params.decision === "ready_to_rollout" ? syncCommand : params.primaryCommand,
593
+ },
594
+ {
595
+ id: "sync_runtime",
596
+ label: "Sync runtime",
597
+ command: syncCommand,
598
+ purpose: "Generate Claude/Codex runtime artifacts after manager gates pass.",
599
+ writes: true,
600
+ status: syncStatus,
601
+ reason: params.decision === "ready_to_rollout"
602
+ ? "Runtime sync is available after rollout review confirms the target is ready."
603
+ : blockReason,
604
+ next: params.decision === "ready_to_rollout"
605
+ ? "Run dna release deploy-plan <changed-files> only if web/public surfaces changed."
606
+ : params.primaryCommand,
607
+ },
608
+ {
609
+ id: "release_public_surface",
610
+ label: "Release public surface",
611
+ command: "dna release deploy-plan <changed-files>",
612
+ purpose: "Decide whether Vercel CLI deployment and deployed URL verification are required.",
613
+ writes: false,
614
+ status: "conditional",
615
+ reason: "Release work is separate from organization rollout and runtime sync, and only opens for web/public deployed surface changes.",
616
+ next: "If required, run the printed Vercel deploy, inspect, and route verification commands.",
617
+ },
618
+ ];
619
+ }
620
+ function managerPathStatus(choice) {
621
+ if (choice.status === "recommended")
622
+ return "current";
623
+ return choice.status;
624
+ }
625
+ function buildManagerPath(params) {
626
+ const choiceById = new Map(params.choices.map((choice) => [choice.id, choice]));
627
+ const choiceStep = (id, routeStages, owner) => {
628
+ const choice = choiceById.get(id);
629
+ if (!choice) {
630
+ return {
631
+ id,
632
+ label: id,
633
+ command: params.primaryCommand,
634
+ purpose: "Manager path step unavailable; inspect dna org review.",
635
+ owner,
636
+ writes: false,
637
+ status: "blocked",
638
+ routeStages,
639
+ next: "dna org review",
640
+ };
641
+ }
642
+ return {
643
+ id,
644
+ label: choice.label,
645
+ command: choice.command,
646
+ purpose: choice.purpose,
647
+ owner,
648
+ writes: choice.writes,
649
+ status: managerPathStatus(choice),
650
+ routeStages,
651
+ next: choice.next,
652
+ };
653
+ };
654
+ return [
655
+ {
656
+ id: "review_system",
657
+ label: "Review organization state",
658
+ command: "dna org review",
659
+ purpose: "Read the organization profile, declared workflow assets, evidence gates, target readiness, and release boundary before action.",
660
+ owner: "manager",
661
+ writes: false,
662
+ status: "done",
663
+ routeStages: ["organize"],
664
+ next: params.primaryCommand,
665
+ },
666
+ choiceStep("handle_evidence", ["improve"], "dna_core"),
667
+ choiceStep("configure_assets", ["configure"], "dna_core"),
668
+ choiceStep("review_rollout", ["rollout"], "manager"),
669
+ choiceStep("sync_runtime", ["sync"], "runtime"),
670
+ choiceStep("release_public_surface", ["deploy"], "release"),
671
+ ];
672
+ }
673
+ function buildOrgEntryPacket(params) {
674
+ const decision = params.path === "create_profile"
675
+ ? "create_profile_first"
676
+ : params.path === "bind_asset"
677
+ ? "bind_asset_first"
678
+ : "review_existing_profile";
679
+ const primaryAction = params.path === "review_existing" && params.evidenceGate === "open" && params.followupCommand
680
+ ? params.followupCommand
681
+ : params.command;
682
+ const currentState = params.path === "create_profile"
683
+ ? `missing_profile:${params.assetName}:${params.syncTarget}`
684
+ : params.path === "bind_asset"
685
+ ? `profile_exists:asset_unbound:${params.assetName}:${params.syncTarget}`
686
+ : `profile_exists:asset_bound:${params.assetName}:${params.syncTarget}`;
687
+ const reason = params.path === "create_profile"
688
+ ? "Create the local organization/project/asset route before manager review, rollout, or runtime sync."
689
+ : params.path === "bind_asset"
690
+ ? "Bind the selected workflow asset and sync target into the existing organization profile before rollout review."
691
+ : params.evidenceGate === "open"
692
+ ? "The organization route exists, but active dogfood evidence must be reviewed or converted into a follow-up before rollout, sync, or evolution."
693
+ : "The organization route exists; review current gates before rollout or runtime sync.";
694
+ return {
695
+ currentState,
696
+ primaryAction,
697
+ decision,
698
+ evidenceGate: params.evidenceGate,
699
+ reason,
700
+ allowedNow: [
701
+ `Inspect workflow asset lineage: dna assets lineage ${params.assetName}.`,
702
+ params.evidenceGate === "open"
703
+ ? `Review active dogfood evidence: dna assets lifecycle ${params.assetName}.`
704
+ : `Review asset adoption posture: dna assets adoption ${params.assetName}.`,
705
+ `Run the recommended organization action: ${params.command}.`,
706
+ "Use dna org review after the route is represented.",
707
+ ],
708
+ blockedNow: [
709
+ params.evidenceGate === "open"
710
+ ? "Runtime sync is blocked while active dogfood evidence remains open or untriaged."
711
+ : "Runtime sync is blocked until manager review and rollout readiness pass.",
712
+ "Evolution marker mutation is blocked until evidence-backed suggestions are reviewed and accepted.",
713
+ "Dashboard and Obsidian/wiki are not organization state or runtime truth.",
714
+ "Vercel deployment is not a substitute for organization rollout or runtime sync.",
715
+ ],
716
+ reviewPath: params.reviewPath,
717
+ };
718
+ }
719
+ function buildManagerSummary(review) {
720
+ const assetGates = review.projects.flatMap((project) => project.assetGates.map((gate) => ({
721
+ project: project.name,
722
+ asset: gate.asset,
723
+ assetVersion: gate.assetVersion,
724
+ contentHash: gate.contentHash,
725
+ evidencePosture: gate.evidencePosture,
726
+ reviewRefs: gate.reviewRefs,
727
+ reviewEvidence: gate.reviewEvidence,
728
+ reviewCommand: gate.reviewCommand,
729
+ syncTargets: gate.syncTargets,
730
+ syncReady: gate.syncReady,
731
+ evidenceGate: gate.evidenceGate,
732
+ bindingStatus: gate.bindingStatus,
733
+ nextCommand: gate.nextCommand,
734
+ reason: gate.reason,
735
+ })));
736
+ const readyAssets = assetGates
737
+ .filter((gate) => gate.syncReady)
738
+ .map((gate) => ({ project: gate.project, asset: gate.asset, targets: gate.syncTargets }));
739
+ const blockedAssets = assetGates
740
+ .filter((gate) => !gate.syncReady)
741
+ .map((gate) => ({
742
+ project: gate.project,
743
+ asset: gate.asset,
744
+ reason: gate.reason,
745
+ nextCommand: gate.nextCommand,
746
+ }));
747
+ const assetPortfolio = buildAssetPortfolio(assetGates);
748
+ const syncTargets = [...new Set(review.projects.flatMap((project) => project.syncTargets))];
749
+ const readyTargets = syncTargets
750
+ .map((target) => ({
751
+ target,
752
+ command: rolloutCommandForTarget(target),
753
+ assets: readyAssets
754
+ .filter((asset) => asset.targets.includes(target))
755
+ .map((asset) => ({ project: asset.project, asset: asset.asset })),
756
+ }))
757
+ .filter((target) => target.assets.length > 0);
758
+ const blockedTargets = syncTargets
759
+ .map((target) => ({
760
+ target,
761
+ assets: assetGates
762
+ .filter((gate) => gate.syncTargets.includes(target) && !gate.syncReady)
763
+ .map((gate) => ({ project: gate.project, asset: gate.asset, nextCommand: gate.nextCommand })),
764
+ }))
765
+ .filter((target) => target.assets.length > 0);
766
+ const hasOpenEvidence = assetGates.some((gate) => gate.evidenceGate === "open");
767
+ const hasMissingConfiguration = assetGates.some((gate) => !gate.syncReady && gate.evidenceGate !== "open" && gate.nextCommand.startsWith("dna init --template "));
768
+ const hasInactiveBinding = assetGates.some((gate) => !gate.syncReady && gate.evidenceGate !== "open" && gate.reason.includes("binding is not active"));
769
+ const status = hasMissingConfiguration || blockedAssets.length > 0
770
+ ? hasOpenEvidence ? "review_required" : "blocked"
771
+ : "ready";
772
+ const decision = hasOpenEvidence
773
+ ? "review_evidence_before_rollout"
774
+ : hasMissingConfiguration
775
+ ? "configure_assets_before_rollout"
776
+ : hasInactiveBinding
777
+ ? "activate_bindings_before_rollout"
778
+ : "ready_to_rollout";
779
+ const primaryGate = [...assetGates].sort((a, b) => orgRouteAssetRank(a) - orgRouteAssetRank(b) || a.asset.localeCompare(b.asset))[0];
780
+ const primaryCommand = decision === "configure_assets_before_rollout"
781
+ ? "dna org configure-plan"
782
+ : decision === "review_evidence_before_rollout"
783
+ ? primaryGate?.nextCommand ?? `dna assets suggestions ${WORKFLOW_ASSET_IDS.default}`
784
+ : decision === "activate_bindings_before_rollout"
785
+ ? primaryGate?.nextCommand ?? "dna org review"
786
+ : "dna org rollout";
787
+ const primaryAsset = primaryGate?.asset ?? blockedAssets[0]?.asset ?? readyAssets[0]?.asset ?? WORKFLOW_ASSET_IDS.default;
788
+ const primaryFollowup = activeEvidenceFollowupRoute(primaryAsset);
789
+ const primaryNextAsset = decision === "review_evidence_before_rollout" && primaryCommand !== `dna assets suggestions ${primaryAsset}`
790
+ ? primaryFollowup.assetName
791
+ : undefined;
792
+ const primaryTarget = blockedAssets.length > 0
793
+ ? primaryGate?.syncTargets[0]
794
+ : readyAssets[0]?.targets[0];
795
+ const currentStage = decision === "configure_assets_before_rollout"
796
+ ? "configure"
797
+ : decision === "review_evidence_before_rollout"
798
+ ? "improve"
799
+ : decision === "activate_bindings_before_rollout"
800
+ ? "organize"
801
+ : "rollout";
802
+ const evidenceStatus = review.totals.dogfoodEvidenceModules === 0
803
+ ? "unknown"
804
+ : hasOpenEvidence || review.totals.dogfoodEvidenceRemaining > 0
805
+ ? "open"
806
+ : "clear";
807
+ const unfinishedWork = buildUnfinishedWorkReview({
808
+ decision,
809
+ primaryAsset,
810
+ primaryCommand,
811
+ assets: assetGates,
812
+ });
813
+ const decisionPacket = {
814
+ currentState: `${readyAssets.length}/${review.totals.declaredAssets} ready, ${blockedAssets.length} blocked, evidence=${evidenceStatus}`,
815
+ primaryAction: primaryCommand,
816
+ risk: managerRisk({ decision, hasOpenEvidence, hasMissingConfiguration, hasInactiveBinding }),
817
+ allowedNow: managerAllowedNow({ decision, primaryCommand, primaryAsset }),
818
+ blockedNow: managerBlockedNow({
819
+ decision,
820
+ hasOpenEvidence,
821
+ hasMissingConfiguration,
822
+ hasInactiveBinding,
823
+ blockedAssets: blockedAssets.length,
824
+ }),
825
+ reason: managerDecisionReason({
826
+ decision,
827
+ readyAssets: readyAssets.length,
828
+ declaredAssets: review.totals.declaredAssets,
829
+ blockedAssets: blockedAssets.length,
830
+ dogfoodRemaining: review.totals.dogfoodEvidenceRemaining,
831
+ }),
832
+ unfinishedWork,
833
+ };
834
+ const managerChoices = buildManagerChoices({
835
+ decision,
836
+ primaryCommand,
837
+ primaryAsset,
838
+ primaryTarget,
839
+ hasOpenEvidence,
840
+ hasMissingConfiguration,
841
+ hasInactiveBinding,
842
+ readyTargets,
843
+ });
844
+ return {
845
+ status,
846
+ decision,
847
+ headline: `${readyAssets.length}/${review.totals.declaredAssets} declared asset(s) ready for sync; ${blockedAssets.length} blocked.`,
848
+ primaryCommand,
849
+ decisionPacket,
850
+ unfinishedWork,
851
+ operatingRoute: buildOrgOperatingRoute({
852
+ asset: primaryAsset,
853
+ nextAsset: primaryNextAsset,
854
+ syncTarget: primaryTarget,
855
+ currentStage,
856
+ currentCommand: primaryCommand,
857
+ }),
858
+ readyAssets,
859
+ blockedAssets,
860
+ assetPortfolio,
861
+ readyTargets,
862
+ blockedTargets,
863
+ dogfoodEvidence: {
864
+ status: evidenceStatus,
865
+ modules: review.totals.dogfoodEvidenceModules,
866
+ pass: review.totals.dogfoodEvidencePass,
867
+ remaining: review.totals.dogfoodEvidenceRemaining,
868
+ },
869
+ improvementPrinciple: {
870
+ command: `dna assets suggestions ${primaryAsset}`,
871
+ rule: "Dogfood problems become reviewed workflow assets; repeated workflow-template gaps become shared dna core infrastructure.",
872
+ templateBoundary: "A concrete project problem should become a reviewed template version or reusable workflow asset before rollout or sync.",
873
+ infraBoundary: "If multiple templates need the same support, add or reuse dna core capability instead of copy-pasting prompt logic into each workflow.",
874
+ },
875
+ releaseGate: buildOrgReleaseGate(),
876
+ managerChoices,
877
+ managerPath: buildManagerPath({
878
+ decision,
879
+ primaryCommand,
880
+ choices: managerChoices,
881
+ }),
882
+ nextCommands: [...new Set([
883
+ primaryCommand,
884
+ ...review.nextActions.map((action) => action.split(": ").slice(1).join(": ")).filter(Boolean),
885
+ "dna org rollout",
886
+ "dna org review",
887
+ ])],
888
+ mutationBoundary: "Manager summary is read-only. It does not configure assets, bind organizations, sync runtime artifacts, deploy web surfaces, or replace the detailed organization review.",
889
+ };
890
+ }
891
+ function withManagerSummary(review) {
892
+ return {
893
+ ...review,
894
+ managerSummary: buildManagerSummary(review),
895
+ };
896
+ }
897
+ function orgReceiptGuardrails() {
898
+ return [
899
+ "Organization review receipt is local projection evidence only; cloud_authority=false.",
900
+ "Internal-ready candidates come from local org review and rollout gates, not org-private SaaS visibility.",
901
+ "Org-private publication requires dna templates publish plus Supabase auth/RLS/audit/storage contracts; this receipt only prepares operator commands.",
902
+ "This receipt does not imply online org auth, permissions, backend persistence, or cloud storage.",
903
+ "dna sync installs, distributes, and injects artifacts only; it does not execute workflows.",
904
+ "Online may project this receipt, but the local dna CLI/core remains the source for org review and rollout truth.",
905
+ ];
906
+ }
907
+ function orgPrivatePublicationBoundaries() {
908
+ return [
909
+ "Publication plan is local readiness guidance only; it does not publish, list, revoke, or persist org-private assets.",
910
+ "internal_ready remains an adoption candidate state until dna templates publish succeeds against authenticated Supabase Edge Functions.",
911
+ "Org-private visibility must come from Supabase auth, RLS, storage, permission checks, and audit records, never from the static marketplace catalog.",
912
+ "Published workflow assets remain installable through local dna commands; online and Supabase do not execute workflows or make allow/warn/block/validate decisions.",
913
+ ];
914
+ }
915
+ function orgReviewReceiptRef(receiptPathHint) {
916
+ return {
917
+ label: "Local org review receipt",
918
+ path: receiptPathHint,
919
+ kind: "org_review_receipt",
920
+ description: "Manager-facing local organization review source for publication handoff; it is not install, approval, publication, execution, or runtime authority.",
921
+ };
922
+ }
923
+ function localOrgReviewReceiptEvidence(params) {
924
+ return {
925
+ mode: "local_org_review_receipt",
926
+ authority: "manager_review_handoff_only",
927
+ receipt_command: params.receiptCommand,
928
+ receipt_path_hint: params.receiptPathHint,
929
+ boundaries: [
930
+ "Local org review receipt evidence is a manager review handoff only; it does not publish, list, revoke, install, approve, or execute workflow assets.",
931
+ "Supabase auth, RLS, scoped tokens, storage, and audit receipts remain required before org_private lifecycle claims.",
932
+ "Local dna/dna-hook remain runtime truth for allow/warn/block/validate decisions.",
933
+ ],
934
+ };
935
+ }
936
+ function serializedReviewEvidence(evidence) {
937
+ return evidence.map((entry) => ({
938
+ mode: entry.mode,
939
+ authority: entry.authority,
940
+ receipt_command: entry.receiptCommand,
941
+ receipt_path_hint: entry.receiptPathHint,
942
+ boundaries: entry.boundaries,
943
+ }));
944
+ }
945
+ function buildOrgPrivatePublicationPlan(params) {
946
+ const receiptPathHint = ".dna/reports/org-review-receipt.json";
947
+ const receiptCommand = `dna org receipt --json > ${receiptPathHint}`;
948
+ const receiptAuthorityBoundary = "Org-private publication plan proves local review readiness only; it does not prove Supabase membership, RLS permission checks, audit persistence, local install, workflow execution, or runtime policy authority.";
949
+ const postPublicationLocalGates = [
950
+ "dna org review",
951
+ "dna org rollout",
952
+ "dna sync --dry-run",
953
+ "dna sync",
954
+ "dna report package --json",
955
+ ];
956
+ const prerequisites = [
957
+ "Run dna org receipt --json and keep the emitted receipt as source evidence for publication.",
958
+ "Deploy publish-template and list-template-publications Edge Functions from the Supabase deploy plan.",
959
+ "Deploy revoke-template-publication before treating org-private publication lifecycle as complete.",
960
+ "Use a CLI API token scoped to the same organization/project with template:publish for publish/revoke and template:read for listing.",
961
+ "Endpoint can come from INTENTDNA_CLOUD_URL, SUPABASE_URL, a linked Supabase project ref, or an explicit --endpoint.",
962
+ "Run the generated dry-run commands before live publish/list/revoke; dry-run performs no network request.",
963
+ "Keep org-private catalog visibility in SaaS-backed tables with audit records; do not add org_private to the static marketplace catalog.",
964
+ ];
965
+ const blockers = [
966
+ "Live publish/list/revoke smoke requires Supabase project credentials, deployed Edge Functions, organization/project rows, memberships, and scoped CLI API tokens.",
967
+ "This local receipt cannot prove non-member read denial, unauthorized publish denial, or unpublish audit insertion until live RLS/function tests run.",
968
+ ];
969
+ if (!params.review) {
970
+ return {
971
+ status: "missing_profile",
972
+ authority: "local_publication_readiness_only",
973
+ receipt_authority_boundary: receiptAuthorityBoundary,
974
+ receipt_command: receiptCommand,
975
+ receipt_path_hint: receiptPathHint,
976
+ post_publication_local_gates: postPublicationLocalGates,
977
+ candidates: [],
978
+ blockers: ["No organization profile exists, so no local asset can be prepared for org-private publication.", ...blockers],
979
+ prerequisites,
980
+ boundaries: orgPrivatePublicationBoundaries(),
981
+ };
982
+ }
983
+ const projectsByName = new Map(params.review.projects.map((project) => [project.name, project]));
984
+ const candidates = params.readyAssets.map((asset) => {
985
+ const project = projectsByName.get(asset.project);
986
+ const projectId = project?.id ?? asset.project;
987
+ const publishCommand = `dna templates publish --org ${params.review.profile.organization.id} --project ${projectId} --template ${asset.asset} --receipt ${receiptPathHint}`;
988
+ const publicationsCommand = `dna templates publications --org ${params.review.profile.organization.id} --project ${projectId} --template ${asset.asset}`;
989
+ const revokeCommand = `dna templates revoke --org ${params.review.profile.organization.id} --project ${projectId} --publication-id <publication-id>`;
990
+ const dryRunCommands = [
991
+ `${publishCommand} --dry-run --json`,
992
+ `${publicationsCommand} --dry-run --json`,
993
+ `${revokeCommand} --dry-run --json`,
994
+ ];
995
+ const syncCommand = asset.syncTargets.some((target) => target === "codex" || target === "codex_cli") ? "dna sync --codex" : "dna sync";
996
+ const reviewRefs = [orgReviewReceiptRef(receiptPathHint), ...asset.reviewRefs];
997
+ const reviewEvidence = [
998
+ localOrgReviewReceiptEvidence({ receiptCommand, receiptPathHint }),
999
+ ...serializedReviewEvidence(asset.reviewEvidence),
1000
+ ];
1001
+ return {
1002
+ organization: {
1003
+ id: params.review.profile.organization.id,
1004
+ name: params.review.profile.organization.name,
1005
+ },
1006
+ project: {
1007
+ id: projectId,
1008
+ name: asset.project,
1009
+ },
1010
+ asset: asset.asset,
1011
+ asset_version: asset.assetVersion,
1012
+ content_hash: asset.contentHash,
1013
+ sync_targets: asset.syncTargets,
1014
+ publish_ready: true,
1015
+ publish_command: publishCommand,
1016
+ publications_command: publicationsCommand,
1017
+ revoke_command: revokeCommand,
1018
+ dry_run_commands: dryRunCommands,
1019
+ local_review_route: [
1020
+ `dna assets inspect ${asset.asset} --evidence`,
1021
+ `dna org review`,
1022
+ `dna org rollout`,
1023
+ syncCommand,
1024
+ ],
1025
+ source_receipt: receiptPathHint,
1026
+ review_refs: reviewRefs,
1027
+ review_evidence: reviewEvidence,
1028
+ blockers,
1029
+ };
1030
+ });
1031
+ return {
1032
+ status: candidates.length > 0 ? "ready_for_operator" : "blocked",
1033
+ authority: "local_publication_readiness_only",
1034
+ receipt_authority_boundary: receiptAuthorityBoundary,
1035
+ receipt_command: receiptCommand,
1036
+ receipt_path_hint: receiptPathHint,
1037
+ post_publication_local_gates: postPublicationLocalGates,
1038
+ candidates,
1039
+ blockers: candidates.length > 0
1040
+ ? blockers
1041
+ : ["No sync-ready local assets are available for internal-ready publication guidance yet.", ...blockers],
1042
+ prerequisites,
1043
+ boundaries: orgPrivatePublicationBoundaries(),
1044
+ };
1045
+ }
1046
+ function buildOrgReviewReceipt(profilePath, review, rollout) {
1047
+ const managerSummary = review ? buildManagerSummary(review) : undefined;
1048
+ const readyAssets = rollout.assets.filter((asset) => asset.syncReady);
1049
+ const orgPrivatePublicationPlan = buildOrgPrivatePublicationPlan({
1050
+ review,
1051
+ readyAssets,
1052
+ });
1053
+ const publicationCandidateByAsset = new Map(orgPrivatePublicationPlan.candidates.map((candidate) => [
1054
+ `${candidate.project.name}:${candidate.asset}`,
1055
+ candidate,
1056
+ ]));
1057
+ const internalReadyCandidates = readyAssets
1058
+ .map((asset) => ({
1059
+ organization_id: review?.profile.organization.id ?? "",
1060
+ project_id: publicationCandidateByAsset.get(`${asset.project}:${asset.asset}`)?.project.id ?? asset.project,
1061
+ project: asset.project,
1062
+ asset: asset.asset,
1063
+ asset_version: asset.assetVersion,
1064
+ content_hash: asset.contentHash,
1065
+ sync_targets: asset.syncTargets,
1066
+ publish_ready: true,
1067
+ publish_command: publicationCandidateByAsset.get(`${asset.project}:${asset.asset}`)?.publish_command
1068
+ ?? `dna templates publish --org <organization-id> --project <project-id> --template ${asset.asset} --receipt .dna/reports/org-review-receipt.json`,
1069
+ publications_command: publicationCandidateByAsset.get(`${asset.project}:${asset.asset}`)?.publications_command
1070
+ ?? `dna templates publications --org <organization-id> --project <project-id> --template ${asset.asset}`,
1071
+ revoke_command: publicationCandidateByAsset.get(`${asset.project}:${asset.asset}`)?.revoke_command
1072
+ ?? "dna templates revoke --org <organization-id> --project <project-id> --publication-id <publication-id>",
1073
+ dry_run_commands: publicationCandidateByAsset.get(`${asset.project}:${asset.asset}`)?.dry_run_commands
1074
+ ?? [
1075
+ `dna templates publish --org <organization-id> --project <project-id> --template ${asset.asset} --receipt .dna/reports/org-review-receipt.json --dry-run --json`,
1076
+ `dna templates publications --org <organization-id> --project <project-id> --template ${asset.asset} --dry-run --json`,
1077
+ "dna templates revoke --org <organization-id> --project <project-id> --publication-id <publication-id> --dry-run --json",
1078
+ ],
1079
+ local_review_route: publicationCandidateByAsset.get(`${asset.project}:${asset.asset}`)?.local_review_route
1080
+ ?? [
1081
+ `dna assets inspect ${asset.asset} --evidence`,
1082
+ "dna org review",
1083
+ "dna org rollout",
1084
+ asset.syncTargets.some((target) => target === "codex" || target === "codex_cli") ? "dna sync --codex" : "dna sync",
1085
+ ],
1086
+ source_receipt: orgPrivatePublicationPlan.receipt_path_hint,
1087
+ review_refs: publicationCandidateByAsset.get(`${asset.project}:${asset.asset}`)?.review_refs ?? asset.reviewRefs,
1088
+ review_evidence: publicationCandidateByAsset.get(`${asset.project}:${asset.asset}`)?.review_evidence ?? serializedReviewEvidence(asset.reviewEvidence),
1089
+ blockers: orgPrivatePublicationPlan.blockers,
1090
+ reason: "Local org review marks this asset sync-ready; online may project it as an internal-ready adoption candidate only.",
1091
+ }));
1092
+ return {
1093
+ schema_version: "intentdna.org_review_receipt.v1",
1094
+ generated_at: new Date().toISOString(),
1095
+ mode: "local_org_review_receipt",
1096
+ status: review ? managerSummary?.status ?? "review_required" : "missing_profile",
1097
+ cloud_authority: false,
1098
+ organization: review ? {
1099
+ id: review.profile.organization.id,
1100
+ name: review.profile.organization.name,
1101
+ } : undefined,
1102
+ profile_path: profilePath,
1103
+ summary: {
1104
+ projects: review?.totals.projects ?? 0,
1105
+ declared_assets: review?.totals.declaredAssets ?? 0,
1106
+ configured_assets: review?.totals.configuredAssets ?? 0,
1107
+ missing_assets: review?.totals.missingAssets ?? 0,
1108
+ ready_assets: rollout.summary.readyAssets,
1109
+ blocked_assets: rollout.summary.blockedAssets,
1110
+ sync_targets: rollout.summary.syncTargets,
1111
+ internal_ready_candidates: internalReadyCandidates.length,
1112
+ },
1113
+ asset_portfolio_summary: {
1114
+ rollout_maturity: managerSummary?.assetPortfolio.rolloutMaturity ?? "unknown",
1115
+ active_assets: managerSummary?.assetPortfolio.active.length ?? 0,
1116
+ stale_assets: managerSummary?.assetPortfolio.stale.length ?? 0,
1117
+ blocked_assets: managerSummary?.assetPortfolio.blocked.length ?? rollout.summary.blockedAssets,
1118
+ evidence_gaps: managerSummary?.assetPortfolio.evidenceGaps.length ?? 0,
1119
+ upgrade_risks: managerSummary?.assetPortfolio.upgradeRisks.length ?? 0,
1120
+ publish_ready_assets: internalReadyCandidates.length,
1121
+ local_authority: "manager_review_projection_only",
1122
+ boundary: "Asset portfolio summary is local manager review projection only; it does not publish org_private assets, prove SaaS membership, install workflow assets, or authorize runtime sync.",
1123
+ },
1124
+ projects: review?.projects.map((project) => ({
1125
+ id: project.id,
1126
+ name: project.name,
1127
+ path: project.path,
1128
+ declared_assets: project.plannedAssets,
1129
+ configured_assets: project.configuredAssets,
1130
+ missing_assets: project.missingAssets,
1131
+ sync_targets: project.syncTargets,
1132
+ asset_gates: project.assetGates.map((gate) => ({
1133
+ asset: gate.asset,
1134
+ asset_version: gate.assetVersion,
1135
+ content_hash: gate.contentHash,
1136
+ evidence_posture: gate.evidencePosture,
1137
+ review_refs: gate.reviewRefs,
1138
+ review_evidence: gate.reviewEvidence.map((evidence) => ({
1139
+ mode: evidence.mode,
1140
+ authority: evidence.authority,
1141
+ receipt_command: evidence.receiptCommand,
1142
+ receipt_path_hint: evidence.receiptPathHint,
1143
+ boundaries: evidence.boundaries,
1144
+ })),
1145
+ review_command: gate.reviewCommand,
1146
+ binding_status: gate.bindingStatus,
1147
+ configured: gate.configured,
1148
+ configuration_posture: gate.configurationPosture,
1149
+ evidence_gate: gate.evidenceGate,
1150
+ sync_ready: gate.syncReady,
1151
+ sync_targets: gate.syncTargets,
1152
+ next_command: gate.nextCommand,
1153
+ reason: gate.reason,
1154
+ })),
1155
+ })) ?? [],
1156
+ sync_targets: rollout.distributionPlan.targets,
1157
+ asset_gates: rollout.assets,
1158
+ asset_portfolio: managerSummary?.assetPortfolio,
1159
+ unfinished_work: managerSummary?.unfinishedWork,
1160
+ blockers: rollout.blockers,
1161
+ internal_ready_candidates: internalReadyCandidates,
1162
+ org_private_publication_plan: orgPrivatePublicationPlan,
1163
+ next_commands: rollout.nextCommands,
1164
+ guardrails: orgReceiptGuardrails(),
1165
+ };
1166
+ }
1167
+ function writeOrgReceipt(receipt) {
1168
+ process.stderr.write("Organization review receipt\n");
1169
+ process.stderr.write(`Schema: ${receipt.schema_version}\n`);
1170
+ process.stderr.write(`Status: ${receipt.status}\n`);
1171
+ process.stderr.write(`Cloud authority: ${String(receipt.cloud_authority)}\n`);
1172
+ if (receipt.organization)
1173
+ process.stderr.write(`Organization: ${receipt.organization.name}\n`);
1174
+ process.stderr.write(`Projects: ${receipt.summary.projects}\n`);
1175
+ process.stderr.write(`Declared assets: ${receipt.summary.declared_assets}\n`);
1176
+ process.stderr.write(`Ready assets: ${receipt.summary.ready_assets}\n`);
1177
+ process.stderr.write(`Blocked assets: ${receipt.summary.blocked_assets}\n`);
1178
+ process.stderr.write(`Internal-ready candidates: ${receipt.summary.internal_ready_candidates}\n`);
1179
+ process.stderr.write(`Org-private publication plan: ${receipt.org_private_publication_plan.status}\n`);
1180
+ process.stderr.write(`Receipt command: ${receipt.org_private_publication_plan.receipt_command}\n`);
1181
+ process.stderr.write("\nPublication candidates:\n");
1182
+ if (receipt.org_private_publication_plan.candidates.length === 0) {
1183
+ process.stderr.write(" - none\n");
1184
+ }
1185
+ else {
1186
+ for (const candidate of receipt.org_private_publication_plan.candidates) {
1187
+ const version = candidate.asset_version ? ` version=${candidate.asset_version}` : "";
1188
+ process.stderr.write(` - ${candidate.project.name}/${candidate.asset}:${version} publish_ready=${candidate.publish_ready ? "yes" : "no"}\n`);
1189
+ process.stderr.write(` publish: ${candidate.publish_command}\n`);
1190
+ process.stderr.write(` list: ${candidate.publications_command}\n`);
1191
+ process.stderr.write(` revoke: ${candidate.revoke_command}\n`);
1192
+ process.stderr.write(` dry-run: ${candidate.dry_run_commands.join(" | ")}\n`);
1193
+ process.stderr.write(` local route: ${candidate.local_review_route.join(" -> ")}\n`);
1194
+ process.stderr.write(` source receipt: ${candidate.source_receipt}\n`);
1195
+ if (candidate.review_refs.length > 0) {
1196
+ process.stderr.write(` review refs: ${candidate.review_refs.map((ref) => `${ref.label ?? ref.kind}:${ref.path}`).join(" | ")}\n`);
1197
+ }
1198
+ if (candidate.review_evidence.length > 0) {
1199
+ process.stderr.write(` review evidence: ${candidate.review_evidence.map((evidence) => `${evidence.authority}:${evidence.receipt_command}`).join(" | ")}\n`);
1200
+ }
1201
+ }
1202
+ }
1203
+ process.stderr.write("\nPublication blockers:\n");
1204
+ for (const blocker of receipt.org_private_publication_plan.blockers)
1205
+ process.stderr.write(` - ${blocker}\n`);
1206
+ process.stderr.write("\nPublication boundaries:\n");
1207
+ for (const boundary of receipt.org_private_publication_plan.boundaries)
1208
+ process.stderr.write(` - ${boundary}\n`);
1209
+ process.stderr.write("\nGuardrails:\n");
1210
+ for (const guardrail of receipt.guardrails)
1211
+ process.stderr.write(` - ${guardrail}\n`);
1212
+ process.stderr.write("\nNext commands:\n");
1213
+ for (const command of receipt.next_commands.slice(0, 8))
1214
+ process.stderr.write(` - ${command}\n`);
1215
+ }
1216
+ async function buildOrgIntake(opts) {
1217
+ const profile = await readOrganizationProfile({ projectDir: opts.projectDir });
1218
+ const assetName = await preferredAssetName(opts);
1219
+ const asset = await findWorkflowAsset(assetName, { projectDir: opts.projectDir });
1220
+ const evidence = asset?.status.evidencePosture === "dogfood_linked"
1221
+ ? await buildWorkflowAssetEvidenceIndex(assetName, { projectDir: opts.projectDir })
1222
+ : undefined;
1223
+ const evidenceOpen = evidence
1224
+ ? evidence.totals.continue > 0 || evidence.totals.blocked > 0 || evidence.totals.requestChanges > 0 || evidence.totals.remaining > 0
1225
+ : false;
1226
+ const evidenceGate = evidence ? evidenceOpen ? "open" : "clear" : "unknown";
1227
+ const assets = await listWorkflowAssets({ projectDir: opts.projectDir });
1228
+ const followup = evidenceOpen ? activeEvidenceFollowupReviewRoute(assetName, assets, evidence) : undefined;
1229
+ const organization = opts.name?.trim() || profile.profile?.organization.name || "My Org";
1230
+ const project = preferredProjectName(opts);
1231
+ const syncTarget = opts.syncTarget?.trim() || syncTargetFromDescription(opts.description) || "codex";
1232
+ const status = opts.status && isOrganizationAssetStatus(opts.status) ? opts.status : "active";
1233
+ const hasAsset = projectHasAsset(profile.profile, project, assetName);
1234
+ const hasSyncTarget = projectHasSyncTarget(profile.profile, project, syncTarget);
1235
+ const command = !profile.profile
1236
+ ? orgInitCommand({ name: organization, project, asset: assetName, syncTarget })
1237
+ : hasAsset && hasSyncTarget
1238
+ ? "dna org review"
1239
+ : orgBindCommand({ project: opts.project?.trim(), asset: assetName, status, syncTarget });
1240
+ const path = !profile.profile ? "create_profile" : hasAsset && hasSyncTarget ? "review_existing" : "bind_asset";
1241
+ const warnings = [];
1242
+ if (!asset)
1243
+ warnings.push(`Workflow asset is not in the local catalog yet: ${assetName}`);
1244
+ if (profile.profile && !hasAsset)
1245
+ warnings.push(`Organization profile exists, but ${assetName} is not bound to the selected project yet.`);
1246
+ if (profile.profile && hasAsset && !hasSyncTarget)
1247
+ warnings.push(`Asset ${assetName} is bound, but sync target ${syncTarget} is not declared yet.`);
1248
+ const reviewPath = [...new Set([
1249
+ "dna assets library",
1250
+ evidenceOpen ? `dna assets lifecycle ${assetName}` : `dna assets lineage ${assetName}`,
1251
+ ...(followup ? [
1252
+ followup.currentCommand,
1253
+ followup.diffCommand,
1254
+ followup.adoptionCommand,
1255
+ ] : []),
1256
+ command,
1257
+ "dna org review",
1258
+ `dna guide ${assetName} --brief`,
1259
+ ])];
1260
+ return {
1261
+ mode: "organization_intake",
1262
+ input: {
1263
+ description: opts.description?.trim() || undefined,
1264
+ organization,
1265
+ project,
1266
+ asset: assetName,
1267
+ syncTarget,
1268
+ },
1269
+ current: {
1270
+ profilePath: profile.path,
1271
+ configured: profile.profile !== undefined,
1272
+ organization: profile.profile?.organization.name,
1273
+ projects: profile.profile?.projects.length ?? 0,
1274
+ syncTargets: profileSyncTargets(profile.profile),
1275
+ },
1276
+ asset: asset ? {
1277
+ name: asset.name,
1278
+ status: asset.status.managementStatus,
1279
+ evidence: asset.status.evidencePosture,
1280
+ cascade: asset.status.cascadePosture,
1281
+ evolution: asset.status.evolutionPosture,
1282
+ configured: asset.status.configured,
1283
+ positioning: asset.positioning,
1284
+ } : undefined,
1285
+ recommendation: {
1286
+ path,
1287
+ command,
1288
+ reason: path === "create_profile"
1289
+ ? "No organization profile exists yet, so create the local organization/project binding before adoption or sync."
1290
+ : path === "bind_asset"
1291
+ ? "An organization profile exists, but the selected asset or sync target still needs to be bound."
1292
+ : "The selected asset and sync target are already represented; review the current system state next.",
1293
+ writes: path !== "review_existing",
1294
+ },
1295
+ setupPlan: {
1296
+ principle: "Organization setup is a reviewable route from asset choice to manager gates and explicit runtime distribution.",
1297
+ phases: [
1298
+ {
1299
+ id: "asset",
1300
+ label: "Select workflow asset",
1301
+ command: `dna assets lineage ${assetName}`,
1302
+ purpose: "Understand asset origin, cascade, evidence, and evolution posture before binding it to a project.",
1303
+ writes: false,
1304
+ owner: "user",
1305
+ },
1306
+ {
1307
+ id: "profile",
1308
+ label: path === "create_profile"
1309
+ ? "Create organization profile"
1310
+ : path === "bind_asset"
1311
+ ? "Bind asset to organization"
1312
+ : "Review existing organization",
1313
+ command,
1314
+ purpose: path === "review_existing"
1315
+ ? "Confirm the existing organization profile already represents this asset and sync target."
1316
+ : "Record the organization/project/asset/sync-target relationship locally before runtime sync.",
1317
+ writes: path !== "review_existing",
1318
+ owner: "manager",
1319
+ },
1320
+ {
1321
+ id: "configure",
1322
+ label: evidenceOpen ? "Save unfinished-work draft" : "Configure project asset",
1323
+ command: followup?.currentCommand ?? `dna init --template ${assetName}`,
1324
+ purpose: evidenceOpen
1325
+ ? "Continue the reviewed unfinished-work draft route from open dogfood evidence before configuring, rollout, runtime sync, or marker mutation; this does not replace the source asset automatically."
1326
+ : "Create the project DNA config for the selected asset before runtime artifacts are generated.",
1327
+ writes: followup?.currentWrites ?? true,
1328
+ owner: "dna_core",
1329
+ },
1330
+ {
1331
+ id: "review",
1332
+ label: "Review system state",
1333
+ command: "dna org review",
1334
+ purpose: "Show configured assets, evidence gates, sync readiness, and next actions for managers.",
1335
+ writes: false,
1336
+ owner: "manager",
1337
+ },
1338
+ {
1339
+ id: "rollout",
1340
+ label: "Check rollout readiness",
1341
+ command: "dna org rollout",
1342
+ purpose: "Produce target-level ready/partial/blocked distribution plans before any sync command.",
1343
+ writes: false,
1344
+ owner: "manager",
1345
+ },
1346
+ {
1347
+ id: "sync",
1348
+ label: "Sync harness surface",
1349
+ command: rolloutCommandForTarget(syncTarget),
1350
+ purpose: "Generate Codex or Claude runtime artifacts only after organization gates pass.",
1351
+ writes: true,
1352
+ owner: "runtime",
1353
+ },
1354
+ {
1355
+ id: "deploy",
1356
+ label: "Deploy public surface",
1357
+ command: "dna release deploy-plan",
1358
+ purpose: "Plan Vercel CLI deployment and deployed URL verification only when web, dashboard, blog, or other public deployed surfaces changed.",
1359
+ writes: false,
1360
+ owner: "release",
1361
+ },
1362
+ ],
1363
+ boundaries: [
1364
+ "Organization intake is read-only until the recommended command is explicitly run.",
1365
+ "dna CLI/core/runtime owns organization bindings and governance decisions.",
1366
+ "Claude Code and Codex are sync targets, not places where governance truth is edited.",
1367
+ "Obsidian/wiki remains internal project memory and does not define product runtime state.",
1368
+ "Vercel deployment is separate from org rollout and is required only for changed web/public deployed surfaces.",
1369
+ ],
1370
+ },
1371
+ entryPacket: buildOrgEntryPacket({
1372
+ path,
1373
+ command,
1374
+ assetName,
1375
+ syncTarget,
1376
+ evidenceGate,
1377
+ followupCommand: followup?.currentCommand,
1378
+ reviewPath,
1379
+ }),
1380
+ reviewPath,
1381
+ mutationBoundary: "Read-only organization intake; no .dna/org.yaml, asset binding, runtime sync, dashboard state, or policy truth is changed.",
1382
+ warnings,
1383
+ nextCommands: [...new Set([
1384
+ command,
1385
+ evidenceOpen ? `dna assets lifecycle ${assetName}` : `dna assets lineage ${assetName}`,
1386
+ ...(followup ? [followup.currentCommand] : []),
1387
+ "dna org review",
1388
+ `dna guide ${assetName} --brief`,
1389
+ ])],
1390
+ };
1391
+ }
1392
+ function buildOrgRolloutPlan(profilePath, review) {
1393
+ if (!review) {
1394
+ return {
1395
+ mode: "organization_rollout_plan",
1396
+ profilePath,
1397
+ status: "missing_profile",
1398
+ operatingRoute: buildOrgOperatingRoute({
1399
+ currentStage: "organize",
1400
+ currentCommand: `dna org intake --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex`,
1401
+ }),
1402
+ summary: {
1403
+ projects: 0,
1404
+ declaredAssets: 0,
1405
+ readyAssets: 0,
1406
+ blockedAssets: 0,
1407
+ syncTargets: [],
1408
+ },
1409
+ assets: [],
1410
+ distributionPlan: {
1411
+ targets: [],
1412
+ deploymentBoundary: "Vercel CLI deployment is separate from workflow runtime rollout and is required only when web/public deployed surfaces changed.",
1413
+ guidance: [
1414
+ "Create an organization profile before runtime distribution.",
1415
+ "Use dna org rollout again before any sync command.",
1416
+ ],
1417
+ },
1418
+ releaseGate: buildOrgReleaseGate(),
1419
+ rolloutCommands: [],
1420
+ blockers: ["No organization profile exists, so there is no governed asset distribution plan yet."],
1421
+ nextCommands: [
1422
+ `dna org intake --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex`,
1423
+ `dna org init --name <organization> --project <project> --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex`,
1424
+ ],
1425
+ mutationBoundary: "Read-only rollout plan; no organization profile, project config, runtime sync, dashboard state, or policy truth is changed.",
1426
+ };
1427
+ }
1428
+ const assets = review.projects.flatMap((project) => project.assetGates.map((gate) => ({
1429
+ project: project.name,
1430
+ asset: gate.asset,
1431
+ assetVersion: gate.assetVersion,
1432
+ contentHash: gate.contentHash,
1433
+ evidencePosture: gate.evidencePosture,
1434
+ reviewRefs: gate.reviewRefs,
1435
+ reviewEvidence: gate.reviewEvidence,
1436
+ reviewCommand: gate.reviewCommand,
1437
+ bindingStatus: gate.bindingStatus,
1438
+ configured: gate.configured,
1439
+ evidenceGate: gate.evidenceGate,
1440
+ syncReady: gate.syncReady,
1441
+ syncTargets: gate.syncTargets,
1442
+ nextCommand: gate.nextCommand,
1443
+ reason: gate.reason,
1444
+ })));
1445
+ const syncTargets = [...new Set(review.projects.flatMap((project) => project.syncTargets))];
1446
+ const readyAssets = assets.filter((asset) => asset.syncReady);
1447
+ const blockedAssets = assets.filter((asset) => !asset.syncReady);
1448
+ const targetPlans = buildDistributionPlan(syncTargets, assets);
1449
+ const rolloutCommands = [...new Set(targetPlans
1450
+ .filter((target) => target.status === "ready")
1451
+ .map((target) => target.command))];
1452
+ const blockers = [
1453
+ ...blockedAssets.map((asset) => `${asset.project}/${asset.asset}: ${asset.reason} Next: ${asset.nextCommand}`),
1454
+ ...review.projects.filter((project) => project.syncTargets.length === 0).map((project) => `${project.name}: no sync target declared.`),
1455
+ ];
1456
+ const hasOpenEvidence = blockedAssets.some((asset) => asset.evidenceGate === "open");
1457
+ const hasMissingConfiguredAssets = blockedAssets.some((asset) => asset.evidenceGate !== "open" &&
1458
+ !asset.configured &&
1459
+ asset.nextCommand.startsWith("dna init --template "));
1460
+ const hasInactiveBindings = blockedAssets.some((asset) => asset.evidenceGate !== "open" &&
1461
+ asset.configured &&
1462
+ asset.nextCommand.startsWith("dna org bind "));
1463
+ const evidenceFirstBlockedAssets = [...blockedAssets].sort((a, b) => orgRouteAssetRank(a) - orgRouteAssetRank(b) || a.asset.localeCompare(b.asset));
1464
+ const nextCommands = blockers.length > 0
1465
+ ? [...new Set(hasOpenEvidence
1466
+ ? evidenceFirstBlockedAssets.map((asset) => asset.nextCommand).concat(hasMissingConfiguredAssets ? "dna org configure-plan" : [], "dna org review")
1467
+ : hasMissingConfiguredAssets
1468
+ ? ["dna org configure-plan", "dna org configure-plan --apply", "dna org review"]
1469
+ : blockedAssets.map((asset) => asset.nextCommand).concat("dna org review"))]
1470
+ : [...new Set([...rolloutCommands, "dna org review"])];
1471
+ const currentCommand = nextCommands[0] ?? rolloutCommands[0] ?? "dna org review";
1472
+ const currentStage = blockers.length > 0
1473
+ ? hasOpenEvidence
1474
+ ? "improve"
1475
+ : hasMissingConfiguredAssets
1476
+ ? "configure"
1477
+ : hasInactiveBindings
1478
+ ? "organize"
1479
+ : "improve"
1480
+ : "sync";
1481
+ const routePrimary = [...assets].sort((a, b) => orgRouteAssetRank(a) - orgRouteAssetRank(b) || a.asset.localeCompare(b.asset))[0];
1482
+ const routeAsset = routePrimary?.asset ?? blockedAssets[0]?.asset ?? readyAssets[0]?.asset ?? WORKFLOW_ASSET_IDS.default;
1483
+ const routeFollowup = activeEvidenceFollowupRoute(routeAsset);
1484
+ const routeNextAsset = hasOpenEvidence && routePrimary?.nextCommand !== `dna assets suggestions ${routeAsset}`
1485
+ ? routeFollowup.assetName
1486
+ : undefined;
1487
+ const routeTarget = routePrimary?.syncTargets[0] ?? blockedAssets[0]?.syncTargets[0] ?? readyAssets[0]?.syncTargets[0] ?? syncTargets[0] ?? "codex";
1488
+ return {
1489
+ mode: "organization_rollout_plan",
1490
+ profilePath: review.profilePath,
1491
+ organization: review.profile.organization.name,
1492
+ status: blockers.length > 0 || readyAssets.length === 0 ? "blocked" : "ready",
1493
+ operatingRoute: buildOrgOperatingRoute({
1494
+ asset: routeAsset,
1495
+ nextAsset: routeNextAsset,
1496
+ syncTarget: routeTarget,
1497
+ currentStage,
1498
+ currentCommand,
1499
+ }),
1500
+ summary: {
1501
+ projects: review.totals.projects,
1502
+ declaredAssets: review.totals.declaredAssets,
1503
+ readyAssets: readyAssets.length,
1504
+ blockedAssets: blockedAssets.length,
1505
+ syncTargets,
1506
+ },
1507
+ assets,
1508
+ distributionPlan: {
1509
+ targets: targetPlans,
1510
+ deploymentBoundary: "Vercel CLI deployment is separate from workflow runtime rollout and is required only when web/public deployed surfaces changed.",
1511
+ guidance: [
1512
+ "Run only rollout commands whose target status is ready.",
1513
+ "Resolve blocked assets before broad organization distribution.",
1514
+ "Dashboard or blog/public surface changes require Vercel CLI deployment and deployed URL verification; CLI/core/runtime rollout does not.",
1515
+ ],
1516
+ },
1517
+ releaseGate: buildOrgReleaseGate(),
1518
+ rolloutCommands,
1519
+ blockers,
1520
+ nextCommands,
1521
+ mutationBoundary: "Read-only rollout plan; no organization profile, project config, runtime sync, dashboard state, or policy truth is changed.",
1522
+ };
1523
+ }
1524
+ function buildOrgConfigurePlan(profilePath, review) {
1525
+ if (!review) {
1526
+ return {
1527
+ mode: "organization_configure_plan",
1528
+ profilePath,
1529
+ status: "missing_profile",
1530
+ summary: {
1531
+ projects: 0,
1532
+ declaredAssets: 0,
1533
+ missingAssets: 0,
1534
+ configuredAssets: 0,
1535
+ },
1536
+ items: [],
1537
+ commands: [],
1538
+ afterAll: [
1539
+ `dna org intake --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex`,
1540
+ `dna org init --name <organization> --project <project> --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex`,
1541
+ ],
1542
+ mutationBoundary: "Read-only configuration plan; no organization profile, project config, runtime sync, dashboard state, or policy truth is changed.",
1543
+ };
1544
+ }
1545
+ const items = review.projects.flatMap((project) => project.assetGates
1546
+ .filter((gate) => !gate.configured && gate.configurationPosture !== "missing")
1547
+ .map((gate) => {
1548
+ const action = gate.evidenceGate === "open" ? "followup" : "configure";
1549
+ return {
1550
+ action,
1551
+ project: project.name,
1552
+ asset: gate.asset,
1553
+ bindingStatus: gate.bindingStatus,
1554
+ configured: gate.configured,
1555
+ evidenceGate: gate.evidenceGate,
1556
+ command: action === "followup" ? gate.nextCommand : `dna init --template ${gate.asset}`,
1557
+ afterConfigure: action === "followup"
1558
+ ? [
1559
+ `dna assets inspect ${gate.asset}-active-followup --evidence`,
1560
+ `dna assets adoption ${gate.asset}-active-followup`,
1561
+ "dna org rollout",
1562
+ "dna org review",
1563
+ ]
1564
+ : [
1565
+ `dna assets governance ${gate.asset}`,
1566
+ `dna assets adoption ${gate.asset}`,
1567
+ "dna org rollout",
1568
+ "dna org review",
1569
+ ],
1570
+ writes: action === "configure",
1571
+ reason: action === "followup"
1572
+ ? "Dogfood evidence is open, so save and review an unfinished-work draft before configuring this older asset version."
1573
+ : gate.reason,
1574
+ };
1575
+ }));
1576
+ const commands = [...new Set(items.filter((item) => item.writes).map((item) => item.command))];
1577
+ return {
1578
+ mode: "organization_configure_plan",
1579
+ profilePath: review.profilePath,
1580
+ organization: review.profile.organization.name,
1581
+ status: items.length > 0 ? "ready" : "nothing_to_configure",
1582
+ summary: {
1583
+ projects: review.totals.projects,
1584
+ declaredAssets: review.totals.declaredAssets,
1585
+ missingAssets: review.totals.missingAssets,
1586
+ configuredAssets: review.totals.configuredAssets,
1587
+ },
1588
+ items,
1589
+ commands,
1590
+ afterAll: items.length > 0
1591
+ ? ["dna org rollout", "dna org review"]
1592
+ : ["dna org review", "dna guide --brief"],
1593
+ mutationBoundary: "Read-only configuration plan; it lists explicit dna init commands but does not write .dna/configs, sync runtime artifacts, mutate dashboard state, or change policy truth.",
1594
+ };
1595
+ }
1596
+ function buildOrgStatusPacket(profilePath, review) {
1597
+ if (!review) {
1598
+ const primaryCommand = `dna org intake --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex`;
1599
+ return {
1600
+ mode: "organization_status",
1601
+ profilePath,
1602
+ configured: false,
1603
+ status: "missing_profile",
1604
+ current: {
1605
+ projects: 0,
1606
+ declaredAssets: 0,
1607
+ configuredAssets: 0,
1608
+ missingAssets: 0,
1609
+ syncTargets: [],
1610
+ },
1611
+ primaryCommand,
1612
+ operatingRoute: buildOrgOperatingRoute({
1613
+ currentStage: "organize",
1614
+ currentCommand: primaryCommand,
1615
+ }),
1616
+ firstRunPath: [
1617
+ {
1618
+ id: "preview",
1619
+ label: "Preview route",
1620
+ command: primaryCommand,
1621
+ writes: false,
1622
+ purpose: "Inspect the recommended organization, project, asset, and sync target route before writing state.",
1623
+ },
1624
+ {
1625
+ id: "create",
1626
+ label: "Create profile",
1627
+ command: `dna org init --name <organization> --project <project> --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex`,
1628
+ writes: true,
1629
+ purpose: "Create the local .dna/org.yaml route after the preview looks correct.",
1630
+ },
1631
+ {
1632
+ id: "review",
1633
+ label: "Review system",
1634
+ command: "dna org review",
1635
+ writes: false,
1636
+ purpose: "Review declared workflow assets, evidence gates, sync readiness, and manager next actions.",
1637
+ },
1638
+ {
1639
+ id: "navigate",
1640
+ label: "Navigate route",
1641
+ command: "dna guide --brief",
1642
+ writes: false,
1643
+ purpose: "Show the simple user-facing route from workflow description to asset review, rollout, sync, and deploy gate.",
1644
+ },
1645
+ ],
1646
+ releaseGate: buildOrgReleaseGate(),
1647
+ boundaries: [
1648
+ "org status is read-only and does not create organization state.",
1649
+ "Obsidian/wiki is internal memory, not organization state or product runtime truth.",
1650
+ "Dashboard is a read-only projection and should not become the workflow builder or policy source of truth.",
1651
+ "Claude Code and Codex are sync targets; dna owns the governance route.",
1652
+ "Runtime sync waits for org review and rollout readiness.",
1653
+ ],
1654
+ nextCommands: [
1655
+ primaryCommand,
1656
+ `dna org init --name <organization> --project <project> --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex`,
1657
+ "dna guide --brief",
1658
+ ],
1659
+ mutationBoundary: "Read-only organization status; no .dna/org.yaml, workflow asset, runtime sync, dashboard state, deploy, or policy truth is changed.",
1660
+ };
1661
+ }
1662
+ const managerSummary = buildManagerSummary(review);
1663
+ return {
1664
+ mode: "organization_status",
1665
+ profilePath: review.profilePath,
1666
+ configured: true,
1667
+ status: managerSummary.status,
1668
+ current: {
1669
+ organization: review.profile.organization.name,
1670
+ projects: review.totals.projects,
1671
+ declaredAssets: review.totals.declaredAssets,
1672
+ configuredAssets: review.totals.configuredAssets,
1673
+ missingAssets: review.totals.missingAssets,
1674
+ syncTargets: [...new Set(review.projects.flatMap((project) => project.syncTargets))],
1675
+ },
1676
+ primaryCommand: managerSummary.primaryCommand,
1677
+ operatingRoute: managerSummary.operatingRoute,
1678
+ managerSummary,
1679
+ releaseGate: managerSummary.releaseGate,
1680
+ boundaries: managerSummary.operatingRoute.boundaries,
1681
+ nextCommands: managerSummary.nextCommands,
1682
+ mutationBoundary: "Read-only organization status; use dna org review for full manager details and dna org rollout before runtime sync.",
1683
+ };
1684
+ }
1685
+ async function applyOrgConfigurePlan(plan, projectDir) {
1686
+ if (plan.status === "missing_profile") {
1687
+ plan.apply = {
1688
+ requested: true,
1689
+ exitCode: 2,
1690
+ results: [],
1691
+ nextCommands: plan.afterAll,
1692
+ };
1693
+ return 2;
1694
+ }
1695
+ const uniqueItems = [...new Map(plan.items.filter((item) => item.writes).map((item) => [item.asset, item])).values()];
1696
+ const results = [];
1697
+ for (const item of uniqueItems) {
1698
+ try {
1699
+ const result = await createConfigFromTemplate({
1700
+ template: item.asset,
1701
+ projectDir,
1702
+ });
1703
+ results.push({
1704
+ project: item.project,
1705
+ asset: item.asset,
1706
+ command: item.command,
1707
+ status: "created",
1708
+ path: result.path,
1709
+ namespace: result.namespace,
1710
+ });
1711
+ }
1712
+ catch (err) {
1713
+ results.push({
1714
+ project: item.project,
1715
+ asset: item.asset,
1716
+ command: item.command,
1717
+ status: "failed",
1718
+ error: err instanceof Error ? err.message : String(err),
1719
+ });
1720
+ }
1721
+ }
1722
+ const exitCode = results.some((result) => result.status === "failed") ? 1 : 0;
1723
+ plan.apply = {
1724
+ requested: true,
1725
+ exitCode,
1726
+ results,
1727
+ nextCommands: exitCode === 0 ? ["dna org rollout", "dna org review"] : ["dna org configure-plan", "dna org review"],
1728
+ };
1729
+ return exitCode;
1730
+ }
1731
+ function writeProfileMissing(path) {
1732
+ process.stderr.write("No local organization profile found.\n");
1733
+ process.stderr.write(`Expected: ${path}\n`);
1734
+ process.stderr.write("Start with a read-only setup preview before writing .dna/org.yaml.\n");
1735
+ process.stderr.write("\nFirst-run path:\n");
1736
+ process.stderr.write(` - preview: dna org intake --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex\n`);
1737
+ process.stderr.write(` - create: dna org init --name <organization> --project <project> --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex\n`);
1738
+ process.stderr.write(" - review: dna org review\n");
1739
+ process.stderr.write(" - navigate: dna guide --brief\n");
1740
+ process.stderr.write("\nBoundaries:\n");
1741
+ process.stderr.write(" - org status is read-only and does not create organization state.\n");
1742
+ process.stderr.write(" - Obsidian/wiki is internal memory, not organization state.\n");
1743
+ process.stderr.write(" - Runtime sync waits for org review and rollout readiness.\n");
1744
+ }
1745
+ function writeOrgStatus(packet) {
1746
+ process.stderr.write("Organization status\n");
1747
+ process.stderr.write(`Mode: ${packet.mode}\n`);
1748
+ process.stderr.write(`Profile: ${packet.configured ? "configured" : "missing"} (${packet.profilePath})\n`);
1749
+ if (packet.current.organization)
1750
+ process.stderr.write(`Organization: ${packet.current.organization}\n`);
1751
+ process.stderr.write(`Status: ${packet.status}\n`);
1752
+ process.stderr.write(`Projects: ${packet.current.projects}\n`);
1753
+ process.stderr.write(`Declared assets: ${packet.current.declaredAssets}\n`);
1754
+ process.stderr.write(`Configured assets: ${packet.current.configuredAssets}\n`);
1755
+ process.stderr.write(`Missing assets: ${packet.current.missingAssets}\n`);
1756
+ process.stderr.write(`Sync targets: ${packet.current.syncTargets.length > 0 ? packet.current.syncTargets.join(", ") : "none"}\n`);
1757
+ process.stderr.write(`Primary command: ${packet.primaryCommand}\n`);
1758
+ process.stderr.write(`Mutation boundary: ${packet.mutationBoundary}\n`);
1759
+ writeOperatingRoute(packet.operatingRoute);
1760
+ if (packet.firstRunPath) {
1761
+ process.stderr.write("\nFirst-run path:\n");
1762
+ for (const step of packet.firstRunPath) {
1763
+ process.stderr.write(` - ${step.id}. ${step.label} writes=${step.writes ? "yes" : "no"}\n`);
1764
+ process.stderr.write(` command: ${step.command}\n`);
1765
+ process.stderr.write(` purpose: ${step.purpose}\n`);
1766
+ }
1767
+ }
1768
+ if (packet.managerSummary) {
1769
+ process.stderr.write("\nManager summary:\n");
1770
+ process.stderr.write(` Status: ${packet.managerSummary.status}\n`);
1771
+ process.stderr.write(` Decision: ${packet.managerSummary.decision}\n`);
1772
+ process.stderr.write(` Headline: ${packet.managerSummary.headline}\n`);
1773
+ process.stderr.write(` Primary command: ${packet.managerSummary.primaryCommand}\n`);
1774
+ process.stderr.write(` Dogfood evidence: ${packet.managerSummary.dogfoodEvidence.status} (${packet.managerSummary.dogfoodEvidence.pass}/${packet.managerSummary.dogfoodEvidence.modules} PASS, remaining=${packet.managerSummary.dogfoodEvidence.remaining})\n`);
1775
+ process.stderr.write(` Portfolio maturity: ${packet.managerSummary.assetPortfolio.rolloutMaturity}\n`);
1776
+ process.stderr.write(` Active assets: ${packet.managerSummary.assetPortfolio.active.length}\n`);
1777
+ process.stderr.write(` Stale assets: ${packet.managerSummary.assetPortfolio.stale.length}\n`);
1778
+ process.stderr.write(` Blocked assets: ${packet.managerSummary.assetPortfolio.blocked.length}\n`);
1779
+ process.stderr.write(` Evidence gaps: ${packet.managerSummary.assetPortfolio.evidenceGaps.length}\n`);
1780
+ process.stderr.write(` Upgrade risks: ${packet.managerSummary.assetPortfolio.upgradeRisks.length}\n`);
1781
+ writeAssetPortfolioGuide(" ");
1782
+ }
1783
+ process.stderr.write("\nBoundaries:\n");
1784
+ for (const boundary of packet.boundaries)
1785
+ process.stderr.write(` - ${boundary}\n`);
1786
+ process.stderr.write("\nNext commands:\n");
1787
+ for (const command of packet.nextCommands.slice(0, 8))
1788
+ process.stderr.write(` - ${command}\n`);
1789
+ process.stderr.write("\n");
1790
+ writeReleaseGate(packet.releaseGate);
1791
+ }
1792
+ function writeConfigurePlan(plan) {
1793
+ process.stderr.write("Organization configure plan\n");
1794
+ process.stderr.write(`Mode: ${plan.mode}\n`);
1795
+ process.stderr.write(`Profile: ${plan.profilePath}\n`);
1796
+ if (plan.organization)
1797
+ process.stderr.write(`Organization: ${plan.organization}\n`);
1798
+ process.stderr.write(`Status: ${plan.status}\n`);
1799
+ process.stderr.write(`Projects: ${plan.summary.projects}\n`);
1800
+ process.stderr.write(`Declared assets: ${plan.summary.declaredAssets}\n`);
1801
+ process.stderr.write(`Configured assets: ${plan.summary.configuredAssets}\n`);
1802
+ process.stderr.write(`Missing configured assets: ${plan.summary.missingAssets}\n`);
1803
+ process.stderr.write(`Mutation boundary: ${plan.mutationBoundary}\n`);
1804
+ process.stderr.write("\nAssets to configure:\n");
1805
+ if (plan.items.length === 0) {
1806
+ process.stderr.write(" - none\n");
1807
+ }
1808
+ else {
1809
+ for (const item of plan.items) {
1810
+ process.stderr.write(` - ${item.project}/${item.asset}: binding=${item.bindingStatus} evidence=${item.evidenceGate}\n`);
1811
+ process.stderr.write(` action: ${item.action}\n`);
1812
+ process.stderr.write(` command: ${item.command}\n`);
1813
+ process.stderr.write(` writes: ${item.writes ? "yes" : "no"}\n`);
1814
+ process.stderr.write(` reason: ${item.reason}\n`);
1815
+ process.stderr.write(` after ${item.action}:\n`);
1816
+ for (const command of item.afterConfigure)
1817
+ process.stderr.write(` - ${command}\n`);
1818
+ }
1819
+ }
1820
+ process.stderr.write("\nConfiguration commands:\n");
1821
+ if (plan.commands.length === 0) {
1822
+ process.stderr.write(" - none\n");
1823
+ }
1824
+ else {
1825
+ for (const command of plan.commands)
1826
+ process.stderr.write(` - ${command}\n`);
1827
+ }
1828
+ process.stderr.write("\nAfter all configured:\n");
1829
+ for (const command of plan.afterAll)
1830
+ process.stderr.write(` - ${command}\n`);
1831
+ if (plan.apply) {
1832
+ process.stderr.write("\nApply results:\n");
1833
+ if (plan.apply.results.length === 0) {
1834
+ process.stderr.write(" - none\n");
1835
+ }
1836
+ else {
1837
+ for (const result of plan.apply.results) {
1838
+ process.stderr.write(` - ${result.project}/${result.asset}: ${result.status}\n`);
1839
+ process.stderr.write(` command: ${result.command}\n`);
1840
+ if (result.path)
1841
+ process.stderr.write(` path: ${result.path}\n`);
1842
+ if (result.namespace)
1843
+ process.stderr.write(` namespace: ${result.namespace}\n`);
1844
+ if (result.error)
1845
+ process.stderr.write(` error: ${result.error}\n`);
1846
+ }
1847
+ }
1848
+ process.stderr.write(` exit_code: ${plan.apply.exitCode}\n`);
1849
+ process.stderr.write(" next commands:\n");
1850
+ for (const command of plan.apply.nextCommands)
1851
+ process.stderr.write(` - ${command}\n`);
1852
+ }
1853
+ }
1854
+ function writeOrgIntake(packet) {
1855
+ process.stderr.write("Organization intake\n");
1856
+ process.stderr.write(`Mode: ${packet.mode}\n`);
1857
+ if (packet.input.description)
1858
+ process.stderr.write(`Description: ${packet.input.description}\n`);
1859
+ process.stderr.write(`Organization: ${packet.input.organization}\n`);
1860
+ process.stderr.write(`Project: ${packet.input.project}\n`);
1861
+ process.stderr.write(`Workflow asset: ${packet.input.asset}\n`);
1862
+ process.stderr.write(`Sync target: ${packet.input.syncTarget}\n`);
1863
+ process.stderr.write(`Profile: ${packet.current.configured ? "configured" : "missing"} (${packet.current.profilePath})\n`);
1864
+ process.stderr.write(`Recommended path: ${packet.recommendation.path}\n`);
1865
+ process.stderr.write(`Recommended command: ${packet.recommendation.command}\n`);
1866
+ process.stderr.write(`Writes: ${packet.recommendation.writes ? "yes" : "no"}\n`);
1867
+ process.stderr.write(`Reason: ${packet.recommendation.reason}\n`);
1868
+ process.stderr.write(`Mutation boundary: ${packet.mutationBoundary}\n`);
1869
+ process.stderr.write("\nEntry packet:\n");
1870
+ process.stderr.write(` current: ${packet.entryPacket.currentState}\n`);
1871
+ process.stderr.write(` decision: ${packet.entryPacket.decision}\n`);
1872
+ process.stderr.write(` evidence gate: ${packet.entryPacket.evidenceGate}\n`);
1873
+ process.stderr.write(` primary action: ${packet.entryPacket.primaryAction}\n`);
1874
+ process.stderr.write(` reason: ${packet.entryPacket.reason}\n`);
1875
+ process.stderr.write(" allowed now:\n");
1876
+ for (const item of packet.entryPacket.allowedNow)
1877
+ process.stderr.write(` - ${item}\n`);
1878
+ process.stderr.write(" blocked now:\n");
1879
+ for (const item of packet.entryPacket.blockedNow)
1880
+ process.stderr.write(` - ${item}\n`);
1881
+ process.stderr.write(" review path:\n");
1882
+ for (const command of packet.entryPacket.reviewPath)
1883
+ process.stderr.write(` - ${command}\n`);
1884
+ process.stderr.write("\nSetup plan:\n");
1885
+ process.stderr.write(` - principle: ${packet.setupPlan.principle}\n`);
1886
+ for (const phase of packet.setupPlan.phases) {
1887
+ process.stderr.write(` - ${phase.id}. ${phase.label}\n`);
1888
+ process.stderr.write(` command: ${phase.command}\n`);
1889
+ process.stderr.write(` owner: ${phase.owner}\n`);
1890
+ process.stderr.write(` writes: ${phase.writes ? "yes" : "no"}\n`);
1891
+ process.stderr.write(` purpose: ${phase.purpose}\n`);
1892
+ }
1893
+ process.stderr.write(" - boundaries:\n");
1894
+ for (const boundary of packet.setupPlan.boundaries)
1895
+ process.stderr.write(` - ${boundary}\n`);
1896
+ if (packet.asset) {
1897
+ process.stderr.write("\nAsset posture:\n");
1898
+ process.stderr.write(` - ${packet.asset.name}: ${packet.asset.status}, evidence=${packet.asset.evidence}, cascade=${packet.asset.cascade}, evolution=${packet.asset.evolution}, configured=${packet.asset.configured ? "yes" : "no"}\n`);
1899
+ if (packet.asset.positioning)
1900
+ process.stderr.write(` why: ${packet.asset.positioning}\n`);
1901
+ }
1902
+ if (packet.warnings.length > 0) {
1903
+ process.stderr.write("\nWarnings:\n");
1904
+ for (const warning of packet.warnings)
1905
+ process.stderr.write(` - ${warning}\n`);
1906
+ }
1907
+ process.stderr.write("\nReview path:\n");
1908
+ for (const command of packet.reviewPath)
1909
+ process.stderr.write(` - ${command}\n`);
1910
+ process.stderr.write("\nNext commands:\n");
1911
+ for (const command of packet.nextCommands)
1912
+ process.stderr.write(` - ${command}\n`);
1913
+ }
1914
+ function writeOperatingRoute(route) {
1915
+ process.stderr.write("\nOperating route:\n");
1916
+ process.stderr.write(` - spine: ${route.spine.join(" -> ")}\n`);
1917
+ process.stderr.write(` - current: ${route.currentStage}\n`);
1918
+ process.stderr.write(` - command: ${route.currentCommand}\n`);
1919
+ process.stderr.write(` - principle: ${route.principle}\n`);
1920
+ process.stderr.write(" - boundaries:\n");
1921
+ for (const boundary of route.boundaries)
1922
+ process.stderr.write(` - ${boundary}\n`);
1923
+ }
1924
+ function writeReleaseGate(gate, indent = "") {
1925
+ process.stderr.write(`${indent}Release gate:\n`);
1926
+ process.stderr.write(`${indent} status: ${gate.status}\n`);
1927
+ process.stderr.write(`${indent} command: ${gate.command}\n`);
1928
+ process.stderr.write(`${indent} deploy and verify when: ${gate.deployAndVerifyWhen}\n`);
1929
+ process.stderr.write(`${indent} required when:\n`);
1930
+ for (const item of gate.requiredWhen)
1931
+ process.stderr.write(`${indent} - ${item}\n`);
1932
+ process.stderr.write(`${indent} not required for:\n`);
1933
+ for (const item of gate.notRequiredFor)
1934
+ process.stderr.write(`${indent} - ${item}\n`);
1935
+ process.stderr.write(`${indent} verification:\n`);
1936
+ for (const item of gate.verification)
1937
+ process.stderr.write(`${indent} - ${item}\n`);
1938
+ process.stderr.write(`${indent} boundary: ${gate.boundary}\n`);
1939
+ }
1940
+ function writeAssetPortfolioGuide(indent = "") {
1941
+ process.stderr.write(`${indent}Asset portfolio guide:\n`);
1942
+ process.stderr.write(`${indent} - active: asset currently bound for manager review; still needs clear evidence and rollout before runtime distribution.\n`);
1943
+ process.stderr.write(`${indent} - stale: older or non-active candidate for the same project; compare before promotion, pause, or removal.\n`);
1944
+ process.stderr.write(`${indent} - blocked: asset cannot roll out or sync yet; follow its next command and review command.\n`);
1945
+ process.stderr.write(`${indent} - evidence gaps: missing or open evidence that must be reviewed before broad rollout.\n`);
1946
+ process.stderr.write(`${indent} - upgrade risks: version or follow-up changes that need explicit diff/adoption review.\n`);
1947
+ process.stderr.write(`${indent} - boundary: this is local dna manager review only; it does not create org_private SaaS visibility, publish templates, install assets, sync runtime artifacts, or approve workflow execution.\n`);
1948
+ }
1949
+ function writeRolloutPlan(plan) {
1950
+ process.stderr.write("Organization rollout plan\n");
1951
+ process.stderr.write(`Mode: ${plan.mode}\n`);
1952
+ process.stderr.write(`Profile: ${plan.profilePath}\n`);
1953
+ if (plan.organization)
1954
+ process.stderr.write(`Organization: ${plan.organization}\n`);
1955
+ process.stderr.write(`Status: ${plan.status}\n`);
1956
+ process.stderr.write(`Projects: ${plan.summary.projects}\n`);
1957
+ process.stderr.write(`Declared assets: ${plan.summary.declaredAssets}\n`);
1958
+ process.stderr.write(`Ready assets: ${plan.summary.readyAssets}\n`);
1959
+ process.stderr.write(`Blocked assets: ${plan.summary.blockedAssets}\n`);
1960
+ process.stderr.write(`Sync targets: ${plan.summary.syncTargets.length > 0 ? plan.summary.syncTargets.join(", ") : "none"}\n`);
1961
+ process.stderr.write(`Mutation boundary: ${plan.mutationBoundary}\n`);
1962
+ writeOperatingRoute(plan.operatingRoute);
1963
+ process.stderr.write("\nAssets:\n");
1964
+ if (plan.assets.length === 0) {
1965
+ process.stderr.write(" - none\n");
1966
+ }
1967
+ else {
1968
+ for (const asset of plan.assets) {
1969
+ process.stderr.write(` - ${asset.project}/${asset.asset}: binding=${asset.bindingStatus} configured=${asset.configured ? "yes" : "no"} evidence=${asset.evidenceGate} sync_ready=${asset.syncReady ? "yes" : "no"} targets=${asset.syncTargets.length > 0 ? asset.syncTargets.join(",") : "none"}\n`);
1970
+ process.stderr.write(` next: ${asset.nextCommand}\n`);
1971
+ process.stderr.write(` reason: ${asset.reason}\n`);
1972
+ if (asset.reviewEvidence.length > 0) {
1973
+ for (const evidence of asset.reviewEvidence) {
1974
+ process.stderr.write(` review receipt: ${evidence.receiptCommand}\n`);
1975
+ process.stderr.write(` receipt path: ${evidence.receiptPathHint}\n`);
1976
+ }
1977
+ }
1978
+ }
1979
+ }
1980
+ process.stderr.write("\nBlockers:\n");
1981
+ if (plan.blockers.length === 0) {
1982
+ process.stderr.write(" - none\n");
1983
+ }
1984
+ else {
1985
+ for (const blocker of plan.blockers)
1986
+ process.stderr.write(` - ${blocker}\n`);
1987
+ }
1988
+ process.stderr.write("\nDistribution plan:\n");
1989
+ process.stderr.write(` - deployment boundary: ${plan.distributionPlan.deploymentBoundary}\n`);
1990
+ if (plan.distributionPlan.targets.length === 0) {
1991
+ process.stderr.write(" - targets: none\n");
1992
+ }
1993
+ else {
1994
+ for (const target of plan.distributionPlan.targets) {
1995
+ process.stderr.write(` - ${target.target}: ${target.status} command=${target.command} ready=${target.readyAssets.length} blocked=${target.blockedAssets.length}\n`);
1996
+ for (const blocked of target.blockedAssets.slice(0, 5)) {
1997
+ process.stderr.write(` blocked: ${blocked.project}/${blocked.asset} next=${blocked.nextCommand}\n`);
1998
+ }
1999
+ }
2000
+ }
2001
+ for (const guidance of plan.distributionPlan.guidance) {
2002
+ process.stderr.write(` guidance: ${guidance}\n`);
2003
+ }
2004
+ process.stderr.write("\n");
2005
+ writeReleaseGate(plan.releaseGate);
2006
+ process.stderr.write("\nRollout commands:\n");
2007
+ if (plan.rolloutCommands.length === 0) {
2008
+ process.stderr.write(" - none\n");
2009
+ }
2010
+ else {
2011
+ for (const command of plan.rolloutCommands)
2012
+ process.stderr.write(` - ${command}\n`);
2013
+ }
2014
+ process.stderr.write("\nNext commands:\n");
2015
+ for (const command of plan.nextCommands)
2016
+ process.stderr.write(` - ${command}\n`);
2017
+ }
2018
+ function writeReview(review, title) {
2019
+ const managerSummary = buildManagerSummary(review);
2020
+ process.stderr.write(`${title}\n`);
2021
+ process.stderr.write(`Profile: ${review.profilePath}\n`);
2022
+ process.stderr.write(`Organization: ${review.profile.organization.name} (${review.profile.organization.id})\n`);
2023
+ process.stderr.write("\nManager summary:\n");
2024
+ process.stderr.write(` Status: ${managerSummary.status}\n`);
2025
+ process.stderr.write(` Decision: ${managerSummary.decision}\n`);
2026
+ process.stderr.write(` Headline: ${managerSummary.headline}\n`);
2027
+ process.stderr.write(` Primary command: ${managerSummary.primaryCommand}\n`);
2028
+ process.stderr.write(` Dogfood evidence: ${managerSummary.dogfoodEvidence.status} (${managerSummary.dogfoodEvidence.pass}/${managerSummary.dogfoodEvidence.modules} PASS, remaining=${managerSummary.dogfoodEvidence.remaining})\n`);
2029
+ process.stderr.write(` Portfolio maturity: ${managerSummary.assetPortfolio.rolloutMaturity}\n`);
2030
+ process.stderr.write(` Active assets: ${managerSummary.assetPortfolio.active.length}\n`);
2031
+ process.stderr.write(` Stale assets: ${managerSummary.assetPortfolio.stale.length}\n`);
2032
+ process.stderr.write(` Blocked assets: ${managerSummary.assetPortfolio.blocked.length}\n`);
2033
+ process.stderr.write(` Evidence gaps: ${managerSummary.assetPortfolio.evidenceGaps.length}\n`);
2034
+ process.stderr.write(` Upgrade risks: ${managerSummary.assetPortfolio.upgradeRisks.length}\n`);
2035
+ writeAssetPortfolioGuide(" ");
2036
+ if (managerSummary.unfinishedWork) {
2037
+ process.stderr.write(" Unfinished work review:\n");
2038
+ process.stderr.write(` status: ${managerSummary.unfinishedWork.status}\n`);
2039
+ process.stderr.write(` source asset: ${managerSummary.unfinishedWork.sourceAsset}\n`);
2040
+ process.stderr.write(` draft asset: ${managerSummary.unfinishedWork.draftAsset}\n`);
2041
+ process.stderr.write(` label: ${managerSummary.unfinishedWork.plainLanguage.label}\n`);
2042
+ process.stderr.write(` purpose: ${managerSummary.unfinishedWork.plainLanguage.purpose}\n`);
2043
+ process.stderr.write(` improvement: ${managerSummary.unfinishedWork.plainLanguage.workflowImprovement}\n`);
2044
+ process.stderr.write(" preserves:\n");
2045
+ for (const item of managerSummary.unfinishedWork.plainLanguage.preserves)
2046
+ process.stderr.write(` - ${item}\n`);
2047
+ process.stderr.write(" required before use:\n");
2048
+ for (const item of managerSummary.unfinishedWork.plainLanguage.requiredBeforeUse)
2049
+ process.stderr.write(` - ${item}\n`);
2050
+ process.stderr.write(` current command: ${managerSummary.unfinishedWork.currentCommand}\n`);
2051
+ process.stderr.write(` writes: ${managerSummary.unfinishedWork.writes ? "yes" : "no"}\n`);
2052
+ process.stderr.write(` authority: ${managerSummary.unfinishedWork.localAuthority}\n`);
2053
+ for (const boundary of managerSummary.unfinishedWork.boundaries)
2054
+ process.stderr.write(` boundary: ${boundary}\n`);
2055
+ process.stderr.write(` not allowed to imply: ${managerSummary.unfinishedWork.notAllowedToImply.join(", ")}\n`);
2056
+ }
2057
+ process.stderr.write(" Improvement principle:\n");
2058
+ process.stderr.write(` command: ${managerSummary.improvementPrinciple.command}\n`);
2059
+ process.stderr.write(` rule: ${managerSummary.improvementPrinciple.rule}\n`);
2060
+ process.stderr.write(` template boundary: ${managerSummary.improvementPrinciple.templateBoundary}\n`);
2061
+ process.stderr.write(` infra boundary: ${managerSummary.improvementPrinciple.infraBoundary}\n`);
2062
+ process.stderr.write(` Mutation boundary: ${managerSummary.mutationBoundary}\n`);
2063
+ writeReleaseGate(managerSummary.releaseGate, " ");
2064
+ process.stderr.write(" Manager choices:\n");
2065
+ for (const choice of managerSummary.managerChoices) {
2066
+ process.stderr.write(` - ${choice.id}: ${choice.status} writes=${choice.writes ? "yes" : "no"}\n`);
2067
+ process.stderr.write(` command: ${choice.command}\n`);
2068
+ process.stderr.write(` why: ${choice.reason}\n`);
2069
+ process.stderr.write(` next: ${choice.next}\n`);
2070
+ }
2071
+ process.stderr.write(" Manager path:\n");
2072
+ for (const step of managerSummary.managerPath) {
2073
+ process.stderr.write(` - ${step.id}: ${step.status} owner=${step.owner} writes=${step.writes ? "yes" : "no"}\n`);
2074
+ process.stderr.write(` command: ${step.command}\n`);
2075
+ process.stderr.write(` why: ${step.purpose}\n`);
2076
+ process.stderr.write(` next: ${step.next}\n`);
2077
+ }
2078
+ process.stderr.write(" Decision packet:\n");
2079
+ process.stderr.write(` current: ${managerSummary.decisionPacket.currentState}\n`);
2080
+ process.stderr.write(` primary action: ${managerSummary.decisionPacket.primaryAction}\n`);
2081
+ process.stderr.write(` risk: ${managerSummary.decisionPacket.risk}\n`);
2082
+ process.stderr.write(` reason: ${managerSummary.decisionPacket.reason}\n`);
2083
+ process.stderr.write(" allowed now:\n");
2084
+ for (const item of managerSummary.decisionPacket.allowedNow)
2085
+ process.stderr.write(` - ${item}\n`);
2086
+ process.stderr.write(" blocked now:\n");
2087
+ for (const item of managerSummary.decisionPacket.blockedNow)
2088
+ process.stderr.write(` - ${item}\n`);
2089
+ writeOperatingRoute(managerSummary.operatingRoute);
2090
+ process.stderr.write(" Asset portfolio:\n");
2091
+ process.stderr.write(` rollout maturity: ${managerSummary.assetPortfolio.rolloutMaturity}\n`);
2092
+ process.stderr.write(" active:\n");
2093
+ if (managerSummary.assetPortfolio.active.length === 0) {
2094
+ process.stderr.write(" - none\n");
2095
+ }
2096
+ else {
2097
+ for (const asset of managerSummary.assetPortfolio.active) {
2098
+ const version = asset.version ? ` version=${asset.version}` : "";
2099
+ process.stderr.write(` - ${asset.project}/${asset.asset}:${version} rollout=${asset.rollout} evidence=${asset.evidence} posture=${asset.evidencePosture} targets=${asset.targets.join(", ") || "none"}\n`);
2100
+ process.stderr.write(` review: ${asset.reviewCommand}\n`);
2101
+ for (const evidence of asset.reviewEvidence) {
2102
+ process.stderr.write(` review receipt: ${evidence.receiptCommand}\n`);
2103
+ process.stderr.write(` receipt path: ${evidence.receiptPathHint}\n`);
2104
+ }
2105
+ if (asset.reviewRefs.length > 0) {
2106
+ process.stderr.write(` refs: ${asset.reviewRefs.map((ref) => `${ref.kind}:${ref.path}`).join(", ")}\n`);
2107
+ }
2108
+ }
2109
+ }
2110
+ process.stderr.write(" stale:\n");
2111
+ if (managerSummary.assetPortfolio.stale.length === 0) {
2112
+ process.stderr.write(" - none\n");
2113
+ }
2114
+ else {
2115
+ for (const asset of managerSummary.assetPortfolio.stale) {
2116
+ const version = asset.version ? ` version=${asset.version}` : "";
2117
+ process.stderr.write(` - ${asset.project}/${asset.asset}:${version} ${asset.reason} next=${asset.nextCommand}\n`);
2118
+ process.stderr.write(` review: ${asset.reviewCommand}\n`);
2119
+ for (const evidence of asset.reviewEvidence) {
2120
+ process.stderr.write(` review receipt: ${evidence.receiptCommand}\n`);
2121
+ process.stderr.write(` receipt path: ${evidence.receiptPathHint}\n`);
2122
+ }
2123
+ }
2124
+ }
2125
+ process.stderr.write(" evidence gaps:\n");
2126
+ if (managerSummary.assetPortfolio.evidenceGaps.length === 0) {
2127
+ process.stderr.write(" - none\n");
2128
+ }
2129
+ else {
2130
+ for (const gap of managerSummary.assetPortfolio.evidenceGaps) {
2131
+ const version = gap.version ? ` version=${gap.version}` : "";
2132
+ process.stderr.write(` - ${gap.project}/${gap.asset}:${version} gate=${gap.evidenceGate} posture=${gap.evidencePosture} next=${gap.nextCommand}\n`);
2133
+ if (gap.reviewRefs.length > 0) {
2134
+ process.stderr.write(` refs: ${gap.reviewRefs.map((ref) => `${ref.kind}:${ref.path}`).join(", ")}\n`);
2135
+ }
2136
+ }
2137
+ }
2138
+ process.stderr.write(" blocked:\n");
2139
+ if (managerSummary.assetPortfolio.blocked.length === 0) {
2140
+ process.stderr.write(" - none\n");
2141
+ }
2142
+ else {
2143
+ for (const asset of managerSummary.assetPortfolio.blocked) {
2144
+ const version = asset.version ? ` version=${asset.version}` : "";
2145
+ process.stderr.write(` - ${asset.project}/${asset.asset}:${version} ${asset.reason} next=${asset.nextCommand}\n`);
2146
+ process.stderr.write(` review: ${asset.reviewCommand}\n`);
2147
+ for (const evidence of asset.reviewEvidence) {
2148
+ process.stderr.write(` review receipt: ${evidence.receiptCommand}\n`);
2149
+ process.stderr.write(` receipt path: ${evidence.receiptPathHint}\n`);
2150
+ }
2151
+ }
2152
+ }
2153
+ process.stderr.write(" upgrade risks:\n");
2154
+ if (managerSummary.assetPortfolio.upgradeRisks.length === 0) {
2155
+ process.stderr.write(" - none\n");
2156
+ }
2157
+ else {
2158
+ for (const risk of managerSummary.assetPortfolio.upgradeRisks) {
2159
+ const version = risk.version ? ` version=${risk.version}` : "";
2160
+ process.stderr.write(` - ${risk.project}/${risk.asset}:${version} ${risk.reason} next=${risk.nextCommand}\n`);
2161
+ process.stderr.write(` review: ${risk.reviewCommand}\n`);
2162
+ for (const evidence of risk.reviewEvidence) {
2163
+ process.stderr.write(` review receipt: ${evidence.receiptCommand}\n`);
2164
+ process.stderr.write(` receipt path: ${evidence.receiptPathHint}\n`);
2165
+ }
2166
+ }
2167
+ }
2168
+ process.stderr.write(" Ready targets:\n");
2169
+ if (managerSummary.readyTargets.length === 0) {
2170
+ process.stderr.write(" - none\n");
2171
+ }
2172
+ else {
2173
+ for (const target of managerSummary.readyTargets)
2174
+ process.stderr.write(` - ${target.target}: ${target.command} assets=${target.assets.map((asset) => `${asset.project}/${asset.asset}`).join(", ")}\n`);
2175
+ }
2176
+ process.stderr.write(" Blocked targets:\n");
2177
+ if (managerSummary.blockedTargets.length === 0) {
2178
+ process.stderr.write(" - none\n");
2179
+ }
2180
+ else {
2181
+ for (const target of managerSummary.blockedTargets)
2182
+ process.stderr.write(` - ${target.target}: ${target.assets.map((asset) => `${asset.project}/${asset.asset}`).join(", ")}\n`);
2183
+ }
2184
+ process.stderr.write(" Blocked assets:\n");
2185
+ if (managerSummary.blockedAssets.length === 0) {
2186
+ process.stderr.write(" - none\n");
2187
+ }
2188
+ else {
2189
+ for (const asset of managerSummary.blockedAssets.slice(0, 8)) {
2190
+ process.stderr.write(` - ${asset.project}/${asset.asset}: ${asset.nextCommand}\n`);
2191
+ process.stderr.write(` reason: ${asset.reason}\n`);
2192
+ }
2193
+ }
2194
+ process.stderr.write(" Next commands:\n");
2195
+ for (const command of managerSummary.nextCommands.slice(0, 8))
2196
+ process.stderr.write(` - ${command}\n`);
2197
+ process.stderr.write("\nTotals:\n");
2198
+ process.stderr.write(`Projects: ${review.totals.projects}\n`);
2199
+ process.stderr.write(`Declared assets: ${review.totals.declaredAssets}\n`);
2200
+ process.stderr.write(`Configured assets: ${review.totals.configuredAssets}\n`);
2201
+ process.stderr.write(`Missing assets: ${review.totals.missingAssets}\n`);
2202
+ process.stderr.write(`Sync targets: ${review.totals.syncTargets}\n`);
2203
+ process.stderr.write(`Dogfood evidence modules: ${review.totals.dogfoodEvidenceModules}\n`);
2204
+ process.stderr.write(`Dogfood evidence PASS: ${review.totals.dogfoodEvidencePass}\n`);
2205
+ process.stderr.write(`Dogfood evidence remaining: ${review.totals.dogfoodEvidenceRemaining}\n`);
2206
+ process.stderr.write("\nProjects:\n");
2207
+ for (const project of review.projects) {
2208
+ process.stderr.write(` - ${project.name} (${project.id}) path=${project.path}\n`);
2209
+ process.stderr.write(` assets: ${project.plannedAssets.length > 0 ? project.plannedAssets.join(", ") : "none"}\n`);
2210
+ process.stderr.write(` configured: ${project.configuredAssets.length > 0 ? project.configuredAssets.join(", ") : "none"}\n`);
2211
+ process.stderr.write(` missing: ${project.missingAssets.length > 0 ? project.missingAssets.join(", ") : "none"}\n`);
2212
+ process.stderr.write(` sync targets: ${project.syncTargets.length > 0 ? project.syncTargets.join(", ") : "none"}\n`);
2213
+ if (project.assetGates.length > 0) {
2214
+ process.stderr.write(" asset gates:\n");
2215
+ for (const gate of project.assetGates) {
2216
+ process.stderr.write(` - ${gate.asset}: binding=${gate.bindingStatus} configured=${gate.configured ? "yes" : "no"} evidence=${gate.evidenceGate} sync_ready=${gate.syncReady ? "yes" : "no"}\n`);
2217
+ process.stderr.write(` next: ${gate.nextCommand}\n`);
2218
+ process.stderr.write(` reason: ${gate.reason}\n`);
2219
+ }
2220
+ }
2221
+ }
2222
+ if (review.nextActions.length === 0) {
2223
+ process.stderr.write("\nNo next actions.\n");
2224
+ return;
2225
+ }
2226
+ process.stderr.write("\nNext actions:\n");
2227
+ for (const action of review.nextActions.slice(0, 12)) {
2228
+ process.stderr.write(` - ${action}\n`);
2229
+ }
2230
+ if (review.nextActions.length > 12) {
2231
+ process.stderr.write(` ... ${review.nextActions.length - 12} more item(s). Use --json for the full review payload.\n`);
2232
+ }
2233
+ }
2234
+ function writeBindResult(result) {
2235
+ process.stderr.write(`Bound organization asset: ${result.asset}\n`);
2236
+ process.stderr.write(`Profile: ${result.path}\n`);
2237
+ process.stderr.write(`Project: ${result.projectId}\n`);
2238
+ process.stderr.write(`Status: ${result.status}\n`);
2239
+ process.stderr.write(`Asset binding: ${result.addedAsset ? "added" : "updated"}\n`);
2240
+ if (result.addedSyncTargets.length > 0) {
2241
+ process.stderr.write(`Added sync targets: ${result.addedSyncTargets.join(", ")}\n`);
2242
+ }
2243
+ process.stderr.write("\nBinding guidance:\n");
2244
+ process.stderr.write(` - effect: ${result.guidance.bindingEffect}\n`);
2245
+ process.stderr.write(` - review: ${result.guidance.reviewCommand}\n`);
2246
+ process.stderr.write(` - rollout: ${result.guidance.rolloutCommand}\n`);
2247
+ process.stderr.write(` - boundary: ${result.guidance.replacementBoundary}\n`);
2248
+ process.stderr.write("\nNext commands:\n");
2249
+ process.stderr.write(` - dna org review\n`);
2250
+ process.stderr.write(` - dna assets governance ${result.asset}\n`);
2251
+ process.stderr.write(` - dna init --template ${result.asset}\n`);
2252
+ }
2253
+ export async function runOrg(opts) {
2254
+ const subcommand = opts.subcommand || "status";
2255
+ if (subcommand === "intake") {
2256
+ const packet = await buildOrgIntake(opts);
2257
+ if (opts.json) {
2258
+ process.stdout.write(JSON.stringify(packet, null, 2) + "\n");
2259
+ return 0;
2260
+ }
2261
+ writeOrgIntake(packet);
2262
+ return 0;
2263
+ }
2264
+ if (subcommand === "configure-plan") {
2265
+ const profile = await readOrganizationProfile({ projectDir: opts.projectDir });
2266
+ const review = profile.profile ? await reviewOrganization({ projectDir: opts.projectDir }) : undefined;
2267
+ const plan = buildOrgConfigurePlan(profile.path, review);
2268
+ const exitCode = opts.apply ? await applyOrgConfigurePlan(plan, opts.projectDir ?? process.cwd()) : 0;
2269
+ if (opts.json) {
2270
+ process.stdout.write(JSON.stringify(plan, null, 2) + "\n");
2271
+ return exitCode;
2272
+ }
2273
+ writeConfigurePlan(plan);
2274
+ return exitCode;
2275
+ }
2276
+ if (subcommand === "rollout" || subcommand === "route") {
2277
+ const profile = await readOrganizationProfile({ projectDir: opts.projectDir });
2278
+ const review = profile.profile ? await reviewOrganization({ projectDir: opts.projectDir }) : undefined;
2279
+ const plan = buildOrgRolloutPlan(profile.path, review);
2280
+ if (opts.json) {
2281
+ process.stdout.write(JSON.stringify(plan, null, 2) + "\n");
2282
+ return 0;
2283
+ }
2284
+ writeRolloutPlan(plan);
2285
+ return 0;
2286
+ }
2287
+ if (subcommand === "receipt") {
2288
+ const profile = await readOrganizationProfile({ projectDir: opts.projectDir });
2289
+ const review = profile.profile ? await reviewOrganization({ projectDir: opts.projectDir }) : undefined;
2290
+ const rollout = buildOrgRolloutPlan(profile.path, review);
2291
+ const receipt = buildOrgReviewReceipt(profile.path, review, rollout);
2292
+ if (opts.json) {
2293
+ process.stdout.write(JSON.stringify(receipt, null, 2) + "\n");
2294
+ return 0;
2295
+ }
2296
+ writeOrgReceipt(receipt);
2297
+ return 0;
2298
+ }
2299
+ if (subcommand === "init") {
2300
+ const orgName = opts.name?.trim();
2301
+ if (!orgName) {
2302
+ process.stderr.write("Usage: dna org init --name <organization> [--project <project>] [--asset <asset>] [--sync-target <target>] [--json]\n");
2303
+ return 2;
2304
+ }
2305
+ const result = await createOrganizationProfile({
2306
+ orgName,
2307
+ projectName: opts.project,
2308
+ asset: opts.asset,
2309
+ syncTarget: opts.syncTarget,
2310
+ projectDir: opts.projectDir,
2311
+ });
2312
+ const review = await reviewOrganization({ projectDir: opts.projectDir });
2313
+ if (opts.json) {
2314
+ process.stdout.write(JSON.stringify({ path: result.path, profile: result.profile, review: review ? withManagerSummary(review) : review }, null, 2) + "\n");
2315
+ return 0;
2316
+ }
2317
+ process.stderr.write(`Created organization profile: ${result.path}\n`);
2318
+ if (review)
2319
+ writeReview(review, "Organization status");
2320
+ return 0;
2321
+ }
2322
+ if (subcommand === "bind") {
2323
+ const asset = opts.asset?.trim();
2324
+ if (!asset) {
2325
+ process.stderr.write("Usage: dna org bind --asset <asset> [--project <project>] [--status planned|active|paused] [--sync-target <target>] [--json]\n");
2326
+ return 2;
2327
+ }
2328
+ if (opts.status && !isOrganizationAssetStatus(opts.status)) {
2329
+ process.stderr.write(`Invalid org asset status: ${opts.status}. Expected one of planned, active, paused.\n`);
2330
+ return 2;
2331
+ }
2332
+ const result = await bindOrganizationAsset({
2333
+ asset,
2334
+ project: opts.project,
2335
+ status: opts.status,
2336
+ syncTarget: opts.syncTarget,
2337
+ projectDir: opts.projectDir,
2338
+ });
2339
+ if (!result) {
2340
+ const profile = await readOrganizationProfile({ projectDir: opts.projectDir });
2341
+ if (!profile.profile) {
2342
+ writeProfileMissing(profile.path);
2343
+ return 2;
2344
+ }
2345
+ process.stderr.write(`Project not found for binding: ${opts.project}\n`);
2346
+ return 2;
2347
+ }
2348
+ const review = await reviewOrganization({ projectDir: opts.projectDir });
2349
+ if (opts.json) {
2350
+ process.stdout.write(JSON.stringify({ result, review: review ? withManagerSummary(review) : review }, null, 2) + "\n");
2351
+ return 0;
2352
+ }
2353
+ writeBindResult(result);
2354
+ if (review)
2355
+ writeReview(review, "Organization status");
2356
+ return 0;
2357
+ }
2358
+ if (subcommand === "status" || subcommand === "review") {
2359
+ const profile = await readOrganizationProfile({ projectDir: opts.projectDir });
2360
+ if (!profile.profile) {
2361
+ const packet = buildOrgStatusPacket(profile.path, undefined);
2362
+ if (opts.json) {
2363
+ process.stdout.write(JSON.stringify(packet, null, 2) + "\n");
2364
+ return 0;
2365
+ }
2366
+ if (subcommand === "status") {
2367
+ writeOrgStatus(packet);
2368
+ }
2369
+ else {
2370
+ writeProfileMissing(profile.path);
2371
+ }
2372
+ return subcommand === "review" ? 2 : 0;
2373
+ }
2374
+ const review = await reviewOrganization({ projectDir: opts.projectDir });
2375
+ if (!review) {
2376
+ process.stderr.write(`Organization profile is unreadable: ${profile.path}\n`);
2377
+ return 2;
2378
+ }
2379
+ if (opts.json) {
2380
+ process.stdout.write(JSON.stringify(subcommand === "status" ? buildOrgStatusPacket(profile.path, review) : withManagerSummary(review), null, 2) + "\n");
2381
+ return 0;
2382
+ }
2383
+ if (subcommand === "status") {
2384
+ writeOrgStatus(buildOrgStatusPacket(profile.path, review));
2385
+ }
2386
+ else {
2387
+ writeReview(review, "Organization review");
2388
+ }
2389
+ return 0;
2390
+ }
2391
+ process.stderr.write("Usage: dna org <intake|rollout|init|bind|status|review> [description] [--name <organization>] [--project <project>] [--asset <asset>] [--status planned|active|paused] [--sync-target <target>] [--json]\n");
2392
+ return 2;
2393
+ }