@design-ai/cli 4.56.0 → 4.57.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 (264) hide show
  1. package/.claude-plugin/plugin.json +109 -25
  2. package/CHANGELOG.md +36 -0
  3. package/README.ko.md +9 -26
  4. package/README.md +10 -23
  5. package/cli/bin/design-ai.mjs +1 -0
  6. package/cli/commands/help.mjs +7 -4
  7. package/cli/commands/index.mjs +292 -0
  8. package/cli/commands/learn-help.mjs +149 -0
  9. package/cli/commands/learn-print-profile.mjs +373 -0
  10. package/cli/commands/learn-print-restore.mjs +349 -0
  11. package/cli/commands/learn-print-signals.mjs +453 -0
  12. package/cli/commands/learn.mjs +60 -1281
  13. package/cli/commands/pack.mjs +7 -3
  14. package/cli/commands/prompt.mjs +7 -3
  15. package/cli/commands/search.mjs +94 -1
  16. package/cli/lib/dispatch.mjs +3 -0
  17. package/cli/lib/embedding-index.mjs +199 -0
  18. package/cli/lib/embedding-provider.mjs +121 -0
  19. package/cli/lib/embedding-rerank.mjs +52 -0
  20. package/cli/lib/learn-args.mjs +490 -0
  21. package/cli/lib/learn-backup.mjs +748 -0
  22. package/cli/lib/learn-curation.mjs +612 -0
  23. package/cli/lib/learn-eval.mjs +459 -0
  24. package/cli/lib/learn-profile.mjs +763 -0
  25. package/cli/lib/learn-select.mjs +223 -0
  26. package/cli/lib/learn-shared.mjs +158 -0
  27. package/cli/lib/learn-test-support.mjs +218 -0
  28. package/cli/lib/learn-usage.mjs +360 -0
  29. package/cli/lib/learn.mjs +83 -3694
  30. package/cli/lib/lexical.mjs +137 -0
  31. package/cli/lib/local-config.mjs +110 -0
  32. package/cli/lib/mcp-server.mjs +13 -3
  33. package/cli/lib/pack.mjs +31 -4
  34. package/cli/lib/prompt.mjs +42 -4
  35. package/cli/lib/recall.mjs +193 -0
  36. package/cli/lib/retrieval-index.mjs +0 -0
  37. package/cli/lib/search-ranked.mjs +170 -0
  38. package/cli/lib/search.mjs +11 -1
  39. package/cli/lib/signals-backlog-commands.mjs +673 -0
  40. package/cli/lib/signals-backlog.mjs +361 -0
  41. package/cli/lib/signals-eval.mjs +176 -0
  42. package/cli/lib/signals-readiness.mjs +247 -0
  43. package/cli/lib/signals-registry.mjs +377 -0
  44. package/cli/lib/signals-render.mjs +478 -0
  45. package/cli/lib/signals-shared.mjs +75 -0
  46. package/cli/lib/signals.mjs +16 -2337
  47. package/cli/lib/site-bundle-handoff-expected.mjs +173 -0
  48. package/cli/lib/site-bundle-handoff-runbook-action-summary.mjs +332 -0
  49. package/cli/lib/site-bundle-handoff-runbook-human-lines.mjs +167 -0
  50. package/cli/lib/site-bundle-handoff-runbook-maps.mjs +238 -0
  51. package/cli/lib/site-bundle-handoff-runbook-next-step.mjs +278 -0
  52. package/cli/lib/site-bundle-handoff-runbook.mjs +44 -931
  53. package/cli/lib/site-test-support.mjs +68 -0
  54. package/cli/lib/skill-proposals-apply-commands.mjs +135 -0
  55. package/cli/lib/skill-proposals-apply-contract.mjs +750 -0
  56. package/cli/lib/skill-proposals-apply-plan.mjs +118 -0
  57. package/cli/lib/skill-proposals-generate.mjs +298 -0
  58. package/cli/lib/skill-proposals-render.mjs +532 -0
  59. package/cli/lib/skill-proposals-review.mjs +262 -0
  60. package/cli/lib/skill-proposals.mjs +15 -2046
  61. package/cli/lib/workspace-args.mjs +83 -0
  62. package/cli/lib/workspace-git.mjs +169 -0
  63. package/cli/lib/workspace-learning.mjs +483 -0
  64. package/cli/lib/workspace-repo.mjs +139 -0
  65. package/cli/lib/workspace-report.mjs +283 -0
  66. package/cli/lib/workspace-test-support.mjs +99 -0
  67. package/cli/lib/workspace.mjs +30 -1056
  68. package/docs/AI-LEARNING-PHASE2.md +215 -0
  69. package/docs/DISTRIBUTION.ko.md +2 -2
  70. package/docs/DISTRIBUTION.md +2 -2
  71. package/docs/NEXT-SURFACE-DECISION.md +125 -0
  72. package/docs/PRODUCT-READINESS.md +3 -3
  73. package/docs/RELEASE-GATES.ko.md +35 -0
  74. package/docs/RELEASE-GATES.md +234 -0
  75. package/docs/ROADMAP.md +53 -8
  76. package/docs/external-status.md +6 -8
  77. package/docs/inspection-20260630.md +169 -0
  78. package/docs/integrations/design-ai-mcp-server.md +4 -4
  79. package/docs/reference/ant-design.md +413 -0
  80. package/docs/reference/awesome-design-md.md +439 -0
  81. package/docs/reference/mui.md +783 -0
  82. package/docs/reference/shadcn-ui.md +298 -0
  83. package/examples/component-accordion-actions.md +5 -5
  84. package/examples/component-accordion-details.md +5 -5
  85. package/examples/component-accordion-summary.md +5 -5
  86. package/examples/component-accordion.md +3 -3
  87. package/examples/component-affix.md +1 -1
  88. package/examples/component-alert-dialog.md +1 -1
  89. package/examples/component-alert-title.md +1 -1
  90. package/examples/component-alert.md +3 -3
  91. package/examples/component-anchor.md +1 -1
  92. package/examples/component-app-bar.md +1 -1
  93. package/examples/component-aspect-ratio.md +1 -1
  94. package/examples/component-auto-complete.md +2 -2
  95. package/examples/component-avatar-group.md +5 -5
  96. package/examples/component-avatar.md +3 -3
  97. package/examples/component-back-top.md +1 -1
  98. package/examples/component-backdrop.md +1 -1
  99. package/examples/component-badge.md +3 -3
  100. package/examples/component-border-beam.md +3 -3
  101. package/examples/component-bottom-navigation.md +1 -1
  102. package/examples/component-box.md +1 -1
  103. package/examples/component-breadcrumb.md +3 -3
  104. package/examples/component-button-base.md +3 -3
  105. package/examples/component-button-group.md +2 -2
  106. package/examples/component-button.md +3 -3
  107. package/examples/component-calendar.md +2 -2
  108. package/examples/component-card-actions.md +1 -1
  109. package/examples/component-card-content.md +1 -1
  110. package/examples/component-card-header.md +1 -1
  111. package/examples/component-card-media.md +1 -1
  112. package/examples/component-card.md +3 -3
  113. package/examples/component-carousel.md +2 -2
  114. package/examples/component-cascader.md +1 -1
  115. package/examples/component-chart.md +1 -1
  116. package/examples/component-checkbox.md +3 -3
  117. package/examples/component-click-away-listener.md +1 -1
  118. package/examples/component-code.md +1 -1
  119. package/examples/component-collapsible.md +1 -1
  120. package/examples/component-color-picker.md +1 -1
  121. package/examples/component-combobox.md +1 -1
  122. package/examples/component-command.md +1 -1
  123. package/examples/component-config-provider.md +3 -3
  124. package/examples/component-context-menu.md +1 -1
  125. package/examples/component-css-baseline.md +3 -3
  126. package/examples/component-date-picker.md +2 -2
  127. package/examples/component-descriptions.md +1 -1
  128. package/examples/component-dialog-actions.md +1 -1
  129. package/examples/component-dialog-content-text.md +1 -1
  130. package/examples/component-dialog-content.md +1 -1
  131. package/examples/component-dialog-title.md +1 -1
  132. package/examples/component-dialog.md +2 -2
  133. package/examples/component-divider.md +3 -3
  134. package/examples/component-drawer.md +3 -3
  135. package/examples/component-dropdown.md +3 -3
  136. package/examples/component-empty.md +2 -2
  137. package/examples/component-fade.md +1 -1
  138. package/examples/component-field.md +1 -1
  139. package/examples/component-filled-input.md +1 -1
  140. package/examples/component-flex.md +1 -1
  141. package/examples/component-float-button.md +2 -2
  142. package/examples/component-form-control-label.md +1 -1
  143. package/examples/component-form-control.md +1 -1
  144. package/examples/component-form-controls.md +1 -1
  145. package/examples/component-form-group.md +1 -1
  146. package/examples/component-form-helper-text.md +1 -1
  147. package/examples/component-form-label.md +1 -1
  148. package/examples/component-form.md +2 -2
  149. package/examples/component-grid.md +2 -2
  150. package/examples/component-grow.md +1 -1
  151. package/examples/component-hover-card.md +1 -1
  152. package/examples/component-icon-button.md +1 -1
  153. package/examples/component-icon.md +1 -1
  154. package/examples/component-image-list.md +1 -1
  155. package/examples/component-image.md +1 -1
  156. package/examples/component-input-adornment.md +1 -1
  157. package/examples/component-input-base.md +1 -1
  158. package/examples/component-input-number.md +1 -1
  159. package/examples/component-input-otp.md +2 -2
  160. package/examples/component-input.md +3 -3
  161. package/examples/component-item.md +1 -1
  162. package/examples/component-label.md +1 -1
  163. package/examples/component-layout.md +1 -1
  164. package/examples/component-link.md +1 -1
  165. package/examples/component-list-item-avatar.md +1 -1
  166. package/examples/component-list-item-button.md +1 -1
  167. package/examples/component-list-item-icon.md +1 -1
  168. package/examples/component-list-item-text.md +1 -1
  169. package/examples/component-list-item.md +1 -1
  170. package/examples/component-list-subheader.md +1 -1
  171. package/examples/component-list.md +2 -2
  172. package/examples/component-masonry.md +1 -1
  173. package/examples/component-mentions.md +1 -1
  174. package/examples/component-menu-item.md +1 -1
  175. package/examples/component-menu-list.md +1 -1
  176. package/examples/component-menu.md +1 -1
  177. package/examples/component-menubar.md +1 -1
  178. package/examples/component-message.md +1 -1
  179. package/examples/component-mobile-stepper.md +1 -1
  180. package/examples/component-modal.md +5 -5
  181. package/examples/component-navigation-menu.md +1 -1
  182. package/examples/component-notification.md +1 -1
  183. package/examples/component-outlined-input.md +1 -1
  184. package/examples/component-pagination.md +3 -3
  185. package/examples/component-paper.md +1 -1
  186. package/examples/component-popconfirm.md +1 -1
  187. package/examples/component-popover.md +3 -3
  188. package/examples/component-popper.md +1 -1
  189. package/examples/component-progress.md +2 -2
  190. package/examples/component-qr-code.md +1 -1
  191. package/examples/component-radio.md +3 -3
  192. package/examples/component-rate.md +2 -2
  193. package/examples/component-resizable.md +1 -1
  194. package/examples/component-result.md +1 -1
  195. package/examples/component-scroll-area.md +1 -1
  196. package/examples/component-segmented.md +3 -3
  197. package/examples/component-select.md +3 -3
  198. package/examples/component-separator.md +1 -1
  199. package/examples/component-sheet.md +1 -1
  200. package/examples/component-sidebar.md +1 -1
  201. package/examples/component-skeleton.md +3 -3
  202. package/examples/component-slide.md +1 -1
  203. package/examples/component-slider.md +3 -3
  204. package/examples/component-snackbar-content.md +1 -1
  205. package/examples/component-snackbar.md +1 -1
  206. package/examples/component-sonner.md +1 -1
  207. package/examples/component-space.md +1 -1
  208. package/examples/component-speed-dial-action.md +1 -1
  209. package/examples/component-speed-dial.md +1 -1
  210. package/examples/component-spin.md +2 -2
  211. package/examples/component-spinner.md +1 -1
  212. package/examples/component-splitter.md +2 -2
  213. package/examples/component-stack.md +1 -1
  214. package/examples/component-statistic.md +1 -1
  215. package/examples/component-step-button.md +4 -4
  216. package/examples/component-step-connector.md +4 -4
  217. package/examples/component-step-content.md +1 -1
  218. package/examples/component-step-icon.md +1 -1
  219. package/examples/component-step-label.md +1 -1
  220. package/examples/component-step.md +2 -2
  221. package/examples/component-steps.md +2 -2
  222. package/examples/component-swipeable-drawer.md +1 -1
  223. package/examples/component-switch.md +3 -3
  224. package/examples/component-tab-scroll-button.md +5 -5
  225. package/examples/component-tab.md +1 -1
  226. package/examples/component-table-body.md +1 -1
  227. package/examples/component-table-cell.md +1 -1
  228. package/examples/component-table-container.md +1 -1
  229. package/examples/component-table-footer.md +1 -1
  230. package/examples/component-table-head.md +1 -1
  231. package/examples/component-table-pagination.md +1 -1
  232. package/examples/component-table-row.md +1 -1
  233. package/examples/component-table-sort-label.md +1 -1
  234. package/examples/component-table.md +2 -2
  235. package/examples/component-tabs.md +3 -3
  236. package/examples/component-tag-badge.md +2 -2
  237. package/examples/component-tag.md +1 -1
  238. package/examples/component-textarea-autosize.md +1 -1
  239. package/examples/component-textarea.md +1 -1
  240. package/examples/component-time-picker.md +1 -1
  241. package/examples/component-timeline.md +1 -1
  242. package/examples/component-toast.md +4 -4
  243. package/examples/component-toggle-button.md +1 -1
  244. package/examples/component-toggle.md +1 -1
  245. package/examples/component-toolbar.md +1 -1
  246. package/examples/component-tooltip.md +3 -3
  247. package/examples/component-tour.md +1 -1
  248. package/examples/component-transfer.md +1 -1
  249. package/examples/component-tree-select.md +1 -1
  250. package/examples/component-tree.md +1 -1
  251. package/examples/component-typography.md +2 -2
  252. package/examples/component-upload.md +1 -1
  253. package/examples/component-watermark.md +1 -1
  254. package/examples/component-zoom.md +1 -1
  255. package/knowledge/COVERAGE.md +4 -3
  256. package/knowledge/components/INDEX.md +202 -202
  257. package/knowledge/patterns/brand-references.md +72 -72
  258. package/package.json +1 -1
  259. package/tools/audit/local-ci.py +16 -1
  260. package/tools/audit/package-smoke.py +611 -9
  261. package/tools/audit/registry-smoke.py +534 -0
  262. package/tools/audit/release-metadata.py +47 -1
  263. package/tools/audit/smoke_assertions.py +915 -13
  264. package/tools/migrations/refs-links-to-reference-pages.py +179 -0
@@ -0,0 +1,750 @@
1
+ // Apply-plan command contract construction for skill proposals.
2
+
3
+ import {
4
+ APPLY_PLAN_BASE_COMMAND,
5
+ APPLY_PLAN_FOLLOW_UP_COMMAND_DESCRIPTIONS,
6
+ APPLY_PLAN_FOLLOW_UP_COMMAND_DISPLAY_LABELS,
7
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACTS,
8
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_ACTIONS,
9
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_APPLY_PRECONDITION_IDS,
10
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_APPLY_PRECONDITION_LABELS,
11
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_DISPOSITIONS,
12
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_MANUAL_APPLY_CANDIDATES,
13
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_MEDIA_TYPES,
14
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_REQUIRES_CLEAN_WORKSPACE_BEFORE_APPLY,
15
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_REQUIRES_MANUAL_REVIEW,
16
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_REVIEW_INSTRUCTIONS,
17
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_TYPES,
18
+ APPLY_PLAN_FOLLOW_UP_COMMAND_POLICIES,
19
+ APPLY_PLAN_FOLLOW_UP_COMMAND_SPECS,
20
+ APPLY_PLAN_FORBIDDEN_FLAGS,
21
+ argsEndWith,
22
+ argsStartWith,
23
+ commandArgCheck,
24
+ } from "./skill-proposals-apply-commands.mjs";
25
+
26
+ function applyPreconditionsForCommandKey(commandKey) {
27
+ const ids = APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_APPLY_PRECONDITION_IDS[commandKey] || [];
28
+ const labels = APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_APPLY_PRECONDITION_LABELS[commandKey] || [];
29
+ return ids.map((id, index) => ({
30
+ id,
31
+ label: labels[index] || id,
32
+ required: true,
33
+ }));
34
+ }
35
+
36
+ function applyPreconditionIsSatisfied(precondition) {
37
+ return precondition?.satisfied === true;
38
+ }
39
+
40
+ function manualApplyBlockedReason({ manualApplyCandidate, requiredPendingApplyPreconditionCount }) {
41
+ if (!manualApplyCandidate) {
42
+ return {
43
+ code: "not-manual-apply-candidate",
44
+ message: "This output artifact is review-only and cannot be applied.",
45
+ };
46
+ }
47
+ if (requiredPendingApplyPreconditionCount > 0) {
48
+ return {
49
+ code: "required-preconditions-pending",
50
+ message: "Complete required apply preconditions before applying this patch preview.",
51
+ };
52
+ }
53
+ return { code: "", message: "" };
54
+ }
55
+
56
+ function manualApplyStatus({ manualApplyCandidate, manualApplyReady }) {
57
+ if (manualApplyReady) return "ready";
58
+ if (manualApplyCandidate) return "blocked";
59
+ return "not-applicable";
60
+ }
61
+
62
+ function manualApplyStatusLabel(status) {
63
+ if (status === "ready") return "Ready to apply";
64
+ if (status === "blocked") return "Blocked";
65
+ return "Review only";
66
+ }
67
+
68
+ function manualApplyStatusTone(status) {
69
+ if (status === "ready") return "success";
70
+ if (status === "blocked") return "warning";
71
+ return "neutral";
72
+ }
73
+
74
+ export function buildApplyPlanCommandContract(followUpCommands, reviewFile) {
75
+ const requiredKeys = Object.keys(APPLY_PLAN_FOLLOW_UP_COMMAND_SPECS);
76
+ const commandArgs = Object.fromEntries(Object.entries(followUpCommands).map(([key, value]) => [
77
+ key,
78
+ Array.isArray(value?.commandArgs) ? value.commandArgs : [],
79
+ ]));
80
+ const missingCommandKeys = requiredKeys.filter((key) => !Array.isArray(commandArgs[key]) || commandArgs[key].length === 0);
81
+ const unexpectedCommandKeys = Object.keys(commandArgs).filter((key) => !requiredKeys.includes(key));
82
+ const checks = [
83
+ commandArgCheck({
84
+ id: "required-command-keys-present",
85
+ passed: missingCommandKeys.length === 0,
86
+ message: missingCommandKeys.length === 0
87
+ ? "All required apply-plan follow-up commands are present."
88
+ : "Some required apply-plan follow-up commands are missing.",
89
+ evidence: { requiredKeys, missingCommandKeys },
90
+ }),
91
+ commandArgCheck({
92
+ id: "no-unexpected-command-keys",
93
+ passed: unexpectedCommandKeys.length === 0,
94
+ message: unexpectedCommandKeys.length === 0
95
+ ? "No unexpected apply-plan follow-up commands are present."
96
+ : "Unexpected apply-plan follow-up command keys were found.",
97
+ evidence: { unexpectedCommandKeys },
98
+ }),
99
+ ];
100
+
101
+ for (const key of requiredKeys) {
102
+ const args = commandArgs[key] || [];
103
+ const reviewFileIndex = args.indexOf("--review-file");
104
+ checks.push(commandArgCheck({
105
+ id: `${key}-base-command`,
106
+ passed: argsStartWith(args, APPLY_PLAN_BASE_COMMAND),
107
+ message: `${key} starts with design-ai learn --propose-skills.`,
108
+ evidence: { commandArgs: args.slice(0, APPLY_PLAN_BASE_COMMAND.length) },
109
+ }));
110
+ checks.push(commandArgCheck({
111
+ id: `${key}-review-file-context`,
112
+ passed: Boolean(reviewFile) && reviewFileIndex >= 0 && args[reviewFileIndex + 1] === reviewFile,
113
+ message: `${key} preserves the configured review file.`,
114
+ evidence: { reviewFile, commandReviewFile: reviewFileIndex >= 0 ? args[reviewFileIndex + 1] || "" : "" },
115
+ }));
116
+ checks.push(commandArgCheck({
117
+ id: `${key}-expected-suffix`,
118
+ passed: argsEndWith(args, APPLY_PLAN_FOLLOW_UP_COMMAND_SPECS[key]),
119
+ message: `${key} ends with the expected action flags.`,
120
+ evidence: { expectedSuffix: APPLY_PLAN_FOLLOW_UP_COMMAND_SPECS[key], actualSuffix: args.slice(-APPLY_PLAN_FOLLOW_UP_COMMAND_SPECS[key].length) },
121
+ }));
122
+ checks.push(commandArgCheck({
123
+ id: `${key}-read-only-flags`,
124
+ passed: APPLY_PLAN_FORBIDDEN_FLAGS.every((flag) => !args.includes(flag)),
125
+ message: `${key} does not include write/apply confirmation flags.`,
126
+ evidence: { forbiddenFlags: APPLY_PLAN_FORBIDDEN_FLAGS },
127
+ }));
128
+ }
129
+
130
+ const failures = checks.filter((check) => check.level === "fail").length;
131
+ const warnings = checks.filter((check) => check.level === "warn").length;
132
+ const passes = checks.filter((check) => check.level === "pass").length;
133
+ const checkCount = checks.length;
134
+ const failedChecks = checks
135
+ .filter((check) => check.level === "fail")
136
+ .map((check) => ({
137
+ id: check.id,
138
+ message: check.message,
139
+ evidence: check.evidence || {},
140
+ }));
141
+ const nextCommandKey = failures > 0 ? "" : "reviewCheckJson";
142
+ const nextCommand = nextCommandKey ? followUpCommands[nextCommandKey]?.command || "" : "";
143
+ const nextCommandArgs = nextCommandKey ? commandArgs[nextCommandKey] || [] : [];
144
+ const nextCommandRunPolicy = nextCommandKey ? "preview-only" : "";
145
+ const nextCommandSafety = nextCommandKey
146
+ ? {
147
+ level: "read-only",
148
+ writesLocalFiles: false,
149
+ mutatesLocalState: false,
150
+ mutatesProfile: false,
151
+ mutatesReviewFile: false,
152
+ mutatesSkillFiles: false,
153
+ callsExternalAiApis: false,
154
+ requiresCleanWorkspace: false,
155
+ reason: "The next apply-plan follow-up command only checks proposal review readiness and does not mutate local state.",
156
+ }
157
+ : {};
158
+ const commandSequence = failures > 0
159
+ ? []
160
+ : requiredKeys.map((key, index) => {
161
+ const writesOutputArtifact = key === "reviewCheckReport" || key === "proposalPatchPreview";
162
+ return {
163
+ step: index + 1,
164
+ key,
165
+ command: followUpCommands[key]?.command || "",
166
+ commandArgs: commandArgs[key] || [],
167
+ runPolicy: APPLY_PLAN_FOLLOW_UP_COMMAND_POLICIES[key] || "preview-only",
168
+ safety: {
169
+ level: writesOutputArtifact ? "local-output" : "read-only",
170
+ writesLocalFiles: writesOutputArtifact,
171
+ writesOutputArtifact,
172
+ mutatesLocalState: writesOutputArtifact,
173
+ mutatesProfile: false,
174
+ mutatesReviewFile: false,
175
+ mutatesSkillFiles: false,
176
+ callsExternalAiApis: false,
177
+ requiresCleanWorkspace: false,
178
+ reason: writesOutputArtifact
179
+ ? "This follow-up command writes a local preview artifact with --out but does not mutate learning, review, or skill files."
180
+ : "This follow-up command validates readiness without writing local files or mutating local state.",
181
+ },
182
+ };
183
+ });
184
+ const commandSequenceSummary = {
185
+ executable: failures === 0,
186
+ blocked: failures > 0,
187
+ stepCount: commandSequence.length,
188
+ readOnlyStepCount: commandSequence.filter((item) => item.safety?.level === "read-only").length,
189
+ localOutputStepCount: commandSequence.filter((item) => item.safety?.level === "local-output").length,
190
+ writesLocalFiles: commandSequence.some((item) => Boolean(item.safety?.writesLocalFiles)),
191
+ writesOutputArtifacts: commandSequence.some((item) => Boolean(item.safety?.writesOutputArtifact)),
192
+ mutatesProfile: commandSequence.some((item) => Boolean(item.safety?.mutatesProfile)),
193
+ mutatesReviewFile: commandSequence.some((item) => Boolean(item.safety?.mutatesReviewFile)),
194
+ mutatesSkillFiles: commandSequence.some((item) => Boolean(item.safety?.mutatesSkillFiles)),
195
+ callsExternalAiApis: commandSequence.some((item) => Boolean(item.safety?.callsExternalAiApis)),
196
+ requiresCleanWorkspace: commandSequence.some((item) => Boolean(item.safety?.requiresCleanWorkspace)),
197
+ runPolicy: failures > 0 ? "blocked" : "mixed-preview-local-output",
198
+ reason: failures > 0
199
+ ? "Command contract failures must be fixed before running follow-up commands."
200
+ : "The sequence combines read-only readiness checks with local output artifact previews; it does not mutate learning, review, or skill files.",
201
+ };
202
+ const commandSequenceKeys = commandSequence.map((item) => item.key);
203
+ const commandSequenceByKey = Object.fromEntries(commandSequence.map((item) => [item.key, item]));
204
+ const operatorRunbookStages = failures > 0
205
+ ? []
206
+ : [
207
+ {
208
+ step: 1,
209
+ key: "previewArtifacts",
210
+ label: "Generate optional review artifacts",
211
+ kind: "local-output-preview",
212
+ required: false,
213
+ commandKeys: ["reviewCheckReport", "proposalPatchPreview"],
214
+ commands: ["reviewCheckReport", "proposalPatchPreview"].map((key) => commandSequenceByKey[key]),
215
+ reason: "Optional Markdown review and patch preview artifacts can be generated before manual skill edits.",
216
+ },
217
+ {
218
+ step: 2,
219
+ key: "manualSkillEdit",
220
+ label: "Apply accepted skill deltas manually",
221
+ kind: "manual-review",
222
+ required: true,
223
+ commandKeys: [],
224
+ commands: [],
225
+ reason: "No apply-plan command mutates skill files; the operator must manually edit accepted skill deltas after review.",
226
+ },
227
+ {
228
+ step: 3,
229
+ key: "reviewReadiness",
230
+ label: "Run review readiness check",
231
+ kind: "read-only-check",
232
+ required: true,
233
+ commandKeys: ["reviewCheckJson"],
234
+ commands: [commandSequenceByKey.reviewCheckJson],
235
+ reason: "Run the read-only review check after manual skill edits to verify proposal review state.",
236
+ },
237
+ {
238
+ step: 4,
239
+ key: "strictGate",
240
+ label: "Run strict readiness gate",
241
+ kind: "read-only-gate",
242
+ required: true,
243
+ commandKeys: ["strictGate"],
244
+ commands: [commandSequenceByKey.strictGate],
245
+ reason: "Run the strict gate before marking accepted proposals applied.",
246
+ },
247
+ ];
248
+ const operatorRunbookStageKeys = operatorRunbookStages.map((stage) => stage.key);
249
+ const operatorRunbookStageByKey = Object.fromEntries(operatorRunbookStages.map((stage) => [stage.key, stage]));
250
+ const summarizeOperatorRunbookStage = (stage) => {
251
+ if (!stage) return {};
252
+ const commandSafetyItems = stage.commands
253
+ .map((command) => command?.safety)
254
+ .filter((safety) => safety && typeof safety === "object");
255
+ const writesLocalFiles = commandSafetyItems.some((safety) => Boolean(safety.writesLocalFiles));
256
+ const writesOutputArtifacts = commandSafetyItems.some((safety) => Boolean(safety.writesOutputArtifact));
257
+ const mutatesLocalState = commandSafetyItems.some((safety) => Boolean(safety.mutatesLocalState));
258
+ const mutatesProfile = commandSafetyItems.some((safety) => Boolean(safety.mutatesProfile));
259
+ const mutatesReviewFile = commandSafetyItems.some((safety) => Boolean(safety.mutatesReviewFile));
260
+ const mutatesSkillFiles = commandSafetyItems.some((safety) => Boolean(safety.mutatesSkillFiles));
261
+ const callsExternalAiApis = commandSafetyItems.some((safety) => Boolean(safety.callsExternalAiApis));
262
+ const requiresCleanWorkspace = commandSafetyItems.some((safety) => Boolean(safety.requiresCleanWorkspace));
263
+ return {
264
+ key: stage.key,
265
+ step: stage.step,
266
+ label: stage.label,
267
+ kind: stage.kind,
268
+ required: stage.required,
269
+ hasCommands: stage.commandKeys.length > 0,
270
+ commandCount: stage.commandKeys.length,
271
+ commandKeys: stage.commandKeys,
272
+ writesLocalFiles,
273
+ writesOutputArtifacts,
274
+ mutatesLocalState,
275
+ mutatesProfile,
276
+ mutatesReviewFile,
277
+ mutatesSkillFiles,
278
+ callsExternalAiApis,
279
+ requiresCleanWorkspace,
280
+ reason: stage.reason,
281
+ };
282
+ };
283
+ const nextStage = failures > 0
284
+ ? null
285
+ : operatorRunbookStageByKey.previewArtifacts || null;
286
+ const nextRequiredStage = failures > 0
287
+ ? null
288
+ : operatorRunbookStages.find((stage) => stage.required) || null;
289
+ const nextRequiredCommandStage = failures > 0
290
+ ? null
291
+ : operatorRunbookStages.find((stage) => stage.required && stage.commandKeys.length > 0) || null;
292
+ const nextStageSummary = summarizeOperatorRunbookStage(nextStage);
293
+ const nextRequiredStageSummary = summarizeOperatorRunbookStage(nextRequiredStage);
294
+ const nextRequiredCommandStageSummary = summarizeOperatorRunbookStage(nextRequiredCommandStage);
295
+ const summarizeDecisionCommand = (command) => command
296
+ ? {
297
+ step: command.step,
298
+ key: command.key,
299
+ command: command.command,
300
+ commandArgs: command.commandArgs,
301
+ runPolicy: command.runPolicy,
302
+ safetyLevel: command.safety?.level || "",
303
+ safety: {
304
+ level: command.safety?.level || "",
305
+ writesLocalFiles: Boolean(command.safety?.writesLocalFiles),
306
+ writesOutputArtifact: Boolean(command.safety?.writesOutputArtifact),
307
+ mutatesLocalState: Boolean(command.safety?.mutatesLocalState),
308
+ mutatesProfile: Boolean(command.safety?.mutatesProfile),
309
+ mutatesReviewFile: Boolean(command.safety?.mutatesReviewFile),
310
+ mutatesSkillFiles: Boolean(command.safety?.mutatesSkillFiles),
311
+ callsExternalAiApis: Boolean(command.safety?.callsExternalAiApis),
312
+ requiresCleanWorkspace: Boolean(command.safety?.requiresCleanWorkspace),
313
+ reason: command.safety?.reason || "",
314
+ },
315
+ writesLocalFiles: Boolean(command.safety?.writesLocalFiles),
316
+ writesOutputArtifact: Boolean(command.safety?.writesOutputArtifact),
317
+ mutatesLocalState: Boolean(command.safety?.mutatesLocalState),
318
+ mutatesProfile: Boolean(command.safety?.mutatesProfile),
319
+ mutatesReviewFile: Boolean(command.safety?.mutatesReviewFile),
320
+ mutatesSkillFiles: Boolean(command.safety?.mutatesSkillFiles),
321
+ callsExternalAiApis: Boolean(command.safety?.callsExternalAiApis),
322
+ requiresCleanWorkspace: Boolean(command.safety?.requiresCleanWorkspace),
323
+ }
324
+ : {};
325
+ const decisionCommands = failures > 0
326
+ ? []
327
+ : (nextStage?.commands || []).map((command) => summarizeDecisionCommand(command));
328
+ const decisionCommandByKey = Object.fromEntries(decisionCommands.map((command) => [command.key, command]));
329
+ const decisionCommandStepByKey = Object.fromEntries(decisionCommands.map((command) => [command.key, command.step]));
330
+ const decisionCommandRunPolicyByKey = Object.fromEntries(
331
+ decisionCommands.map((command) => [command.key, command.runPolicy]),
332
+ );
333
+ const decisionCommandSafetyLevelByKey = Object.fromEntries(
334
+ decisionCommands.map((command) => [command.key, command.safetyLevel]),
335
+ );
336
+ const decisionCommandArgsByKey = Object.fromEntries(
337
+ decisionCommands.map((command) => [command.key, command.commandArgs]),
338
+ );
339
+ const decisionCommandStringByKey = Object.fromEntries(
340
+ decisionCommands.map((command) => [command.key, command.command]),
341
+ );
342
+ const decisionCommandDisplayLabelByKey = Object.fromEntries(
343
+ decisionCommands.map((command) => [
344
+ command.key,
345
+ APPLY_PLAN_FOLLOW_UP_COMMAND_DISPLAY_LABELS[command.key] || command.key,
346
+ ]),
347
+ );
348
+ const decisionCommandDescriptionByKey = Object.fromEntries(
349
+ decisionCommands.map((command) => [
350
+ command.key,
351
+ APPLY_PLAN_FOLLOW_UP_COMMAND_DESCRIPTIONS[command.key] || "",
352
+ ]),
353
+ );
354
+ const decisionCommandOutputArtifactByKey = Object.fromEntries(
355
+ decisionCommands.map((command) => [
356
+ command.key,
357
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACTS[command.key] || "",
358
+ ]),
359
+ );
360
+ const decisionCommandOutputArtifactTypeByKey = Object.fromEntries(
361
+ decisionCommands.map((command) => [
362
+ command.key,
363
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_TYPES[command.key] || "",
364
+ ]),
365
+ );
366
+ const decisionCommandOutputArtifactActionByKey = Object.fromEntries(
367
+ decisionCommands.map((command) => [
368
+ command.key,
369
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_ACTIONS[command.key] || "",
370
+ ]),
371
+ );
372
+ const decisionCommandOutputArtifactMediaTypeByKey = Object.fromEntries(
373
+ decisionCommands.map((command) => [
374
+ command.key,
375
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_MEDIA_TYPES[command.key] || "",
376
+ ]),
377
+ );
378
+ const decisionCommandOutputArtifactDispositionByKey = Object.fromEntries(
379
+ decisionCommands.map((command) => [
380
+ command.key,
381
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_DISPOSITIONS[command.key] || "",
382
+ ]),
383
+ );
384
+ const decisionCommandOutputArtifactManualApplyCandidateByKey = Object.fromEntries(
385
+ decisionCommands.map((command) => [
386
+ command.key,
387
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_MANUAL_APPLY_CANDIDATES[command.key] || false,
388
+ ]),
389
+ );
390
+ const decisionCommandOutputArtifactRequiresManualReviewByKey = Object.fromEntries(
391
+ decisionCommands.map((command) => [
392
+ command.key,
393
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_REQUIRES_MANUAL_REVIEW[command.key] || false,
394
+ ]),
395
+ );
396
+ const decisionCommandOutputArtifactReviewInstructionByKey = Object.fromEntries(
397
+ decisionCommands.map((command) => [
398
+ command.key,
399
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_REVIEW_INSTRUCTIONS[command.key] || "",
400
+ ]),
401
+ );
402
+ const decisionCommandOutputArtifactRequiresCleanWorkspaceBeforeApplyByKey = Object.fromEntries(
403
+ decisionCommands.map((command) => [
404
+ command.key,
405
+ APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_REQUIRES_CLEAN_WORKSPACE_BEFORE_APPLY[command.key] || false,
406
+ ]),
407
+ );
408
+ const decisionCommandOutputArtifactApplyPreconditionIdsByKey = Object.fromEntries(
409
+ decisionCommands.map((command) => [
410
+ command.key,
411
+ [...(APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_APPLY_PRECONDITION_IDS[command.key] || [])],
412
+ ]),
413
+ );
414
+ const decisionCommandOutputArtifactApplyPreconditionLabelsByKey = Object.fromEntries(
415
+ decisionCommands.map((command) => [
416
+ command.key,
417
+ [...(APPLY_PLAN_FOLLOW_UP_COMMAND_OUTPUT_ARTIFACT_APPLY_PRECONDITION_LABELS[command.key] || [])],
418
+ ]),
419
+ );
420
+ const decisionCommandOutputArtifactApplyPreconditionsByKey = Object.fromEntries(
421
+ decisionCommands.map((command) => [
422
+ command.key,
423
+ applyPreconditionsForCommandKey(command.key),
424
+ ]),
425
+ );
426
+ const decisionCommandOutputArtifactApplyPreconditionCountByKey = Object.fromEntries(
427
+ decisionCommands.map((command) => [
428
+ command.key,
429
+ decisionCommandOutputArtifactApplyPreconditionsByKey[command.key]?.length || 0,
430
+ ]),
431
+ );
432
+ const decisionCommandOutputArtifactRequiredApplyPreconditionCountByKey = Object.fromEntries(
433
+ decisionCommands.map((command) => [
434
+ command.key,
435
+ (decisionCommandOutputArtifactApplyPreconditionsByKey[command.key] || [])
436
+ .filter((precondition) => precondition.required).length,
437
+ ]),
438
+ );
439
+ const decisionCommandOutputArtifactSatisfiedApplyPreconditionCountByKey = Object.fromEntries(
440
+ decisionCommands.map((command) => [
441
+ command.key,
442
+ (decisionCommandOutputArtifactApplyPreconditionsByKey[command.key] || [])
443
+ .filter((precondition) => applyPreconditionIsSatisfied(precondition)).length,
444
+ ]),
445
+ );
446
+ const decisionCommandOutputArtifactPendingApplyPreconditionCountByKey = Object.fromEntries(
447
+ decisionCommands.map((command) => [
448
+ command.key,
449
+ (decisionCommandOutputArtifactApplyPreconditionsByKey[command.key] || [])
450
+ .filter((precondition) => !applyPreconditionIsSatisfied(precondition)).length,
451
+ ]),
452
+ );
453
+ const decisionCommandOutputArtifactRequiredPendingApplyPreconditionCountByKey = Object.fromEntries(
454
+ decisionCommands.map((command) => [
455
+ command.key,
456
+ (decisionCommandOutputArtifactApplyPreconditionsByKey[command.key] || [])
457
+ .filter((precondition) => precondition.required && !applyPreconditionIsSatisfied(precondition)).length,
458
+ ]),
459
+ );
460
+ const decisionCommandOutputArtifactManualApplyReadyByKey = Object.fromEntries(
461
+ decisionCommands.map((command) => [
462
+ command.key,
463
+ Boolean(decisionCommandOutputArtifactManualApplyCandidateByKey[command.key])
464
+ && (decisionCommandOutputArtifactRequiredPendingApplyPreconditionCountByKey[command.key] || 0) === 0,
465
+ ]),
466
+ );
467
+ const decisionCommandOutputArtifactManualApplyStatusByKey = Object.fromEntries(
468
+ decisionCommands.map((command) => [
469
+ command.key,
470
+ manualApplyStatus({
471
+ manualApplyCandidate: Boolean(decisionCommandOutputArtifactManualApplyCandidateByKey[command.key]),
472
+ manualApplyReady: Boolean(decisionCommandOutputArtifactManualApplyReadyByKey[command.key]),
473
+ }),
474
+ ]),
475
+ );
476
+ const decisionCommandOutputArtifactManualApplyStatusLabelByKey = Object.fromEntries(
477
+ decisionCommands.map((command) => [
478
+ command.key,
479
+ manualApplyStatusLabel(decisionCommandOutputArtifactManualApplyStatusByKey[command.key]),
480
+ ]),
481
+ );
482
+ const decisionCommandOutputArtifactManualApplyStatusToneByKey = Object.fromEntries(
483
+ decisionCommands.map((command) => [
484
+ command.key,
485
+ manualApplyStatusTone(decisionCommandOutputArtifactManualApplyStatusByKey[command.key]),
486
+ ]),
487
+ );
488
+ const decisionCommandOutputArtifactManualApplyBlockedReasonByKey = Object.fromEntries(
489
+ decisionCommands.map((command) => [
490
+ command.key,
491
+ manualApplyBlockedReason({
492
+ manualApplyCandidate: Boolean(decisionCommandOutputArtifactManualApplyCandidateByKey[command.key]),
493
+ requiredPendingApplyPreconditionCount: decisionCommandOutputArtifactRequiredPendingApplyPreconditionCountByKey[command.key] || 0,
494
+ }).message,
495
+ ]),
496
+ );
497
+ const decisionCommandOutputArtifactManualApplyBlockedReasonCodeByKey = Object.fromEntries(
498
+ decisionCommands.map((command) => [
499
+ command.key,
500
+ manualApplyBlockedReason({
501
+ manualApplyCandidate: Boolean(decisionCommandOutputArtifactManualApplyCandidateByKey[command.key]),
502
+ requiredPendingApplyPreconditionCount: decisionCommandOutputArtifactRequiredPendingApplyPreconditionCountByKey[command.key] || 0,
503
+ }).code,
504
+ ]),
505
+ );
506
+ const decisionNextCommand = decisionCommands[0] || {};
507
+ const decisionNextCommandDisplayLabel = decisionNextCommand.key
508
+ ? decisionCommandDisplayLabelByKey[decisionNextCommand.key] || decisionNextCommand.key
509
+ : "";
510
+ const decisionNextCommandDescription = decisionNextCommand.key
511
+ ? decisionCommandDescriptionByKey[decisionNextCommand.key] || ""
512
+ : "";
513
+ const decisionNextCommandOutputArtifact = decisionNextCommand.key
514
+ ? decisionCommandOutputArtifactByKey[decisionNextCommand.key] || ""
515
+ : "";
516
+ const decisionNextCommandOutputArtifactType = decisionNextCommand.key
517
+ ? decisionCommandOutputArtifactTypeByKey[decisionNextCommand.key] || ""
518
+ : "";
519
+ const decisionNextCommandOutputArtifactAction = decisionNextCommand.key
520
+ ? decisionCommandOutputArtifactActionByKey[decisionNextCommand.key] || ""
521
+ : "";
522
+ const decisionNextCommandOutputArtifactMediaType = decisionNextCommand.key
523
+ ? decisionCommandOutputArtifactMediaTypeByKey[decisionNextCommand.key] || ""
524
+ : "";
525
+ const decisionNextCommandOutputArtifactDisposition = decisionNextCommand.key
526
+ ? decisionCommandOutputArtifactDispositionByKey[decisionNextCommand.key] || ""
527
+ : "";
528
+ const decisionNextCommandOutputArtifactManualApplyCandidate = decisionNextCommand.key
529
+ ? decisionCommandOutputArtifactManualApplyCandidateByKey[decisionNextCommand.key] || false
530
+ : false;
531
+ const decisionNextCommandOutputArtifactRequiresManualReview = decisionNextCommand.key
532
+ ? decisionCommandOutputArtifactRequiresManualReviewByKey[decisionNextCommand.key] || false
533
+ : false;
534
+ const decisionNextCommandOutputArtifactReviewInstruction = decisionNextCommand.key
535
+ ? decisionCommandOutputArtifactReviewInstructionByKey[decisionNextCommand.key] || ""
536
+ : "";
537
+ const decisionNextCommandOutputArtifactRequiresCleanWorkspaceBeforeApply = decisionNextCommand.key
538
+ ? decisionCommandOutputArtifactRequiresCleanWorkspaceBeforeApplyByKey[decisionNextCommand.key] || false
539
+ : false;
540
+ const decisionNextCommandOutputArtifactApplyPreconditionIds = decisionNextCommand.key
541
+ ? decisionCommandOutputArtifactApplyPreconditionIdsByKey[decisionNextCommand.key] || []
542
+ : [];
543
+ const decisionNextCommandOutputArtifactApplyPreconditionLabels = decisionNextCommand.key
544
+ ? decisionCommandOutputArtifactApplyPreconditionLabelsByKey[decisionNextCommand.key] || []
545
+ : [];
546
+ const decisionNextCommandOutputArtifactApplyPreconditions = decisionNextCommand.key
547
+ ? decisionCommandOutputArtifactApplyPreconditionsByKey[decisionNextCommand.key] || []
548
+ : [];
549
+ const decisionNextCommandOutputArtifactApplyPreconditionCount = decisionNextCommand.key
550
+ ? decisionCommandOutputArtifactApplyPreconditionCountByKey[decisionNextCommand.key] || 0
551
+ : 0;
552
+ const decisionNextCommandOutputArtifactRequiredApplyPreconditionCount = decisionNextCommand.key
553
+ ? decisionCommandOutputArtifactRequiredApplyPreconditionCountByKey[decisionNextCommand.key] || 0
554
+ : 0;
555
+ const decisionNextCommandOutputArtifactSatisfiedApplyPreconditionCount = decisionNextCommand.key
556
+ ? decisionCommandOutputArtifactSatisfiedApplyPreconditionCountByKey[decisionNextCommand.key] || 0
557
+ : 0;
558
+ const decisionNextCommandOutputArtifactPendingApplyPreconditionCount = decisionNextCommand.key
559
+ ? decisionCommandOutputArtifactPendingApplyPreconditionCountByKey[decisionNextCommand.key] || 0
560
+ : 0;
561
+ const decisionNextCommandOutputArtifactRequiredPendingApplyPreconditionCount = decisionNextCommand.key
562
+ ? decisionCommandOutputArtifactRequiredPendingApplyPreconditionCountByKey[decisionNextCommand.key] || 0
563
+ : 0;
564
+ const decisionNextCommandOutputArtifactManualApplyReady = decisionNextCommand.key
565
+ ? decisionCommandOutputArtifactManualApplyReadyByKey[decisionNextCommand.key] || false
566
+ : false;
567
+ const decisionNextCommandOutputArtifactManualApplyStatus = decisionNextCommand.key
568
+ ? decisionCommandOutputArtifactManualApplyStatusByKey[decisionNextCommand.key] || "not-applicable"
569
+ : "not-applicable";
570
+ const decisionNextCommandOutputArtifactManualApplyStatusLabel = decisionNextCommand.key
571
+ ? decisionCommandOutputArtifactManualApplyStatusLabelByKey[decisionNextCommand.key] || "Review only"
572
+ : "Review only";
573
+ const decisionNextCommandOutputArtifactManualApplyStatusTone = decisionNextCommand.key
574
+ ? decisionCommandOutputArtifactManualApplyStatusToneByKey[decisionNextCommand.key] || "neutral"
575
+ : "neutral";
576
+ const decisionNextCommandOutputArtifactManualApplyBlockedReason = decisionNextCommand.key
577
+ ? decisionCommandOutputArtifactManualApplyBlockedReasonByKey[decisionNextCommand.key] || ""
578
+ : "";
579
+ const decisionNextCommandOutputArtifactManualApplyBlockedReasonCode = decisionNextCommand.key
580
+ ? decisionCommandOutputArtifactManualApplyBlockedReasonCodeByKey[decisionNextCommand.key] || ""
581
+ : "";
582
+ const operatorRunbookStageSelection = failures > 0
583
+ ? {}
584
+ : {
585
+ strategy: "optional-preview-before-required-manual-edit",
586
+ decision: {
587
+ action: "offer-optional-preview",
588
+ stageKey: "previewArtifacts",
589
+ stageKind: "local-output-preview",
590
+ required: false,
591
+ hasCommands: true,
592
+ commandCount: decisionCommands.length,
593
+ commandKeys: ["reviewCheckReport", "proposalPatchPreview"],
594
+ commands: decisionCommands,
595
+ commandByKey: decisionCommandByKey,
596
+ commandStepByKey: decisionCommandStepByKey,
597
+ commandRunPolicyByKey: decisionCommandRunPolicyByKey,
598
+ commandSafetyLevelByKey: decisionCommandSafetyLevelByKey,
599
+ commandArgsByKey: decisionCommandArgsByKey,
600
+ commandStringByKey: decisionCommandStringByKey,
601
+ commandDisplayLabelByKey: decisionCommandDisplayLabelByKey,
602
+ commandDescriptionByKey: decisionCommandDescriptionByKey,
603
+ commandOutputArtifactByKey: decisionCommandOutputArtifactByKey,
604
+ commandOutputArtifactTypeByKey: decisionCommandOutputArtifactTypeByKey,
605
+ commandOutputArtifactActionByKey: decisionCommandOutputArtifactActionByKey,
606
+ commandOutputArtifactMediaTypeByKey: decisionCommandOutputArtifactMediaTypeByKey,
607
+ commandOutputArtifactDispositionByKey: decisionCommandOutputArtifactDispositionByKey,
608
+ commandOutputArtifactManualApplyCandidateByKey: decisionCommandOutputArtifactManualApplyCandidateByKey,
609
+ commandOutputArtifactRequiresManualReviewByKey: decisionCommandOutputArtifactRequiresManualReviewByKey,
610
+ commandOutputArtifactReviewInstructionByKey: decisionCommandOutputArtifactReviewInstructionByKey,
611
+ commandOutputArtifactRequiresCleanWorkspaceBeforeApplyByKey: decisionCommandOutputArtifactRequiresCleanWorkspaceBeforeApplyByKey,
612
+ commandOutputArtifactApplyPreconditionIdsByKey: decisionCommandOutputArtifactApplyPreconditionIdsByKey,
613
+ commandOutputArtifactApplyPreconditionLabelsByKey: decisionCommandOutputArtifactApplyPreconditionLabelsByKey,
614
+ commandOutputArtifactApplyPreconditionsByKey: decisionCommandOutputArtifactApplyPreconditionsByKey,
615
+ commandOutputArtifactApplyPreconditionCountByKey: decisionCommandOutputArtifactApplyPreconditionCountByKey,
616
+ commandOutputArtifactRequiredApplyPreconditionCountByKey: decisionCommandOutputArtifactRequiredApplyPreconditionCountByKey,
617
+ commandOutputArtifactSatisfiedApplyPreconditionCountByKey: decisionCommandOutputArtifactSatisfiedApplyPreconditionCountByKey,
618
+ commandOutputArtifactPendingApplyPreconditionCountByKey: decisionCommandOutputArtifactPendingApplyPreconditionCountByKey,
619
+ commandOutputArtifactRequiredPendingApplyPreconditionCountByKey: decisionCommandOutputArtifactRequiredPendingApplyPreconditionCountByKey,
620
+ commandOutputArtifactManualApplyReadyByKey: decisionCommandOutputArtifactManualApplyReadyByKey,
621
+ commandOutputArtifactManualApplyStatusByKey: decisionCommandOutputArtifactManualApplyStatusByKey,
622
+ commandOutputArtifactManualApplyStatusLabelByKey: decisionCommandOutputArtifactManualApplyStatusLabelByKey,
623
+ commandOutputArtifactManualApplyStatusToneByKey: decisionCommandOutputArtifactManualApplyStatusToneByKey,
624
+ commandOutputArtifactManualApplyBlockedReasonByKey: decisionCommandOutputArtifactManualApplyBlockedReasonByKey,
625
+ commandOutputArtifactManualApplyBlockedReasonCodeByKey: decisionCommandOutputArtifactManualApplyBlockedReasonCodeByKey,
626
+ nextCommandEntry: decisionNextCommand,
627
+ nextCommandKey: decisionNextCommand.key || "",
628
+ nextCommandDisplayLabel: decisionNextCommandDisplayLabel,
629
+ nextCommandDescription: decisionNextCommandDescription,
630
+ nextCommandOutputArtifact: decisionNextCommandOutputArtifact,
631
+ nextCommandOutputArtifactType: decisionNextCommandOutputArtifactType,
632
+ nextCommandOutputArtifactAction: decisionNextCommandOutputArtifactAction,
633
+ nextCommandOutputArtifactMediaType: decisionNextCommandOutputArtifactMediaType,
634
+ nextCommandOutputArtifactDisposition: decisionNextCommandOutputArtifactDisposition,
635
+ nextCommandOutputArtifactManualApplyCandidate: decisionNextCommandOutputArtifactManualApplyCandidate,
636
+ nextCommandOutputArtifactRequiresManualReview: decisionNextCommandOutputArtifactRequiresManualReview,
637
+ nextCommandOutputArtifactReviewInstruction: decisionNextCommandOutputArtifactReviewInstruction,
638
+ nextCommandOutputArtifactRequiresCleanWorkspaceBeforeApply: decisionNextCommandOutputArtifactRequiresCleanWorkspaceBeforeApply,
639
+ nextCommandOutputArtifactApplyPreconditionIds: decisionNextCommandOutputArtifactApplyPreconditionIds,
640
+ nextCommandOutputArtifactApplyPreconditionLabels: decisionNextCommandOutputArtifactApplyPreconditionLabels,
641
+ nextCommandOutputArtifactApplyPreconditions: decisionNextCommandOutputArtifactApplyPreconditions,
642
+ nextCommandOutputArtifactApplyPreconditionCount: decisionNextCommandOutputArtifactApplyPreconditionCount,
643
+ nextCommandOutputArtifactRequiredApplyPreconditionCount: decisionNextCommandOutputArtifactRequiredApplyPreconditionCount,
644
+ nextCommandOutputArtifactSatisfiedApplyPreconditionCount: decisionNextCommandOutputArtifactSatisfiedApplyPreconditionCount,
645
+ nextCommandOutputArtifactPendingApplyPreconditionCount: decisionNextCommandOutputArtifactPendingApplyPreconditionCount,
646
+ nextCommandOutputArtifactRequiredPendingApplyPreconditionCount: decisionNextCommandOutputArtifactRequiredPendingApplyPreconditionCount,
647
+ nextCommandOutputArtifactManualApplyReady: decisionNextCommandOutputArtifactManualApplyReady,
648
+ nextCommandOutputArtifactManualApplyStatus: decisionNextCommandOutputArtifactManualApplyStatus,
649
+ nextCommandOutputArtifactManualApplyStatusLabel: decisionNextCommandOutputArtifactManualApplyStatusLabel,
650
+ nextCommandOutputArtifactManualApplyStatusTone: decisionNextCommandOutputArtifactManualApplyStatusTone,
651
+ nextCommandOutputArtifactManualApplyBlockedReason: decisionNextCommandOutputArtifactManualApplyBlockedReason,
652
+ nextCommandOutputArtifactManualApplyBlockedReasonCode: decisionNextCommandOutputArtifactManualApplyBlockedReasonCode,
653
+ nextCommandStep: decisionNextCommand.step || 0,
654
+ nextCommand: decisionNextCommand.command || "",
655
+ nextCommandArgs: decisionNextCommand.commandArgs || [],
656
+ nextCommandRunPolicy: decisionNextCommand.runPolicy || "",
657
+ nextCommandSafetyLevel: decisionNextCommand.safetyLevel || "",
658
+ nextCommandSafety: decisionNextCommand.safety || {},
659
+ runPolicy: "optional-local-output-preview",
660
+ safety: {
661
+ level: nextStageSummary.writesLocalFiles ? "local-output" : "read-only",
662
+ writesLocalFiles: nextStageSummary.writesLocalFiles,
663
+ writesOutputArtifacts: nextStageSummary.writesOutputArtifacts,
664
+ mutatesLocalState: nextStageSummary.mutatesLocalState,
665
+ mutatesProfile: nextStageSummary.mutatesProfile,
666
+ mutatesReviewFile: nextStageSummary.mutatesReviewFile,
667
+ mutatesSkillFiles: nextStageSummary.mutatesSkillFiles,
668
+ callsExternalAiApis: nextStageSummary.callsExternalAiApis,
669
+ requiresCleanWorkspace: nextStageSummary.requiresCleanWorkspace,
670
+ reason: "The selected decision only writes optional local preview artifacts and does not mutate learning, review, or skill files.",
671
+ },
672
+ nextRequiredStageKey: nextRequiredStage?.key || "",
673
+ nextRequiredCommandStageKey: nextRequiredCommandStage?.key || "",
674
+ requiresOperatorActionBeforeRequiredCommands: true,
675
+ reason: "Offer optional local preview artifacts first; the required path still starts with manual skill edits before read-only command gates.",
676
+ },
677
+ stageOrder: operatorRunbookStageKeys,
678
+ nextStageKey: "previewArtifacts",
679
+ nextStageCommandKeys: ["reviewCheckReport", "proposalPatchPreview"],
680
+ nextStage: nextStageSummary,
681
+ nextRequiredStageKey: nextRequiredStage?.key || "",
682
+ nextRequiredStageCommandKeys: nextRequiredStage?.commandKeys || [],
683
+ nextRequiredStage: nextRequiredStageSummary,
684
+ nextRequiredCommandStageKey: nextRequiredCommandStage?.key || "",
685
+ nextRequiredCommandStageCommandKeys: nextRequiredCommandStage?.commandKeys || [],
686
+ nextRequiredCommandStage: nextRequiredCommandStageSummary,
687
+ reason: "Offer optional local preview artifacts first, then require the manual skill edit before read-only review and strict gates.",
688
+ };
689
+ const operatorRunbook = {
690
+ version: 1,
691
+ executable: failures === 0,
692
+ blocked: failures > 0,
693
+ stageCount: operatorRunbookStages.length,
694
+ requiredStageCount: operatorRunbookStages.filter((stage) => stage.required).length,
695
+ commandStageCount: operatorRunbookStages.filter((stage) => stage.commandKeys.length > 0).length,
696
+ nextStageKey: failures > 0 ? "" : "previewArtifacts",
697
+ nextStageCommandKeys: failures > 0 ? [] : ["reviewCheckReport", "proposalPatchPreview"],
698
+ nextRequiredStageKey: nextRequiredStage?.key || "",
699
+ nextRequiredStageCommandKeys: nextRequiredStage?.commandKeys || [],
700
+ nextRequiredCommandStageKey: nextRequiredCommandStage?.key || "",
701
+ nextRequiredCommandStageCommandKeys: nextRequiredCommandStage?.commandKeys || [],
702
+ stageSelection: operatorRunbookStageSelection,
703
+ stageKeys: operatorRunbookStageKeys,
704
+ stageByKey: operatorRunbookStageByKey,
705
+ stages: operatorRunbookStages,
706
+ reason: failures > 0
707
+ ? "Command contract failures must be fixed before running the operator runbook."
708
+ : "Generate optional local review artifacts, apply accepted skill deltas manually, then run read-only review and strict readiness gates.",
709
+ };
710
+ return {
711
+ version: 1,
712
+ valid: failures === 0,
713
+ status: failures > 0 ? "fail" : "pass",
714
+ commandCount: Object.keys(commandArgs).length,
715
+ requiredKeys,
716
+ missingCommandKeys,
717
+ unexpectedCommandKeys,
718
+ baseCommand: [...APPLY_PLAN_BASE_COMMAND],
719
+ reviewFileRequired: true,
720
+ reviewFile,
721
+ forbiddenFlags: [...APPLY_PLAN_FORBIDDEN_FLAGS],
722
+ checkCount,
723
+ passCount: passes,
724
+ warningCount: warnings,
725
+ failureCount: failures,
726
+ failedCheckIds: failedChecks.map((check) => check.id),
727
+ failedChecks,
728
+ nextCommandKey,
729
+ nextCommand,
730
+ nextCommandArgs,
731
+ nextCommandRunPolicy,
732
+ nextCommandSafety,
733
+ commandSequenceCount: commandSequence.length,
734
+ commandSequence,
735
+ commandSequenceSummary,
736
+ commandSequenceKeys,
737
+ commandSequenceByKey,
738
+ operatorRunbook,
739
+ nextAction: failures > 0
740
+ ? "Fix command contract failures before running follow-up commands."
741
+ : "Run reviewCheckJson after manual skill edits, then use strictGate before marking proposals applied.",
742
+ checks,
743
+ summary: {
744
+ failures,
745
+ warnings,
746
+ passes,
747
+ total: checkCount,
748
+ },
749
+ };
750
+ }