@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
@@ -1,2337 +1,16 @@
1
- // Read-only learning signal registry for local AI/agent development loops.
2
-
3
- import { existsSync, readFileSync, statSync } from "node:fs";
4
- import path from "node:path";
5
-
6
- import {
7
- auditLearningProfile,
8
- defaultLearningFile,
9
- defaultLearningUsageFile,
10
- learningStats,
11
- learningUsageStats,
12
- loadLearningProfile,
13
- } from "./learn.mjs";
14
- import { collectWorkspaceReport, defaultLearningEvalPath } from "./workspace.mjs";
15
-
16
- export const DEFAULT_SIGNAL_EVAL_FILES = [
17
- "route-eval.json",
18
- "route-eval-report.json",
19
- "prompt-eval.json",
20
- "prompt-eval-report.json",
21
- "pack-eval.json",
22
- "pack-eval-report.json",
23
- "learning-eval.json",
24
- "learning-eval-report.json",
25
- ];
26
-
27
- function countBy(items, keyFn) {
28
- const counts = {};
29
- for (const item of items || []) {
30
- const key = keyFn(item);
31
- if (!key) continue;
32
- counts[key] = (counts[key] || 0) + 1;
33
- }
34
- return counts;
35
- }
36
-
37
- function readinessCountByStatus(items = []) {
38
- const counts = {
39
- pass: 0,
40
- info: 0,
41
- warn: 0,
42
- fail: 0,
43
- missing: 0,
44
- template: 0,
45
- unknown: 0,
46
- };
47
- for (const item of items || []) {
48
- const status = String(item?.status || "unknown").trim() || "unknown";
49
- if (Object.hasOwn(counts, status)) {
50
- counts[status] += 1;
51
- } else {
52
- counts.unknown += 1;
53
- }
54
- }
55
- return counts;
56
- }
57
-
58
- function previewText(text, maxLength = 120) {
59
- const normalized = String(text || "").replace(/\s+/g, " ").trim();
60
- if (normalized.length <= maxLength) return normalized;
61
- return `${normalized.slice(0, maxLength - 1)}...`;
62
- }
63
-
64
- function statusRank(status) {
65
- if (status === "fail") return 3;
66
- if (status === "warn" || status === "missing") return 2;
67
- if (status === "template" || status === "unknown") return 1;
68
- return 0;
69
- }
70
-
71
- function worstStatus(statuses, fallback = "pass") {
72
- return [...statuses].sort((a, b) => statusRank(b) - statusRank(a))[0] || fallback;
73
- }
74
-
75
- function shellQuote(value) {
76
- const text = String(value || "");
77
- if (/^[A-Za-z0-9_./:=@+-]+$/.test(text)) return text;
78
- return `'${text.replace(/'/g, "'\\''")}'`;
79
- }
80
-
81
- function commandFromArgs(args = []) {
82
- return args.map(shellQuote).join(" ");
83
- }
84
-
85
- function commandSpec(args = []) {
86
- const commandArgs = args.map((item) => String(item));
87
- return {
88
- commandArgs,
89
- command: commandFromArgs(commandArgs),
90
- };
91
- }
92
-
93
- function yesNo(value) {
94
- return value ? "yes" : "no";
95
- }
96
-
97
- function listItem(label, value) {
98
- return `- ${label}: ${value}`;
99
- }
100
-
101
- function optionalGapDetail(check = {}) {
102
- if (check.id === "check-capture") {
103
- return {
104
- id: check.id,
105
- label: check.label || "Check learning capture",
106
- status: check.status || "info",
107
- summary: check.summary || "",
108
- reason: "No real warn/fail check result has been intentionally captured into the local learning profile yet.",
109
- nextCondition: "Run `design-ai check <artifact.md> --learn --yes` only after reviewing an actual warning or failure that should improve future outputs.",
110
- automationPolicy: "Do not emit placeholder mutation commands for this advisory gap; wait for real check evidence.",
111
- };
112
- }
113
-
114
- return {
115
- id: check.id || "unknown",
116
- label: check.label || "Optional readiness check",
117
- status: check.status || "info",
118
- summary: check.summary || "",
119
- reason: "Optional evidence is incomplete.",
120
- nextCondition: "Collect real local evidence before treating this optional signal as complete.",
121
- automationPolicy: "Keep this advisory unless a future required gate explicitly depends on it.",
122
- };
123
- }
124
-
125
- function renderOptionalGapDetails(lines, readiness = {}) {
126
- const details = Array.isArray(readiness.optionalGapDetails) ? readiness.optionalGapDetails : [];
127
- if (details.length === 0) return;
128
- lines.push("", "Optional gap details:");
129
- for (const detail of details) {
130
- lines.push(`- ${detail.id || "unknown"}: ${detail.reason || detail.summary || ""}`);
131
- if (detail.nextCondition) lines.push(` Next condition: ${detail.nextCondition}`);
132
- if (detail.automationPolicy) lines.push(` Automation policy: ${detail.automationPolicy}`);
133
- }
134
- }
135
-
136
- function renderReadinessCheckIndex(lines, readiness = {}) {
137
- const requiredIds = Array.isArray(readiness.requiredCheckIds) ? readiness.requiredCheckIds : [];
138
- const optionalIds = Array.isArray(readiness.optionalCheckIds) ? readiness.optionalCheckIds : [];
139
- const statusById = readiness.checkStatusById && typeof readiness.checkStatusById === "object"
140
- ? readiness.checkStatusById
141
- : {};
142
- const requiredById = readiness.checkRequiredById && typeof readiness.checkRequiredById === "object"
143
- ? readiness.checkRequiredById
144
- : {};
145
- const checks = Array.isArray(readiness.checks) ? readiness.checks : [];
146
- const indexIds = [...new Set([
147
- ...checks.map((item) => item?.id).filter(Boolean),
148
- ...requiredIds,
149
- ...optionalIds,
150
- ...Object.keys(statusById),
151
- ...Object.keys(requiredById),
152
- ])];
153
- if (indexIds.length === 0) return;
154
-
155
- lines.push("", "Readiness check index:");
156
- lines.push(`- Required ids: ${requiredIds.length > 0 ? requiredIds.join(", ") : "none"}`);
157
- lines.push(`- Optional ids: ${optionalIds.length > 0 ? optionalIds.join(", ") : "none"}`);
158
- lines.push(`- Status index: ${indexIds.map((id) => `${id}=${statusById[id] || "unknown"}`).join(", ")}`);
159
- lines.push(`- Required index: ${indexIds.map((id) => `${id}=${yesNo(Boolean(requiredById[id]))}`).join(", ")}`);
160
- if (readiness.checkCountByStatus && typeof readiness.checkCountByStatus === "object") {
161
- const formatCounts = (counts = {}) => ["pass", "info", "warn", "fail", "missing", "template", "unknown"]
162
- .map((key) => `${key}=${counts[key] ?? 0}`)
163
- .join(", ");
164
- lines.push(`- Status counts: ${formatCounts(readiness.checkCountByStatus)}`);
165
- lines.push(`- Required status counts: ${formatCounts(readiness.requiredCheckCountByStatus)}`);
166
- lines.push(`- Optional status counts: ${formatCounts(readiness.optionalCheckCountByStatus)}`);
167
- }
168
- }
169
-
170
- function inferSignalKind(payload, filePath = "") {
171
- const sourceName = path.basename(filePath).toLowerCase();
172
- const cases = Array.isArray(payload?.cases) ? payload.cases : [];
173
-
174
- if (sourceName.includes("route")) return "route-eval";
175
- if (sourceName.includes("prompt")) return "prompt-eval";
176
- if (sourceName.includes("pack")) return "pack-eval";
177
- if (sourceName.includes("learning")) return "learning-eval";
178
-
179
- if (payload?.sourceRouteVersion) return "route-eval";
180
- if (payload?.sourcePromptVersion) return "prompt-eval";
181
- if (payload?.sourcePackVersion) return "pack-eval";
182
- if (payload?.sourceProfile) return "learning-eval";
183
- if (cases.some((item) => item && typeof item === "object" && "topRouteId" in item)) return "route-eval";
184
- if (cases.some((item) => item && typeof item === "object" && "missingPromptFragments" in item)) return "prompt-eval";
185
- if (cases.some((item) => item && typeof item === "object" && ("contextStatus" in item || "pack" in item))) return "pack-eval";
186
- if (cases.some((item) => item && typeof item === "object" && ("selectedEntryIds" in item || "expectedSelectedIds" in item))) return "learning-eval";
187
-
188
- return "unknown-eval";
189
- }
190
-
191
- function summarizeCaseCounts(payload) {
192
- const summary = payload?.summary && typeof payload.summary === "object" ? payload.summary : null;
193
- const cases = Array.isArray(payload?.cases) ? payload.cases : [];
194
- if (summary) {
195
- return {
196
- caseCount: Number.isInteger(summary.total) ? summary.total : cases.length,
197
- passed: Number.isInteger(summary.pass) ? summary.pass : 0,
198
- warned: Number.isInteger(summary.warn) ? summary.warn : 0,
199
- failed: Number.isInteger(summary.fail) ? summary.fail : 0,
200
- };
201
- }
202
-
203
- return {
204
- caseCount: cases.length,
205
- passed: cases.filter((item) => item?.status === "pass").length,
206
- warned: cases.filter((item) => item?.status === "warn").length,
207
- failed: cases.filter((item) => item?.status === "fail").length,
208
- };
209
- }
210
-
211
- export function summarizeSignalEvalFile(filePath) {
212
- const resolvedFile = path.resolve(filePath);
213
- if (!existsSync(resolvedFile)) {
214
- return {
215
- file: resolvedFile,
216
- exists: false,
217
- kind: inferSignalKind(null, resolvedFile),
218
- shape: "missing",
219
- status: "missing",
220
- caseCount: 0,
221
- passed: 0,
222
- warned: 0,
223
- failed: 0,
224
- generatedAt: "",
225
- error: "",
226
- };
227
- }
228
-
229
- let payload = null;
230
- try {
231
- payload = JSON.parse(readFileSync(resolvedFile, "utf8"));
232
- } catch {
233
- return {
234
- file: resolvedFile,
235
- exists: true,
236
- kind: inferSignalKind(null, resolvedFile),
237
- shape: "invalid-json",
238
- status: "fail",
239
- caseCount: 0,
240
- passed: 0,
241
- warned: 0,
242
- failed: 0,
243
- generatedAt: "",
244
- error: "Signal eval file is not valid JSON.",
245
- };
246
- }
247
-
248
- const hasCases = Array.isArray(payload?.cases);
249
- const isReport = typeof payload?.status === "string" || Boolean(payload?.summary);
250
- const counts = summarizeCaseCounts(payload);
251
- const status = isReport
252
- ? String(payload.status || worstStatus([
253
- counts.failed > 0 ? "fail" : "",
254
- counts.warned > 0 ? "warn" : "",
255
- "pass",
256
- ])).trim()
257
- : "template";
258
-
259
- return {
260
- file: resolvedFile,
261
- exists: true,
262
- kind: inferSignalKind(payload, resolvedFile),
263
- shape: isReport ? "report" : "template",
264
- status: hasCases ? status : "fail",
265
- caseCount: counts.caseCount,
266
- passed: counts.passed,
267
- warned: counts.warned,
268
- failed: counts.failed,
269
- generatedAt: String(payload?.generatedAt || ""),
270
- error: hasCases ? "" : "Signal eval file must include a cases array.",
271
- };
272
- }
273
-
274
- function resolveSignalFiles({ signalSource = "", root = process.cwd(), extraFiles = [] } = {}) {
275
- const resolvedSource = signalSource ? path.resolve(signalSource) : path.resolve(root);
276
- const resolvedExtraFiles = (Array.isArray(extraFiles) ? extraFiles : [])
277
- .filter(Boolean)
278
- .map((filePath) => path.resolve(filePath))
279
- .filter((filePath) => existsSync(filePath));
280
- const uniqueFiles = (files = []) => [...new Set([...files, ...resolvedExtraFiles])];
281
- if (existsSync(resolvedSource) && statSync(resolvedSource).isDirectory()) {
282
- return uniqueFiles(DEFAULT_SIGNAL_EVAL_FILES
283
- .map((fileName) => path.join(resolvedSource, fileName))
284
- .filter((filePath) => existsSync(filePath)));
285
- }
286
- if (signalSource) return uniqueFiles([resolvedSource]);
287
- return uniqueFiles([]);
288
- }
289
-
290
- function evalReportPathForTemplate(filePath = "") {
291
- const resolvedFile = path.resolve(filePath);
292
- const dir = path.dirname(resolvedFile);
293
- const ext = path.extname(resolvedFile);
294
- const base = path.basename(resolvedFile, ext);
295
- if (base.endsWith("-report")) return resolvedFile;
296
- if (base.endsWith("-eval")) return path.join(dir, `${base}-report${ext || ".json"}`);
297
- return path.join(dir, `${base}-report${ext || ".json"}`);
298
- }
299
-
300
- function defaultLearningEvalReportPath(filePath = defaultLearningFile()) {
301
- return evalReportPathForTemplate(defaultLearningEvalPath(filePath));
302
- }
303
-
304
- function evalSignalEvidenceKey(file = {}) {
305
- return `${file.kind || "unknown-eval"}\n${path.dirname(file.file || "")}`;
306
- }
307
-
308
- function summarizeEvalSignals(evalFiles = []) {
309
- const reportKeys = new Set(
310
- evalFiles
311
- .filter((item) => item.shape === "report")
312
- .map((item) => evalSignalEvidenceKey(item)),
313
- );
314
- const unresolvedTemplates = evalFiles.filter((item) => (
315
- item.shape === "template" && !reportKeys.has(evalSignalEvidenceKey(item))
316
- ));
317
- return {
318
- reports: evalFiles.filter((item) => item.shape === "report").length,
319
- templates: unresolvedTemplates.length,
320
- rawTemplates: evalFiles.filter((item) => item.shape === "template").length,
321
- templateFiles: unresolvedTemplates,
322
- failed: evalFiles.filter((item) => item.status === "fail").length,
323
- warned: evalFiles.filter((item) => item.status === "warn").length,
324
- passed: evalFiles.filter((item) => item.status === "pass").length,
325
- };
326
- }
327
-
328
- function summarizeCheckCapture(profile) {
329
- const entries = (profile.entries || []).filter((entry) => String(entry.source || "").startsWith("check:"));
330
- const sorted = [...entries].sort((a, b) => String(b.createdAt || "").localeCompare(String(a.createdAt || "")));
331
- return {
332
- count: entries.length,
333
- categoryCounts: countBy(entries, (entry) => entry.category || "other"),
334
- sourceCounts: countBy(entries, (entry) => entry.source || "check:artifact"),
335
- latestEntries: sorted.slice(0, 5).map((entry) => ({
336
- id: entry.id,
337
- category: entry.category,
338
- source: entry.source,
339
- createdAt: entry.createdAt || "",
340
- textPreview: previewText(entry.text),
341
- })),
342
- };
343
- }
344
-
345
- function summarizeWorkspace(report) {
346
- const nextActions = Array.isArray(report?.nextActions) ? report.nextActions : [];
347
- const actionCounts = countBy(nextActions, (item) => item.level || "info");
348
- return {
349
- root: report?.context?.root || "",
350
- version: report?.context?.version || "",
351
- git: {
352
- isRepo: Boolean(report?.git?.isRepo),
353
- branch: report?.git?.branch || "",
354
- clean: Boolean(report?.git?.clean),
355
- ahead: Number.isInteger(report?.git?.ahead) ? report.git.ahead : 0,
356
- behind: Number.isInteger(report?.git?.behind) ? report.git.behind : 0,
357
- },
358
- repository: {
359
- status: report?.repository?.status || "",
360
- canonical: Boolean(report?.repository?.canonical),
361
- },
362
- learning: {
363
- status: report?.learning?.readiness?.status || report?.learning?.auditSummary?.status || "",
364
- reason: report?.learning?.readiness?.reason || "",
365
- },
366
- learningUsage: report?.learningUsage?.readiness || null,
367
- learningEval: report?.learningEval?.freshness || report?.learningEval?.readiness || null,
368
- nextActionCounts: actionCounts,
369
- nextActionCount: nextActions.length,
370
- };
371
- }
372
-
373
- function buildRecommendations({ audit, usage, evals, checkCapture, workspace }) {
374
- const recommendations = [];
375
- if (!audit.exists) {
376
- recommendations.push({
377
- level: "info",
378
- text: "Initialize or import a local learning profile before treating signal registry output as complete.",
379
- });
380
- } else if (audit.summary.failures > 0) {
381
- recommendations.push({
382
- level: "fail",
383
- text: "Fix learning profile audit failures before using local learning signals as release evidence.",
384
- });
385
- } else if (audit.summary.warnings > 0) {
386
- recommendations.push({
387
- level: "warn",
388
- text: "Review learning profile audit warnings before promoting learned preferences.",
389
- });
390
- }
391
-
392
- if (!usage.exists || usage.eventCount === 0) {
393
- recommendations.push({
394
- level: "info",
395
- text: "Run prompt or pack with --with-learning to create usage signals for selected learning entries.",
396
- });
397
- }
398
- if (usage.staleSelectedEntryCount > 0) {
399
- recommendations.push({
400
- level: "warn",
401
- text: "Usage sidecar references learning entry ids that are no longer active.",
402
- });
403
- }
404
- if (evals.files.length === 0) {
405
- recommendations.push({
406
- level: "warn",
407
- text: "No route/prompt/pack/learning eval signal files were found; generate eval reports before relying on this registry as a gate.",
408
- });
409
- }
410
- if (evals.failed > 0) {
411
- recommendations.push({
412
- level: "fail",
413
- text: "At least one eval signal report failed; inspect the failing checkpoint before continuing agent development.",
414
- });
415
- }
416
- if (evals.templates > 0) {
417
- recommendations.push({
418
- level: "warn",
419
- text: "Some eval signals are templates rather than executed reports; replay them with --eval --strict for stronger evidence.",
420
- });
421
- }
422
- if (checkCapture.count === 0) {
423
- recommendations.push({
424
- level: "info",
425
- text: "No check learning capture entries are present yet; run check --learn --yes on real warnings/failures when appropriate.",
426
- });
427
- }
428
- if (workspace.nextActionCounts.fail > 0 || workspace.nextActionCounts.warn > 0) {
429
- recommendations.push({
430
- level: workspace.nextActionCounts.fail > 0 ? "fail" : "warn",
431
- text: "Workspace readiness has unresolved next actions; review workspace output before using signals as release evidence.",
432
- });
433
- }
434
- return recommendations;
435
- }
436
-
437
- function signalCheck({
438
- id,
439
- label,
440
- status,
441
- required,
442
- summary,
443
- evidence = {},
444
- }) {
445
- return {
446
- id,
447
- label,
448
- status,
449
- required: Boolean(required),
450
- summary,
451
- evidence,
452
- };
453
- }
454
-
455
- function buildLearningSignalReadiness({
456
- audit,
457
- usage,
458
- evals,
459
- checkCapture,
460
- workspace,
461
- agentDevelopment,
462
- status,
463
- }) {
464
- const profileStatus = !audit.exists
465
- ? "missing"
466
- : audit.summary.failures > 0
467
- ? "fail"
468
- : audit.summary.warnings > 0
469
- ? "warn"
470
- : "pass";
471
- const usageStatus = usage.staleSelectedEntryCount > 0
472
- ? "warn"
473
- : usage.exists && usage.eventCount > 0
474
- ? "pass"
475
- : "info";
476
- const evalStatus = evals.failed > 0
477
- ? "fail"
478
- : evals.warned > 0 || evals.templates > 0 || evals.count === 0
479
- ? "warn"
480
- : "pass";
481
- const checkCaptureStatus = checkCapture.count > 0 ? "pass" : "info";
482
- const workspaceStatus = workspace.nextActionCounts.fail > 0
483
- ? "fail"
484
- : workspace.nextActionCounts.warn > 0
485
- ? "warn"
486
- : "pass";
487
- const agentStatus = agentDevelopment.status || "unknown";
488
- const checks = [
489
- signalCheck({
490
- id: "learning-profile",
491
- label: "Learning profile",
492
- status: profileStatus,
493
- required: true,
494
- summary: audit.exists
495
- ? `Profile has ${audit.count} entries with ${audit.summary.failures} audit failure(s) and ${audit.summary.warnings} warning(s).`
496
- : "Learning profile is missing.",
497
- evidence: {
498
- exists: Boolean(audit.exists),
499
- entries: audit.count,
500
- failures: audit.summary.failures,
501
- warnings: audit.summary.warnings,
502
- },
503
- }),
504
- signalCheck({
505
- id: "usage-sidecar",
506
- label: "Usage sidecar",
507
- status: usageStatus,
508
- required: false,
509
- summary: usage.exists && usage.eventCount > 0
510
- ? `Usage sidecar has ${usage.eventCount} event(s) and ${usage.staleSelectedEntryCount} stale selected id(s).`
511
- : "Usage sidecar has no prompt/pack usage events yet.",
512
- evidence: {
513
- exists: Boolean(usage.exists),
514
- events: usage.eventCount,
515
- staleSelectedEntryCount: usage.staleSelectedEntryCount,
516
- },
517
- }),
518
- signalCheck({
519
- id: "eval-signals",
520
- label: "Eval signals",
521
- status: evalStatus,
522
- required: true,
523
- summary: evals.count > 0
524
- ? `Eval signals include ${evals.reports} report(s), ${evals.templates} unresolved template(s), ${evals.failed} failed report(s), and ${evals.warned} warned report(s).`
525
- : "No route, prompt, pack, or learning eval signal files were found.",
526
- evidence: {
527
- files: evals.count,
528
- reports: evals.reports,
529
- templates: evals.templates,
530
- failed: evals.failed,
531
- warned: evals.warned,
532
- },
533
- }),
534
- signalCheck({
535
- id: "check-capture",
536
- label: "Check learning capture",
537
- status: checkCaptureStatus,
538
- required: false,
539
- summary: checkCapture.count > 0
540
- ? `Profile includes ${checkCapture.count} check-capture learning entr${checkCapture.count === 1 ? "y" : "ies"}.`
541
- : "No check-capture entries are present; this is advisory until real warn/fail checks are captured.",
542
- evidence: {
543
- entries: checkCapture.count,
544
- categoryCounts: checkCapture.categoryCounts,
545
- },
546
- }),
547
- signalCheck({
548
- id: "workspace-readiness",
549
- label: "Workspace readiness",
550
- status: workspaceStatus,
551
- required: true,
552
- summary: `Workspace has ${workspace.nextActionCounts.fail || 0} fail action(s), ${workspace.nextActionCounts.warn || 0} warn action(s), and ${workspace.nextActionCount || 0} total next action(s).`,
553
- evidence: {
554
- fail: workspace.nextActionCounts.fail || 0,
555
- warn: workspace.nextActionCounts.warn || 0,
556
- nextActionCount: workspace.nextActionCount || 0,
557
- },
558
- }),
559
- signalCheck({
560
- id: "agent-development",
561
- label: "Agent development backlog",
562
- status: agentStatus,
563
- required: true,
564
- summary: `Agent backlog has ${agentDevelopment.actionCount || 0} action(s): ${agentDevelopment.p0Count || 0} P0, ${agentDevelopment.p1Count || 0} P1, ${agentDevelopment.p2Count || 0} P2, ${agentDevelopment.p3Count || 0} P3.`,
565
- evidence: {
566
- actions: agentDevelopment.actionCount || 0,
567
- p0: agentDevelopment.p0Count || 0,
568
- p1: agentDevelopment.p1Count || 0,
569
- p2: agentDevelopment.p2Count || 0,
570
- p3: agentDevelopment.p3Count || 0,
571
- },
572
- }),
573
- ];
574
- const requiredChecks = checks.filter((item) => item.required);
575
- const optionalChecks = checks.filter((item) => !item.required);
576
- const blockingChecks = requiredChecks.filter((item) => statusRank(item.status) >= statusRank("warn"));
577
- const optionalGaps = optionalChecks.filter((item) => item.status !== "pass");
578
- const requiredPassCount = requiredChecks.filter((item) => item.status === "pass").length;
579
- const checkStatusById = Object.fromEntries(checks.map((item) => [item.id, item.status]));
580
- const checkRequiredById = Object.fromEntries(checks.map((item) => [item.id, Boolean(item.required)]));
581
- const checkCountByStatus = readinessCountByStatus(checks);
582
- const requiredCheckCountByStatus = readinessCountByStatus(requiredChecks);
583
- const optionalCheckCountByStatus = readinessCountByStatus(optionalChecks);
584
- const summary = blockingChecks.length === 0
585
- ? optionalGaps.length === 0
586
- ? "Required and optional local learning signal surfaces are complete."
587
- : "Required local learning signal surfaces are ready; optional evidence gaps remain."
588
- : "Required local learning signal surfaces need review before this can be used as a gate.";
589
-
590
- return {
591
- version: 1,
592
- status,
593
- summary,
594
- requiredPassCount,
595
- requiredCount: requiredChecks.length,
596
- requiredReady: blockingChecks.length === 0,
597
- blockingCount: blockingChecks.length,
598
- optionalGapCount: optionalGaps.length,
599
- blockingChecks: blockingChecks.map((item) => item.id),
600
- optionalGaps: optionalGaps.map((item) => item.id),
601
- optionalGapDetails: optionalGaps.map(optionalGapDetail),
602
- requiredCheckIds: requiredChecks.map((item) => item.id),
603
- optionalCheckIds: optionalChecks.map((item) => item.id),
604
- checkStatusById,
605
- checkRequiredById,
606
- checkCountByStatus,
607
- requiredCheckCountByStatus,
608
- optionalCheckCountByStatus,
609
- checks,
610
- };
611
- }
612
-
613
- function agentAction({
614
- id,
615
- priority,
616
- category,
617
- title,
618
- rationale,
619
- command = "",
620
- commandArgs = [],
621
- applyCommand = "",
622
- applyCommandArgs = [],
623
- evidence = {},
624
- }) {
625
- const normalizedCommandArgs = Array.isArray(commandArgs) ? commandArgs.map((item) => String(item)) : [];
626
- const normalizedApplyCommandArgs = Array.isArray(applyCommandArgs) ? applyCommandArgs.map((item) => String(item)) : [];
627
- return {
628
- id,
629
- priority,
630
- category,
631
- title,
632
- rationale,
633
- command: command || (normalizedCommandArgs.length > 0 ? commandFromArgs(normalizedCommandArgs) : ""),
634
- commandArgs: normalizedCommandArgs,
635
- applyCommand: applyCommand || (normalizedApplyCommandArgs.length > 0 ? commandFromArgs(normalizedApplyCommandArgs) : ""),
636
- applyCommandArgs: normalizedApplyCommandArgs,
637
- evidence,
638
- };
639
- }
640
-
641
- function agentDevelopmentStatus(actions) {
642
- if ((actions || []).some((item) => item.priority === "p0")) return "fail";
643
- if ((actions || []).some((item) => item.priority === "p1")) return "warn";
644
- return "pass";
645
- }
646
-
647
- function classifyAgentBacklogCommand(command = "") {
648
- const text = String(command || "");
649
- const detectedFlags = [];
650
- if (/\s--out(?:\s|=|$)/.test(text)) detectedFlags.push("--out");
651
- if (/\s--yes(?:\s|$)/.test(text)) detectedFlags.push("--yes");
652
- if (/\s--fix(?:\s|$)/.test(text)) detectedFlags.push("--fix");
653
- if (/\s--force(?:\s|$)/.test(text)) detectedFlags.push("--force");
654
- if (/\s--with-learning(?:\s|$)/.test(text)) detectedFlags.push("--with-learning");
655
- const outputTargets = extractAgentBacklogFlagTargets(text, "--out");
656
- const profileTargets = [
657
- ...extractAgentBacklogFlagTargets(text, "--file"),
658
- ...extractAgentBacklogFlagTargets(text, "--learning-file"),
659
- ...extractAgentBacklogEnvTargets(text, "DESIGN_AI_LEARNING_FILE"),
660
- ];
661
- const usageTargets = [
662
- ...extractAgentBacklogFlagTargets(text, "--usage-file"),
663
- ...extractAgentBacklogFlagTargets(text, "--learning-usage"),
664
- ...extractAgentBacklogEnvTargets(text, "DESIGN_AI_LEARNING_USAGE_FILE"),
665
- ];
666
- const mutationFlags = detectedFlags.filter((flag) => flag === "--yes" || flag === "--fix" || flag === "--force" || flag === "--with-learning");
667
- const writesLocalFiles = detectedFlags.includes("--out");
668
- const mutatesLocalState = detectedFlags.some((flag) => flag === "--yes" || flag === "--fix" || flag === "--with-learning");
669
- const level = mutatesLocalState ? "mutates-local-state" : writesLocalFiles ? "writes-local-file" : "read-only";
670
- const reason = mutatesLocalState
671
- ? detectedFlags.includes("--with-learning")
672
- ? "Command records local learning usage sidecar metadata."
673
- : "Command includes an apply/fix flag that can mutate local state."
674
- : writesLocalFiles
675
- ? "Command writes an explicit local output file."
676
- : "Command is preview/report oriented and has no detected mutation flags.";
677
- return {
678
- level,
679
- writesLocalFiles,
680
- mutatesLocalState,
681
- requiresCleanWorkspace: writesLocalFiles || mutatesLocalState,
682
- detectedFlags,
683
- outputTargets,
684
- profileTargets,
685
- usageTargets,
686
- mutationFlags,
687
- reason,
688
- };
689
- }
690
-
691
- function extractAgentBacklogFlagTargets(command = "", flag = "") {
692
- if (!flag) return [];
693
- const escaped = flag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
694
- const pattern = new RegExp(`(?:^|\\s)${escaped}(?:=|\\s+)(?:"([^"]+)"|'([^']+)'|(\\S+))`, "g");
695
- const targets = [];
696
- let match = pattern.exec(command);
697
- while (match) {
698
- const value = match[1] || match[2] || match[3] || "";
699
- if (value) targets.push({ flag, value });
700
- match = pattern.exec(command);
701
- }
702
- return targets;
703
- }
704
-
705
- function extractAgentBacklogEnvTargets(command = "", name = "") {
706
- if (!name) return [];
707
- const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
708
- const pattern = new RegExp(`(?:^|\\s)${escaped}=(?:"([^"]+)"|'([^']+)'|(\\S+))`, "g");
709
- const targets = [];
710
- let match = pattern.exec(command);
711
- while (match) {
712
- const value = match[1] || match[2] || match[3] || "";
713
- if (value) targets.push({ flag: name, value });
714
- match = pattern.exec(command);
715
- }
716
- return targets;
717
- }
718
-
719
- function summarizeAgentBacklogCommandSafety(steps = []) {
720
- const summary = {
721
- total: steps.length,
722
- readOnly: 0,
723
- writesLocalFile: 0,
724
- mutatesLocalState: 0,
725
- requiresCleanWorkspace: 0,
726
- requiresReviewBeforeMutation: 0,
727
- };
728
- for (const step of steps) {
729
- const safety = step.commandSafety && typeof step.commandSafety === "object" ? step.commandSafety : {};
730
- if (safety.level === "read-only") summary.readOnly += 1;
731
- if (safety.level === "writes-local-file") summary.writesLocalFile += 1;
732
- if (safety.level === "mutates-local-state") summary.mutatesLocalState += 1;
733
- if (safety.requiresCleanWorkspace) summary.requiresCleanWorkspace += 1;
734
- if (step.requiresReviewBeforeMutation) summary.requiresReviewBeforeMutation += 1;
735
- }
736
- return summary;
737
- }
738
-
739
- function agentBacklogRunPolicyForSafetyLevel(safetyLevel = "unknown") {
740
- if (safetyLevel === "read-only") return "preview-only";
741
- if (safetyLevel === "writes-local-file") return "review-before-file-write";
742
- if (safetyLevel === "mutates-local-state") return "review-before-mutation";
743
- return "manual-review";
744
- }
745
-
746
- function buildAgentBacklogCommandEffects(commandSafety = {}) {
747
- const outputTargets = Array.isArray(commandSafety.outputTargets) ? commandSafety.outputTargets : [];
748
- const profileTargets = Array.isArray(commandSafety.profileTargets) ? commandSafety.profileTargets : [];
749
- const usageTargets = Array.isArray(commandSafety.usageTargets) ? commandSafety.usageTargets : [];
750
- const mutationFlags = Array.isArray(commandSafety.mutationFlags) ? commandSafety.mutationFlags : [];
751
- const detectedFlags = Array.isArray(commandSafety.detectedFlags) ? commandSafety.detectedFlags : [];
752
- return {
753
- writesLocalFiles: Boolean(commandSafety.writesLocalFiles),
754
- mutatesLocalState: Boolean(commandSafety.mutatesLocalState),
755
- requiresCleanWorkspace: Boolean(commandSafety.requiresCleanWorkspace),
756
- detectedFlags,
757
- mutationFlags,
758
- outputTargets,
759
- profileTargets,
760
- usageTargets,
761
- reviewReason: commandSafety.reason || "",
762
- };
763
- }
764
-
765
- function summarizeAgentBacklogCommandEffects(effects = {}) {
766
- const parts = [];
767
- const outputTargets = Array.isArray(effects.outputTargets) ? effects.outputTargets : [];
768
- const profileTargets = Array.isArray(effects.profileTargets) ? effects.profileTargets : [];
769
- const usageTargets = Array.isArray(effects.usageTargets) ? effects.usageTargets : [];
770
- const mutationFlags = Array.isArray(effects.mutationFlags) ? effects.mutationFlags : [];
771
- if (outputTargets.length > 0) parts.push(`out ${outputTargets.map((item) => item.value).join(", ")}`);
772
- if (profileTargets.length > 0) parts.push(`profile ${profileTargets.map((item) => item.value).join(", ")}`);
773
- if (usageTargets.length > 0) parts.push(`usage ${usageTargets.map((item) => item.value).join(", ")}`);
774
- if (mutationFlags.length > 0) parts.push(`flags ${mutationFlags.join(", ")}`);
775
- if (parts.length > 0) return parts.join("; ");
776
- if (effects.mutatesLocalState) return "implicit local mutation target";
777
- if (effects.writesLocalFiles) return "local file write target";
778
- return "read-only";
779
- }
780
-
781
- function uniqueAgentBacklogTargets(targets = []) {
782
- const seen = new Set();
783
- const unique = [];
784
- for (const target of targets) {
785
- if (!target || typeof target !== "object") continue;
786
- const flag = String(target.flag || "");
787
- const value = String(target.value || "");
788
- if (!flag || !value) continue;
789
- const key = `${flag}\n${value}`;
790
- if (seen.has(key)) continue;
791
- seen.add(key);
792
- unique.push({ flag, value });
793
- }
794
- return unique;
795
- }
796
-
797
- function uniqueAgentBacklogFlags(flags = []) {
798
- return [...new Set(flags.filter(Boolean).map((flag) => String(flag)))];
799
- }
800
-
801
- function summarizeAgentBacklogCommandEffectManifest(commandManifest = []) {
802
- const effects = commandManifest
803
- .map((item) => (item && typeof item.commandEffects === "object" ? item.commandEffects : {}));
804
- const outputTargets = uniqueAgentBacklogTargets(effects.flatMap((item) => (
805
- Array.isArray(item.outputTargets) ? item.outputTargets : []
806
- )));
807
- const profileTargets = uniqueAgentBacklogTargets(effects.flatMap((item) => (
808
- Array.isArray(item.profileTargets) ? item.profileTargets : []
809
- )));
810
- const usageTargets = uniqueAgentBacklogTargets(effects.flatMap((item) => (
811
- Array.isArray(item.usageTargets) ? item.usageTargets : []
812
- )));
813
- const mutationFlags = uniqueAgentBacklogFlags(effects.flatMap((item) => (
814
- Array.isArray(item.mutationFlags) ? item.mutationFlags : []
815
- )));
816
- return {
817
- totalCommands: commandManifest.length,
818
- writesLocalFileCount: effects.filter((item) => item.writesLocalFiles).length,
819
- mutatesLocalStateCount: effects.filter((item) => item.mutatesLocalState).length,
820
- requiresCleanWorkspaceCount: effects.filter((item) => item.requiresCleanWorkspace).length,
821
- outputTargetCount: outputTargets.length,
822
- profileTargetCount: profileTargets.length,
823
- usageTargetCount: usageTargets.length,
824
- mutationFlagCount: mutationFlags.length,
825
- outputTargets,
826
- profileTargets,
827
- usageTargets,
828
- mutationFlags,
829
- };
830
- }
831
-
832
- function buildAgentBacklogCommandEffectReview(summary = {}, {
833
- refreshCommandArgs = ["design-ai", "learn", "--agent-backlog", "--strict", "--json"],
834
- } = {}) {
835
- const hasMutation = Number(summary.mutatesLocalStateCount || 0) > 0 || Number(summary.mutationFlagCount || 0) > 0;
836
- const hasFileWrite = Number(summary.writesLocalFileCount || 0) > 0 || Number(summary.outputTargetCount || 0) > 0;
837
- const hasProfileOrUsage = Number(summary.profileTargetCount || 0) > 0 || Number(summary.usageTargetCount || 0) > 0;
838
- const hasCommand = Number(summary.totalCommands || 0) > 0;
839
- const checklist = [];
840
- if (hasMutation) {
841
- checklist.push("Review mutation flags and run in a clean workspace before applying.");
842
- }
843
- if (hasFileWrite) {
844
- checklist.push("Inspect explicit output targets before committing generated files.");
845
- }
846
- if (hasProfileOrUsage) {
847
- checklist.push("Confirm learning profile and usage sidecar targets are intentional.");
848
- }
849
- if (checklist.length === 0) {
850
- checklist.push("No command target or mutation flag exposure detected.");
851
- }
852
- const level = hasMutation
853
- ? "mutation-review"
854
- : hasFileWrite || hasProfileOrUsage
855
- ? "target-review"
856
- : "clear";
857
- const headline = level === "mutation-review"
858
- ? "Mutation-capable commands require operator review before execution."
859
- : level === "target-review"
860
- ? "Command targets are explicit and should be reviewed before file changes."
861
- : "No command target or mutation flag exposure detected.";
862
- const gateCommands = [];
863
- if (level !== "clear") {
864
- gateCommands.push({
865
- phase: "before",
866
- label: "Confirm clean workspace before execution",
867
- ...commandSpec(["git", "status", "--short"]),
868
- required: true,
869
- });
870
- }
871
- if (hasMutation || hasFileWrite) {
872
- gateCommands.push({
873
- phase: "after",
874
- label: "Inspect local file changes after execution",
875
- ...commandSpec(["git", "diff", "--stat"]),
876
- required: true,
877
- });
878
- }
879
- gateCommands.push({
880
- phase: "refresh",
881
- label: "Refresh focused agent backlog after review",
882
- ...commandSpec(refreshCommandArgs),
883
- required: hasCommand,
884
- });
885
- const gatePhaseSummary = summarizeAgentBacklogGateCommands(gateCommands);
886
- const gateRunbook = groupAgentBacklogGateCommands(gateCommands);
887
- return {
888
- level,
889
- requiresOperatorReview: level !== "clear",
890
- headline,
891
- checklist,
892
- gatePhaseSummary,
893
- gateRunbook,
894
- gateCommands,
895
- };
896
- }
897
-
898
- function summarizeAgentBacklogGateCommands(gateCommands = []) {
899
- const validCommands = Array.isArray(gateCommands)
900
- ? gateCommands.filter((item) => item && typeof item === "object")
901
- : [];
902
- const phases = [...new Set(validCommands.map((item) => String(item.phase || "")).filter(Boolean))];
903
- const requiredCount = validCommands.filter((item) => item.required === true).length;
904
- return {
905
- count: validCommands.length,
906
- requiredCount,
907
- optionalCount: validCommands.length - requiredCount,
908
- phases,
909
- hasBefore: phases.includes("before"),
910
- hasAfter: phases.includes("after"),
911
- hasRefresh: phases.includes("refresh"),
912
- };
913
- }
914
-
915
- function groupAgentBacklogGateCommands(gateCommands = []) {
916
- const groups = {
917
- before: [],
918
- after: [],
919
- refresh: [],
920
- other: [],
921
- };
922
- const validCommands = Array.isArray(gateCommands)
923
- ? gateCommands.filter((item) => item && typeof item === "object")
924
- : [];
925
- for (const item of validCommands) {
926
- const phase = String(item.phase || "");
927
- if (phase === "before" || phase === "after" || phase === "refresh") {
928
- groups[phase].push(item);
929
- } else {
930
- groups.other.push(item);
931
- }
932
- }
933
- return groups;
934
- }
935
-
936
- function buildAgentBacklogOperatorRunbook({
937
- commandManifest = [],
938
- commandEffectReview = {},
939
- } = {}) {
940
- const gateRunbook = commandEffectReview?.gateRunbook && typeof commandEffectReview.gateRunbook === "object"
941
- ? commandEffectReview.gateRunbook
942
- : {};
943
- const gateCommandsFor = (phase) => (
944
- Array.isArray(gateRunbook[phase]) ? gateRunbook[phase] : []
945
- );
946
- const executeCommands = Array.isArray(commandManifest)
947
- ? commandManifest
948
- .filter((item) => item && typeof item === "object" && item.command)
949
- .map((item) => ({
950
- phase: "execute",
951
- rank: item.rank,
952
- actionId: item.actionId || "",
953
- label: item.actionId ? `Run ${item.actionId}` : "Run backlog command",
954
- command: item.command,
955
- commandArgs: Array.isArray(item.commandArgs) ? item.commandArgs : [],
956
- required: true,
957
- safetyLevel: item.safetyLevel || "unknown",
958
- runPolicy: item.runPolicy || "manual-review",
959
- requiresReviewBeforeMutation: Boolean(item.requiresReviewBeforeMutation),
960
- }))
961
- : [];
962
- const stages = [
963
- {
964
- phase: "before",
965
- label: "Run before executing backlog commands",
966
- commands: gateCommandsFor("before"),
967
- },
968
- {
969
- phase: "execute",
970
- label: "Execute reviewed backlog commands",
971
- commands: executeCommands,
972
- },
973
- {
974
- phase: "after",
975
- label: "Run after executing backlog commands",
976
- commands: gateCommandsFor("after"),
977
- },
978
- {
979
- phase: "refresh",
980
- label: "Refresh backlog status after execution",
981
- commands: gateCommandsFor("refresh"),
982
- },
983
- ].map((stage) => {
984
- const commands = Array.isArray(stage.commands) ? stage.commands : [];
985
- return {
986
- ...stage,
987
- commandCount: commands.length,
988
- requiredCount: commands.filter((item) => item && item.required === true).length,
989
- commands,
990
- };
991
- });
992
- const allCommands = stages.flatMap((stage) => stage.commands);
993
- const nextStage = stages.find((stage) => stage.commands.length > 0) || null;
994
- const nextCommand = nextStage?.commands?.[0] || null;
995
- const stageOrder = ["before", "execute", "after", "refresh"];
996
- const nonRefreshCommandCount = stages
997
- .filter((stage) => stage.phase !== "refresh")
998
- .reduce((count, stage) => count + stage.commands.length, 0);
999
- const optionalRefreshOnlyNextCommand = Boolean(
1000
- nextCommand
1001
- && nextStage?.phase === "refresh"
1002
- && nextCommand.required !== true
1003
- && nonRefreshCommandCount === 0,
1004
- );
1005
- const nextCommandSelection = {
1006
- strategy: "first-command-in-operator-runbook-stage-order",
1007
- stageOrder,
1008
- stage: nextStage?.phase || "",
1009
- label: nextCommand?.label || "",
1010
- command: nextCommand?.command || "",
1011
- commandArgs: Array.isArray(nextCommand?.commandArgs) ? nextCommand.commandArgs : [],
1012
- actionId: nextCommand?.actionId || "",
1013
- rank: nextCommand?.rank ?? null,
1014
- required: Boolean(nextCommand?.required),
1015
- runPolicy: nextCommand?.runPolicy || "",
1016
- reason: optionalRefreshOnlyNextCommand
1017
- ? "Optional refresh command is available as status metadata; no executable backlog handoff command is selected."
1018
- : nextCommand
1019
- ? `Selected the first command in the ${nextStage?.phase || "unknown"} stage using operator runbook stage order.`
1020
- : "No operator runbook command is available.",
1021
- };
1022
- return {
1023
- version: 1,
1024
- stageCount: stages.length,
1025
- commandCount: allCommands.length,
1026
- requiredCommandCount: allCommands.filter((item) => item && item.required === true).length,
1027
- reviewLevel: commandEffectReview?.level || "unknown",
1028
- requiresOperatorReview: Boolean(commandEffectReview?.requiresOperatorReview),
1029
- phases: stages.map((stage) => stage.phase),
1030
- nextStage: nextStage?.phase || "",
1031
- nextCommandLabel: nextCommand?.label || "",
1032
- nextCommand: nextCommand?.command || "",
1033
- nextCommandArgs: Array.isArray(nextCommand?.commandArgs) ? nextCommand.commandArgs : [],
1034
- nextCommandRequired: Boolean(nextCommand?.required),
1035
- nextCommandRunPolicy: nextCommand?.runPolicy || "",
1036
- nextCommandSelection,
1037
- stages,
1038
- };
1039
- }
1040
-
1041
- function buildAgentBacklogNextCommandAlignment({
1042
- operatorRunbook = {},
1043
- nextCommandItem = null,
1044
- rankedNextStep = null,
1045
- } = {}) {
1046
- const operatorSelection = operatorRunbook?.nextCommandSelection && typeof operatorRunbook.nextCommandSelection === "object"
1047
- ? operatorRunbook.nextCommandSelection
1048
- : {};
1049
- const operatorCommand = operatorRunbook?.nextCommand || operatorSelection.command || "";
1050
- const operatorActionId = operatorSelection.actionId || "";
1051
- const operatorStage = operatorRunbook?.nextStage || operatorSelection.stage || "";
1052
- const queueCommand = nextCommandItem?.command || "";
1053
- const queueActionId = nextCommandItem?.actionId || "";
1054
- const rankedActionId = rankedNextStep?.actionId || "";
1055
- const matchesQueueNextCommand = Boolean(operatorCommand && queueCommand && operatorCommand === queueCommand);
1056
- const matchesQueueNextAction = Boolean(operatorActionId && queueActionId && operatorActionId === queueActionId);
1057
- const operatorRunsBeforeQueueCommand = Boolean(operatorCommand && queueCommand && !matchesQueueNextCommand && operatorStage === "before");
1058
- const queueMatchesRankedNextAction = Boolean(queueActionId && rankedActionId && queueActionId === rankedActionId);
1059
- let reason = "No operator runbook command or queue command is available.";
1060
- if (matchesQueueNextCommand) {
1061
- reason = "Operator runbook starts with the same command as the safety-ordered execution queue.";
1062
- } else if (operatorRunsBeforeQueueCommand) {
1063
- reason = "Operator runbook starts with a before-stage gate before the safety-ordered queue command.";
1064
- } else if (operatorCommand && queueCommand) {
1065
- reason = "Operator runbook and safety-ordered queue selected different first commands.";
1066
- } else if (operatorCommand) {
1067
- reason = operatorStage === "refresh"
1068
- ? "Operator runbook exposes an optional refresh command while the safety-ordered execution queue is empty."
1069
- : "Operator runbook exposes a command while the safety-ordered execution queue is empty.";
1070
- } else if (queueCommand) {
1071
- reason = "Safety-ordered execution queue exposes a command while the operator runbook has no command.";
1072
- }
1073
- return {
1074
- strategy: "compare-operator-runbook-next-command-to-execution-queue-next-command",
1075
- operatorStage,
1076
- operatorActionId,
1077
- operatorCommand,
1078
- operatorCommandArgs: Array.isArray(operatorRunbook?.nextCommandArgs) ? operatorRunbook.nextCommandArgs : [],
1079
- queueActionId,
1080
- queueCommand,
1081
- queueCommandArgs: Array.isArray(nextCommandItem?.commandArgs) ? nextCommandItem.commandArgs : [],
1082
- rankedNextActionId: rankedActionId,
1083
- matchesQueueNextCommand,
1084
- matchesQueueNextAction,
1085
- operatorRunsBeforeQueueCommand,
1086
- queueMatchesRankedNextAction,
1087
- reason,
1088
- };
1089
- }
1090
-
1091
- function buildAgentBacklogOperatorHandoff({
1092
- operatorRunbook = {},
1093
- nextCommandItem = null,
1094
- nextCommandAlignment = {},
1095
- } = {}) {
1096
- const operatorSelection = operatorRunbook?.nextCommandSelection && typeof operatorRunbook.nextCommandSelection === "object"
1097
- ? operatorRunbook.nextCommandSelection
1098
- : {};
1099
- const hasOperatorCommand = Boolean(operatorRunbook?.nextCommand);
1100
- const hasQueueCommand = Boolean(nextCommandItem?.command);
1101
- const refreshStage = Array.isArray(operatorRunbook?.stages)
1102
- ? operatorRunbook.stages.find((stage) => stage && stage.phase === "refresh")
1103
- : null;
1104
- const refreshCommandItem = Array.isArray(refreshStage?.commands)
1105
- ? refreshStage.commands.find((item) => item && item.command) || null
1106
- : null;
1107
- const operatorPhase = hasOperatorCommand ? operatorRunbook.nextStage || "" : "";
1108
- const operatorCommandIsGate = Boolean(hasOperatorCommand && operatorPhase && operatorPhase !== "execute");
1109
- const nextQueueCommandRequiresGate = Boolean(nextCommandItem?.requiresReviewBeforeMutation);
1110
- const operatorGateAppliesToNextQueueAction = Boolean(operatorCommandIsGate && hasQueueCommand && nextQueueCommandRequiresGate);
1111
- const shouldUseOperatorCommand = Boolean(hasOperatorCommand && (!operatorCommandIsGate || operatorGateAppliesToNextQueueAction));
1112
- const source = shouldUseOperatorCommand ? "operator-runbook" : hasQueueCommand ? "execution-queue" : "";
1113
- const phase = shouldUseOperatorCommand ? operatorRunbook.nextStage || "" : hasQueueCommand ? "execute" : "";
1114
- const command = shouldUseOperatorCommand ? operatorRunbook.nextCommand || "" : nextCommandItem?.command || "";
1115
- const commandArgs = shouldUseOperatorCommand
1116
- ? Array.isArray(operatorRunbook?.nextCommandArgs) ? operatorRunbook.nextCommandArgs : []
1117
- : Array.isArray(nextCommandItem?.commandArgs) ? nextCommandItem.commandArgs : [];
1118
- const isGate = Boolean(shouldUseOperatorCommand && phase && phase !== "execute");
1119
- const nextQueueActionBlockedByGate = Boolean(isGate && hasQueueCommand);
1120
- const requiresOperatorReviewForHandoff = shouldUseOperatorCommand
1121
- ? Boolean(operatorRunbook?.requiresOperatorReview)
1122
- : Boolean(nextCommandItem?.requiresReviewBeforeMutation);
1123
- const reviewLevelForHandoff = shouldUseOperatorCommand
1124
- ? operatorRunbook?.reviewLevel || "unknown"
1125
- : requiresOperatorReviewForHandoff ? operatorRunbook?.reviewLevel || "unknown" : "clear";
1126
- let decision = "none";
1127
- let reason = refreshCommandItem?.command
1128
- ? "No handoff command is required; optional refresh command remains available as status metadata."
1129
- : "No operator or queue command is available for handoff.";
1130
- if (nextQueueActionBlockedByGate) {
1131
- decision = "run-operator-gate";
1132
- reason = "Run the operator gate before executing the safety-ordered queue command.";
1133
- } else if (nextCommandAlignment?.matchesQueueNextCommand) {
1134
- decision = "run-shared-command";
1135
- reason = "Run the shared operator and queue command next.";
1136
- } else if (shouldUseOperatorCommand) {
1137
- decision = isGate ? "run-operator-gate" : "run-operator-command";
1138
- reason = "Run the operator runbook command next.";
1139
- } else if (hasQueueCommand) {
1140
- decision = "run-queue-command";
1141
- reason = "Run the safety-ordered queue command next.";
1142
- }
1143
- let stateStatus = "no-command";
1144
- let stateSummary = "No operator or queue command is available for handoff.";
1145
- if (command) {
1146
- if (nextQueueActionBlockedByGate) {
1147
- stateStatus = "gate-required";
1148
- stateSummary = "Run the required operator gate before the safety-ordered queue command.";
1149
- } else if (requiresOperatorReviewForHandoff) {
1150
- stateStatus = "review-required";
1151
- stateSummary = "Review command targets and mutation exposure before running the handoff command.";
1152
- } else {
1153
- stateStatus = "ready";
1154
- stateSummary = "The handoff command can be presented or run, then refreshed with the focused backlog check.";
1155
- }
1156
- } else {
1157
- stateSummary = "Focused agent backlog is clear; no handoff command is required.";
1158
- }
1159
- const hasHandoffCommand = Boolean(command);
1160
- const state = {
1161
- version: 1,
1162
- status: stateStatus,
1163
- ready: hasHandoffCommand || stateStatus === "no-command",
1164
- hasCommand: hasHandoffCommand,
1165
- complete: !hasHandoffCommand,
1166
- canRunWithoutReview: Boolean(hasHandoffCommand && !nextQueueActionBlockedByGate && !requiresOperatorReviewForHandoff),
1167
- requiresGate: nextQueueActionBlockedByGate,
1168
- requiresRefresh: Boolean(hasHandoffCommand && refreshCommandItem?.required),
1169
- summary: stateSummary,
1170
- };
1171
- return {
1172
- version: 1,
1173
- decision,
1174
- state,
1175
- source,
1176
- phase,
1177
- label: shouldUseOperatorCommand ? operatorRunbook.nextCommandLabel || "" : nextCommandItem?.actionId || "",
1178
- command,
1179
- commandArgs,
1180
- actionId: shouldUseOperatorCommand ? operatorSelection.actionId || "" : nextCommandItem?.actionId || "",
1181
- rank: shouldUseOperatorCommand ? operatorSelection.rank ?? null : nextCommandItem?.rank ?? null,
1182
- runPolicy: shouldUseOperatorCommand ? operatorRunbook.nextCommandRunPolicy || "" : nextCommandItem?.runPolicy || "",
1183
- required: shouldUseOperatorCommand ? Boolean(operatorRunbook.nextCommandRequired) : Boolean(hasQueueCommand),
1184
- isGate,
1185
- nextQueueActionId: nextCommandItem?.actionId || "",
1186
- nextQueueCommand: nextCommandItem?.command || "",
1187
- nextQueueCommandArgs: Array.isArray(nextCommandItem?.commandArgs) ? nextCommandItem.commandArgs : [],
1188
- nextQueueCommandRequiresGate,
1189
- operatorGateAppliesToNextQueueAction,
1190
- nextQueueActionBlockedByGate,
1191
- refreshCommand: refreshCommandItem?.command || "",
1192
- refreshCommandArgs: Array.isArray(refreshCommandItem?.commandArgs) ? refreshCommandItem.commandArgs : [],
1193
- refreshCommandLabel: refreshCommandItem?.label || "",
1194
- refreshCommandRequired: Boolean(hasHandoffCommand && refreshCommandItem?.required),
1195
- reviewLevel: reviewLevelForHandoff,
1196
- requiresOperatorReview: requiresOperatorReviewForHandoff,
1197
- reason,
1198
- };
1199
- }
1200
-
1201
- function buildAgentBacklogExecutionQueue(steps = [], {
1202
- refreshCommandArgs = ["design-ai", "learn", "--agent-backlog", "--strict", "--json"],
1203
- } = {}) {
1204
- const toQueueItem = (step) => {
1205
- const commandSafety = step.commandSafety && typeof step.commandSafety === "object" ? step.commandSafety : {};
1206
- const safetyLevel = commandSafety.level || "unknown";
1207
- const commandEffects = buildAgentBacklogCommandEffects(commandSafety);
1208
- const applyCommandSafety = step.applyCommandSafety && typeof step.applyCommandSafety === "object"
1209
- ? {
1210
- level: step.applyCommandSafety.level || "unknown",
1211
- ...buildAgentBacklogCommandEffects(step.applyCommandSafety),
1212
- }
1213
- : null;
1214
- return {
1215
- rank: step.rank,
1216
- actionId: step.actionId || "",
1217
- priority: step.priority || "p3",
1218
- category: step.category || "other",
1219
- title: step.title || "",
1220
- command: step.command || "",
1221
- commandArgs: Array.isArray(step.commandArgs) ? step.commandArgs : [],
1222
- applyCommand: step.applyCommand || "",
1223
- applyCommandArgs: Array.isArray(step.applyCommandArgs) ? step.applyCommandArgs : [],
1224
- applyCommandSafety,
1225
- applyRequiresReviewBeforeMutation: Boolean(step.applyRequiresReviewBeforeMutation),
1226
- safetyLevel,
1227
- runPolicy: agentBacklogRunPolicyForSafetyLevel(safetyLevel),
1228
- commandEffects,
1229
- requiresReviewBeforeMutation: Boolean(step.requiresReviewBeforeMutation),
1230
- };
1231
- };
1232
- const preview = steps.filter((step) => step.commandSafety?.level === "read-only").map(toQueueItem);
1233
- const fileWriteReview = steps.filter((step) => step.commandSafety?.level === "writes-local-file").map(toQueueItem);
1234
- const mutationReview = steps.filter((step) => step.commandSafety?.level === "mutates-local-state").map(toQueueItem);
1235
- const ordered = [...preview, ...fileWriteReview, ...mutationReview];
1236
- const commandManifest = ordered
1237
- .filter((item) => item.command)
1238
- .map((item) => ({
1239
- rank: item.rank,
1240
- actionId: item.actionId,
1241
- command: item.command,
1242
- commandArgs: item.commandArgs,
1243
- applyCommand: item.applyCommand,
1244
- applyCommandArgs: item.applyCommandArgs,
1245
- applyCommandSafety: item.applyCommandSafety,
1246
- applyRequiresReviewBeforeMutation: item.applyRequiresReviewBeforeMutation,
1247
- safetyLevel: item.safetyLevel,
1248
- runPolicy: item.runPolicy,
1249
- commandEffects: item.commandEffects,
1250
- requiresReviewBeforeMutation: item.requiresReviewBeforeMutation,
1251
- }));
1252
- const nextCommandItem = ordered.find((item) => item.command) || null;
1253
- const rankedNextStep = steps[0] || null;
1254
- const commandEffectSummary = summarizeAgentBacklogCommandEffectManifest(commandManifest);
1255
- const commandEffectReview = buildAgentBacklogCommandEffectReview(commandEffectSummary, {
1256
- refreshCommandArgs,
1257
- });
1258
- const operatorRunbook = buildAgentBacklogOperatorRunbook({
1259
- commandManifest,
1260
- commandEffectReview,
1261
- });
1262
- const nextCommandMatchesRankedStep = Boolean(
1263
- nextCommandItem?.actionId
1264
- && rankedNextStep?.actionId
1265
- && nextCommandItem.actionId === rankedNextStep.actionId,
1266
- );
1267
- const nextCommandSelection = {
1268
- strategy: "first-command-in-safety-ordered-queue",
1269
- safetyOrder: ["read-only", "writes-local-file", "mutates-local-state"],
1270
- actionId: nextCommandItem?.actionId || "",
1271
- rank: nextCommandItem?.rank ?? null,
1272
- safetyLevel: nextCommandItem?.safetyLevel || "",
1273
- runPolicy: nextCommandItem?.runPolicy || "",
1274
- planNextActionId: rankedNextStep?.actionId || "",
1275
- planNextActionRank: rankedNextStep?.rank ?? null,
1276
- matchesPlanNextAction: nextCommandMatchesRankedStep,
1277
- reason: nextCommandItem
1278
- ? nextCommandMatchesRankedStep
1279
- ? "Selected the ranked next action because it is first in the safety-ordered queue."
1280
- : "Selected the first command in the safety-ordered queue before higher-risk ranked actions."
1281
- : "No command-bearing backlog action is available.",
1282
- };
1283
- const nextCommandAlignment = buildAgentBacklogNextCommandAlignment({
1284
- operatorRunbook,
1285
- nextCommandItem,
1286
- rankedNextStep,
1287
- });
1288
- const operatorHandoff = buildAgentBacklogOperatorHandoff({
1289
- operatorRunbook,
1290
- nextCommandItem,
1291
- nextCommandAlignment,
1292
- });
1293
- return {
1294
- orderedCount: ordered.length,
1295
- commandManifestCount: commandManifest.length,
1296
- previewCount: preview.length,
1297
- fileWriteReviewCount: fileWriteReview.length,
1298
- mutationReviewCount: mutationReview.length,
1299
- nextActionId: ordered[0]?.actionId || "",
1300
- nextCommand: nextCommandItem?.command || "",
1301
- nextCommandArgs: Array.isArray(nextCommandItem?.commandArgs) ? nextCommandItem.commandArgs : [],
1302
- nextCommandRunPolicy: nextCommandItem?.runPolicy || "",
1303
- nextCommandSelection,
1304
- nextCommandAlignment,
1305
- operatorHandoff,
1306
- commandEffectSummary,
1307
- commandEffectReview,
1308
- operatorRunbook,
1309
- ordered,
1310
- commandManifest,
1311
- preview,
1312
- fileWriteReview,
1313
- mutationReview,
1314
- };
1315
- }
1316
-
1317
- function buildAgentBacklogActionPlan({ actions = [], commands = {}, privacy = {} } = {}) {
1318
- const agentBacklogJsonArgs = Array.isArray(commands.agentBacklogJsonArgs) && commands.agentBacklogJsonArgs.length > 0
1319
- ? commands.agentBacklogJsonArgs
1320
- : ["design-ai", "learn", "--agent-backlog", "--strict", "--json"];
1321
- const steps = actions.map((action, index) => {
1322
- const commandArgs = Array.isArray(action.commandArgs) ? action.commandArgs.map((item) => String(item)) : [];
1323
- const command = String(action.command || (commandArgs.length > 0 ? commandFromArgs(commandArgs) : ""));
1324
- const applyCommandArgs = Array.isArray(action.applyCommandArgs) ? action.applyCommandArgs.map((item) => String(item)) : [];
1325
- const applyCommand = String(action.applyCommand || (applyCommandArgs.length > 0 ? commandFromArgs(applyCommandArgs) : ""));
1326
- const commandSafety = classifyAgentBacklogCommand(command);
1327
- const applyCommandSafety = applyCommand ? classifyAgentBacklogCommand(applyCommand) : null;
1328
- return {
1329
- rank: action.rank ?? index + 1,
1330
- actionId: action.id || "",
1331
- priority: action.priority || "p3",
1332
- category: action.category || "other",
1333
- title: action.title || "",
1334
- command,
1335
- commandArgs,
1336
- applyCommand,
1337
- applyCommandArgs,
1338
- applyCommandSafety,
1339
- applyRequiresReviewBeforeMutation: Boolean(applyCommandSafety?.requiresCleanWorkspace),
1340
- expectedOutcome: action.rationale || "",
1341
- verification: command
1342
- ? applyCommand
1343
- ? [
1344
- "Run the preview command first and inspect the proposed local learning profile changes.",
1345
- "Run the apply command only after operator review confirms the target profile path and mutation scope.",
1346
- "Re-run `design-ai learn --agent-backlog --strict --json` after the apply step to confirm the backlog status improved.",
1347
- ]
1348
- : commandSafety.requiresCleanWorkspace
1349
- ? [
1350
- "Run the command in a clean working tree or disposable workspace, then inspect generated or changed files before committing.",
1351
- "Re-run `design-ai learn --agent-backlog --strict --json` after the step to confirm the backlog status improved.",
1352
- ]
1353
- : [
1354
- "Run the command and inspect the preview/report output before applying any follow-up changes.",
1355
- "Re-run `design-ai learn --agent-backlog --strict --json` after the step to confirm the backlog status improved.",
1356
- ]
1357
- : [
1358
- "Review the action manually, then refresh the agent backlog report.",
1359
- ],
1360
- requiresReviewBeforeMutation: commandSafety.requiresCleanWorkspace,
1361
- commandSafety,
1362
- };
1363
- });
1364
- const safetySummary = summarizeAgentBacklogCommandSafety(steps);
1365
- const executionQueue = buildAgentBacklogExecutionQueue(steps, {
1366
- refreshCommandArgs: agentBacklogJsonArgs,
1367
- });
1368
- const verification = [
1369
- commands.signalsJson
1370
- ? {
1371
- label: "Refresh signal registry JSON",
1372
- command: commands.signalsJson,
1373
- commandArgs: Array.isArray(commands.signalsJsonArgs) ? commands.signalsJsonArgs : [],
1374
- }
1375
- : null,
1376
- commands.signalsReport
1377
- ? {
1378
- label: "Save signal registry Markdown handoff",
1379
- command: commands.signalsReport,
1380
- commandArgs: Array.isArray(commands.signalsReportArgs) ? commands.signalsReportArgs : [],
1381
- }
1382
- : null,
1383
- {
1384
- label: "Gate focused agent backlog",
1385
- ...commandSpec(agentBacklogJsonArgs),
1386
- },
1387
- ].filter(Boolean);
1388
-
1389
- return {
1390
- version: 1,
1391
- stepCount: steps.length,
1392
- nextStep: steps[0] || null,
1393
- steps,
1394
- safetySummary,
1395
- executionQueue,
1396
- verification,
1397
- boundaries: {
1398
- reportMutatesProfile: Boolean(privacy.mutatesProfile),
1399
- reportMutatesSkillFiles: Boolean(privacy.mutatesSkillFiles),
1400
- reportCallsExternalAiApis: Boolean(privacy.callsExternalAiApis),
1401
- generatedFromLocalSignals: true,
1402
- },
1403
- };
1404
- }
1405
-
1406
- function buildAgentDevelopmentBacklog({
1407
- audit,
1408
- usage,
1409
- evals,
1410
- checkCapture,
1411
- workspace,
1412
- filePath,
1413
- usageFile,
1414
- signalSource,
1415
- }) {
1416
- const actions = [];
1417
-
1418
- if (!audit.exists) {
1419
- const previewCommand = commandSpec(["design-ai", "learn", "--init", "--dry-run", "--file", filePath]);
1420
- const applyCommand = commandSpec(["design-ai", "learn", "--init", "--yes", "--file", filePath]);
1421
- actions.push(agentAction({
1422
- id: "agent-learning-profile-init",
1423
- priority: "p1",
1424
- category: "learning-profile",
1425
- title: "Initialize the local learning profile before agent development review.",
1426
- rationale: "Signal registry output is incomplete until a profile exists.",
1427
- ...previewCommand,
1428
- applyCommand: applyCommand.command,
1429
- applyCommandArgs: applyCommand.commandArgs,
1430
- evidence: {
1431
- profileExists: false,
1432
- },
1433
- }));
1434
- } else if (audit.summary.failures > 0) {
1435
- actions.push(agentAction({
1436
- id: "agent-learning-profile-audit-fix",
1437
- priority: "p0",
1438
- category: "learning-profile",
1439
- title: "Fix learning profile audit failures before using signals as a development gate.",
1440
- rationale: "Audit failures can make learned context selection and downstream signal summaries unreliable.",
1441
- ...commandSpec(["design-ai", "learn", "--audit", "--file", filePath]),
1442
- evidence: {
1443
- failures: audit.summary.failures,
1444
- warnings: audit.summary.warnings,
1445
- },
1446
- }));
1447
- } else if (audit.summary.warnings > 0) {
1448
- actions.push(agentAction({
1449
- id: "agent-learning-profile-audit-review",
1450
- priority: "p1",
1451
- category: "learning-profile",
1452
- title: "Review learning profile audit warnings before promoting agent behavior.",
1453
- rationale: "Warnings are not blockers, but they should be resolved before treating local learning as release evidence.",
1454
- ...commandSpec(["design-ai", "learn", "--audit", "--file", filePath]),
1455
- evidence: {
1456
- warnings: audit.summary.warnings,
1457
- },
1458
- }));
1459
- }
1460
-
1461
- if (!usage.exists || usage.eventCount === 0) {
1462
- actions.push(agentAction({
1463
- id: "agent-learning-usage-record",
1464
- priority: "p2",
1465
- category: "usage-signals",
1466
- title: "Record prompt or pack usage with learning enabled.",
1467
- rationale: "Usage sidecar events show which learned entries actually affect agent prompts without storing raw brief text.",
1468
- ...commandSpec([
1469
- "env",
1470
- `DESIGN_AI_LEARNING_FILE=${filePath}`,
1471
- `DESIGN_AI_LEARNING_USAGE_FILE=${usageFile}`,
1472
- "design-ai",
1473
- "prompt",
1474
- "audit a design artifact",
1475
- "--with-learning",
1476
- "--json",
1477
- ]),
1478
- evidence: {
1479
- usageExists: Boolean(usage.exists),
1480
- eventCount: usage.eventCount || 0,
1481
- },
1482
- }));
1483
- }
1484
- if (usage.staleSelectedEntryCount > 0) {
1485
- actions.push(agentAction({
1486
- id: "agent-learning-usage-stale-review",
1487
- priority: "p1",
1488
- category: "usage-signals",
1489
- title: "Review stale selected learning ids in the usage sidecar.",
1490
- rationale: "Stale ids indicate usage evidence is no longer aligned with the active profile.",
1491
- ...commandSpec(["design-ai", "learn", "--usage", "--file", filePath, "--usage-file", usageFile]),
1492
- evidence: {
1493
- staleSelectedEntryCount: usage.staleSelectedEntryCount,
1494
- },
1495
- }));
1496
- }
1497
-
1498
- if (evals.files.length === 0) {
1499
- const evalOutputFile = defaultLearningEvalPath(filePath);
1500
- actions.push(agentAction({
1501
- id: "agent-eval-checkpoint-generate",
1502
- priority: "p1",
1503
- category: "eval-harness",
1504
- title: "Generate and run route, prompt, pack, or learning eval checkpoints.",
1505
- rationale: "Agent development needs replayable checkpoints before signal registry output can act as a gate.",
1506
- ...commandSpec(["design-ai", "learn", "--eval-template", "--file", filePath, "--json", "--out", evalOutputFile]),
1507
- evidence: {
1508
- evalSignalCount: 0,
1509
- evalOutputFile,
1510
- },
1511
- }));
1512
- }
1513
- if (evals.failed > 0) {
1514
- actions.push(agentAction({
1515
- id: "agent-eval-failure-review",
1516
- priority: "p0",
1517
- category: "eval-harness",
1518
- title: "Fix failing eval signal reports before continuing agent development.",
1519
- rationale: "Failed checkpoints are the strongest deterministic signal that route, prompt, pack, or learning behavior drifted.",
1520
- ...commandSpec(["design-ai", "learn", "--signals", "--from-file", signalSource || ".", "--file", filePath, "--usage-file", usageFile, "--json"]),
1521
- evidence: {
1522
- failed: evals.failed,
1523
- warned: evals.warned,
1524
- },
1525
- }));
1526
- } else if (evals.templates > 0) {
1527
- const templateFile = Array.isArray(evals.templateFiles) && evals.templateFiles.length > 0
1528
- ? evals.templateFiles[0].file
1529
- : "";
1530
- const evalReportFile = templateFile ? evalReportPathForTemplate(templateFile) : defaultLearningEvalReportPath(filePath);
1531
- actions.push(agentAction({
1532
- id: "agent-eval-template-replay",
1533
- priority: "p1",
1534
- category: "eval-harness",
1535
- title: "Replay template-only eval signal files as executed reports.",
1536
- rationale: "Templates are useful setup artifacts, but executed reports provide stronger evidence for agent behavior.",
1537
- ...commandSpec([
1538
- "design-ai",
1539
- "learn",
1540
- "--eval",
1541
- "--from-file",
1542
- templateFile || defaultLearningEvalPath(filePath),
1543
- "--file",
1544
- filePath,
1545
- "--strict",
1546
- "--json",
1547
- "--out",
1548
- evalReportFile,
1549
- ]),
1550
- evidence: {
1551
- templates: evals.templates,
1552
- templateFile: templateFile || defaultLearningEvalPath(filePath),
1553
- evalReportFile,
1554
- },
1555
- }));
1556
- }
1557
-
1558
- if (checkCapture.count > 0) {
1559
- actions.push(agentAction({
1560
- id: "agent-skill-proposal-preview",
1561
- priority: "p2",
1562
- category: "skill-evolution",
1563
- title: "Preview skill instruction deltas from repeated check-capture signals.",
1564
- rationale: "Captured warn/fail check results can become deterministic skill improvements without mutating skill files automatically.",
1565
- ...commandSpec(["design-ai", "learn", "--propose-skills", "--from-file", signalSource || ".", "--file", filePath, "--usage-file", usageFile, "--json"]),
1566
- evidence: {
1567
- checkCaptureCount: checkCapture.count,
1568
- categoryCounts: checkCapture.categoryCounts,
1569
- },
1570
- }));
1571
- }
1572
-
1573
- if (workspace.nextActionCounts.fail > 0 || workspace.nextActionCounts.warn > 0) {
1574
- actions.push(agentAction({
1575
- id: "agent-workspace-readiness-review",
1576
- priority: workspace.nextActionCounts.fail > 0 ? "p0" : "p1",
1577
- category: "workspace-readiness",
1578
- title: "Resolve workspace readiness actions before treating agent development evidence as complete.",
1579
- rationale: "Workspace readiness joins git, repository metadata, learning, usage, and eval freshness into the operator handoff gate.",
1580
- ...commandSpec(["design-ai", "workspace", "--learning-file", filePath, "--learning-usage", usageFile, "--strict", "--json"]),
1581
- evidence: {
1582
- fail: workspace.nextActionCounts.fail || 0,
1583
- warn: workspace.nextActionCounts.warn || 0,
1584
- nextActionCount: workspace.nextActionCount || 0,
1585
- },
1586
- }));
1587
- }
1588
-
1589
- const priorityOrder = { p0: 0, p1: 1, p2: 2, p3: 3 };
1590
- const actionOrder = {
1591
- "agent-learning-profile-audit-fix": 0,
1592
- "agent-learning-profile-init": 1,
1593
- "agent-learning-profile-audit-review": 2,
1594
- "agent-learning-usage-stale-review": 3,
1595
- "agent-workspace-readiness-review": 4,
1596
- "agent-eval-failure-review": 5,
1597
- "agent-eval-checkpoint-generate": 6,
1598
- "agent-eval-template-replay": 7,
1599
- "agent-learning-usage-record": 8,
1600
- "agent-skill-proposal-preview": 9,
1601
- };
1602
- const sortedActions = actions
1603
- .sort((a, b) => (
1604
- priorityOrder[a.priority] - priorityOrder[b.priority]
1605
- || (actionOrder[a.id] ?? 100) - (actionOrder[b.id] ?? 100)
1606
- || a.category.localeCompare(b.category)
1607
- || a.id.localeCompare(b.id)
1608
- ))
1609
- .map((item, index) => ({
1610
- rank: index + 1,
1611
- ...item,
1612
- }));
1613
-
1614
- return {
1615
- status: agentDevelopmentStatus(sortedActions),
1616
- actionCount: sortedActions.length,
1617
- p0Count: sortedActions.filter((item) => item.priority === "p0").length,
1618
- p1Count: sortedActions.filter((item) => item.priority === "p1").length,
1619
- p2Count: sortedActions.filter((item) => item.priority === "p2").length,
1620
- p3Count: sortedActions.filter((item) => item.priority === "p3").length,
1621
- actions: sortedActions,
1622
- privacy: {
1623
- mutatesProfile: false,
1624
- mutatesSkillFiles: false,
1625
- callsExternalAiApis: false,
1626
- storesRawBriefText: false,
1627
- },
1628
- };
1629
- }
1630
-
1631
- export function learningSignalRegistry({
1632
- filePath = defaultLearningFile(),
1633
- usageFile = "",
1634
- signalSource = "",
1635
- root = process.cwd(),
1636
- now = new Date(),
1637
- workspaceReportProvider = collectWorkspaceReport,
1638
- learningStatsProvider = learningStats,
1639
- learningUsageStatsProvider = learningUsageStats,
1640
- } = {}) {
1641
- const resolvedFile = path.resolve(filePath);
1642
- const resolvedUsageFile = path.resolve(usageFile || defaultLearningUsageFile(resolvedFile));
1643
- const profile = loadLearningProfile(resolvedFile);
1644
- const audit = auditLearningProfile({ filePath: resolvedFile });
1645
- const stats = learningStatsProvider({ filePath: resolvedFile });
1646
- const usage = learningUsageStatsProvider({
1647
- filePath: resolvedFile,
1648
- usageFile: resolvedUsageFile,
1649
- });
1650
- const signalFiles = resolveSignalFiles({
1651
- signalSource,
1652
- root,
1653
- extraFiles: [defaultLearningEvalPath(resolvedFile), defaultLearningEvalReportPath(resolvedFile)],
1654
- });
1655
- const evalFiles = signalFiles.map((file) => summarizeSignalEvalFile(file));
1656
- const evalSignalSummary = summarizeEvalSignals(evalFiles);
1657
- const evalSummary = {
1658
- source: signalSource ? path.resolve(signalSource) : path.resolve(root),
1659
- count: evalFiles.length,
1660
- reports: evalSignalSummary.reports,
1661
- templates: evalSignalSummary.templates,
1662
- rawTemplates: evalSignalSummary.rawTemplates,
1663
- templateFiles: evalSignalSummary.templateFiles,
1664
- failed: evalSignalSummary.failed,
1665
- warned: evalSignalSummary.warned,
1666
- passed: evalSignalSummary.passed,
1667
- files: evalFiles,
1668
- };
1669
- const workspaceReport = workspaceReportProvider({
1670
- root,
1671
- learningFilePath: resolvedFile,
1672
- learningUsagePath: resolvedUsageFile,
1673
- });
1674
- const workspace = summarizeWorkspace(workspaceReport);
1675
- const checkCapture = summarizeCheckCapture(profile);
1676
- const recommendations = buildRecommendations({
1677
- audit,
1678
- usage,
1679
- evals: evalSummary,
1680
- checkCapture,
1681
- workspace,
1682
- });
1683
- const agentDevelopment = buildAgentDevelopmentBacklog({
1684
- audit,
1685
- usage,
1686
- evals: evalSummary,
1687
- checkCapture,
1688
- workspace,
1689
- filePath: resolvedFile,
1690
- usageFile: resolvedUsageFile,
1691
- signalSource: evalSummary.source,
1692
- });
1693
- const status = worstStatus([
1694
- audit.summary.failures > 0 ? "fail" : "",
1695
- audit.summary.warnings > 0 ? "warn" : "",
1696
- usage.staleSelectedEntryCount > 0 ? "warn" : "",
1697
- evalSummary.failed > 0 ? "fail" : "",
1698
- evalSummary.warned > 0 || evalSummary.templates > 0 || evalSummary.count === 0 ? "warn" : "",
1699
- workspace.nextActionCounts.fail > 0 ? "fail" : "",
1700
- workspace.nextActionCounts.warn > 0 ? "warn" : "",
1701
- "pass",
1702
- ]);
1703
- const readiness = buildLearningSignalReadiness({
1704
- audit,
1705
- usage,
1706
- evals: evalSummary,
1707
- checkCapture,
1708
- workspace,
1709
- agentDevelopment,
1710
- status,
1711
- });
1712
-
1713
- return {
1714
- version: 1,
1715
- generatedAt: (now instanceof Date ? now : new Date(now)).toISOString(),
1716
- status,
1717
- file: resolvedFile,
1718
- signalSource: evalSummary.source,
1719
- learning: {
1720
- exists: audit.exists,
1721
- version: audit.version,
1722
- updatedAt: audit.updatedAt,
1723
- count: audit.count,
1724
- categoryCounts: audit.categoryCounts,
1725
- sourceCounts: stats.sourceCounts || {},
1726
- auditSummary: audit.summary,
1727
- },
1728
- usage: {
1729
- usageFile: usage.usageFile,
1730
- exists: usage.exists,
1731
- eventCount: usage.eventCount,
1732
- usedEntryCount: usage.usedEntryCount,
1733
- unusedEntryCount: usage.unusedEntryCount,
1734
- staleSelectedEntryCount: usage.staleSelectedEntryCount,
1735
- commandCounts: usage.commandCounts,
1736
- routeCounts: usage.routeCounts,
1737
- latestEvent: usage.latestEvent || null,
1738
- privacy: usage.privacy,
1739
- },
1740
- evals: evalSummary,
1741
- checkCapture,
1742
- workspace,
1743
- agentDevelopment,
1744
- readiness,
1745
- recommendations,
1746
- privacy: {
1747
- mutatesProfile: false,
1748
- storesRawBriefText: false,
1749
- exposesEntryTextPreview: true,
1750
- readsSignalFilesOnly: true,
1751
- },
1752
- };
1753
- }
1754
-
1755
- export function agentBacklogReport({
1756
- filePath = defaultLearningFile(),
1757
- usageFile = "",
1758
- signalSource = "",
1759
- root = process.cwd(),
1760
- now = new Date(),
1761
- signalRegistryProvider = learningSignalRegistry,
1762
- } = {}) {
1763
- const registry = signalRegistryProvider({
1764
- filePath,
1765
- usageFile,
1766
- signalSource,
1767
- root,
1768
- now,
1769
- });
1770
- const agentDevelopment = registry.agentDevelopment || {
1771
- status: "unknown",
1772
- actionCount: 0,
1773
- p0Count: 0,
1774
- p1Count: 0,
1775
- p2Count: 0,
1776
- p3Count: 0,
1777
- actions: [],
1778
- privacy: {
1779
- mutatesProfile: false,
1780
- mutatesSkillFiles: false,
1781
- callsExternalAiApis: false,
1782
- storesRawBriefText: false,
1783
- },
1784
- };
1785
- const actions = Array.isArray(agentDevelopment.actions) ? agentDevelopment.actions : [];
1786
- const signalsJsonArgs = [
1787
- "design-ai",
1788
- "learn",
1789
- "--signals",
1790
- "--from-file",
1791
- registry.signalSource || ".",
1792
- "--file",
1793
- registry.file || filePath,
1794
- "--usage-file",
1795
- registry.usage?.usageFile || usageFile || defaultLearningUsageFile(path.resolve(filePath)),
1796
- "--json",
1797
- ];
1798
- const signalsReportArgs = [
1799
- "design-ai",
1800
- "learn",
1801
- "--signals",
1802
- "--from-file",
1803
- registry.signalSource || ".",
1804
- "--file",
1805
- registry.file || filePath,
1806
- "--usage-file",
1807
- registry.usage?.usageFile || usageFile || defaultLearningUsageFile(path.resolve(filePath)),
1808
- "--report",
1809
- "--out",
1810
- "learning-signals.md",
1811
- ];
1812
- const agentBacklogJsonArgs = [
1813
- "design-ai",
1814
- "learn",
1815
- "--agent-backlog",
1816
- "--from-file",
1817
- registry.signalSource || ".",
1818
- "--file",
1819
- registry.file || filePath,
1820
- "--usage-file",
1821
- registry.usage?.usageFile || usageFile || defaultLearningUsageFile(path.resolve(filePath)),
1822
- "--strict",
1823
- "--json",
1824
- ];
1825
- const commands = {
1826
- signalsJson: commandFromArgs(signalsJsonArgs),
1827
- signalsJsonArgs,
1828
- signalsReport: commandFromArgs(signalsReportArgs),
1829
- signalsReportArgs,
1830
- agentBacklogJson: commandFromArgs(agentBacklogJsonArgs),
1831
- agentBacklogJsonArgs,
1832
- };
1833
- const privacy = {
1834
- mutatesProfile: false,
1835
- mutatesSkillFiles: false,
1836
- callsExternalAiApis: false,
1837
- storesRawBriefText: false,
1838
- readsSignalFilesOnly: true,
1839
- };
1840
- return {
1841
- version: 1,
1842
- generatedAt: registry.generatedAt || (now instanceof Date ? now : new Date(now)).toISOString(),
1843
- status: agentDevelopment.status || "unknown",
1844
- signalStatus: registry.status || "unknown",
1845
- file: registry.file || path.resolve(filePath),
1846
- usageFile: registry.usage?.usageFile || path.resolve(usageFile || defaultLearningUsageFile(path.resolve(filePath))),
1847
- signalSource: registry.signalSource || (signalSource ? path.resolve(signalSource) : path.resolve(root)),
1848
- counts: {
1849
- actions: agentDevelopment.actionCount ?? actions.length,
1850
- p0: agentDevelopment.p0Count ?? 0,
1851
- p1: agentDevelopment.p1Count ?? 0,
1852
- p2: agentDevelopment.p2Count ?? 0,
1853
- p3: agentDevelopment.p3Count ?? 0,
1854
- learningEntries: registry.learning?.count ?? 0,
1855
- usageEvents: registry.usage?.eventCount ?? 0,
1856
- evalSignals: registry.evals?.count ?? 0,
1857
- checkCaptures: registry.checkCapture?.count ?? 0,
1858
- workspaceNextActions: registry.workspace?.nextActionCount ?? 0,
1859
- },
1860
- actions,
1861
- actionPlan: buildAgentBacklogActionPlan({ actions, commands, privacy }),
1862
- readiness: registry.readiness || null,
1863
- commands,
1864
- recommendations: registry.recommendations || [],
1865
- privacy,
1866
- };
1867
- }
1868
-
1869
- export function renderAgentBacklogReport(payload, {
1870
- generatedAt = new Date(),
1871
- } = {}) {
1872
- const generatedAtText = generatedAt instanceof Date ? generatedAt.toISOString() : String(generatedAt || "");
1873
- const counts = payload.counts || {};
1874
- const actions = Array.isArray(payload.actions) ? payload.actions : [];
1875
- const lines = [
1876
- "# Agent Development Backlog Report",
1877
- "",
1878
- listItem("Generated", generatedAtText),
1879
- listItem("Status", payload.status || "unknown"),
1880
- listItem("Signal status", payload.signalStatus || "unknown"),
1881
- listItem("Learning file", payload.file || ""),
1882
- listItem("Usage file", payload.usageFile || ""),
1883
- listItem("Signal source", payload.signalSource || ""),
1884
- "",
1885
- "## Summary",
1886
- "",
1887
- listItem("Actions", counts.actions ?? actions.length),
1888
- listItem("P0", counts.p0 ?? 0),
1889
- listItem("P1", counts.p1 ?? 0),
1890
- listItem("P2", counts.p2 ?? 0),
1891
- listItem("P3", counts.p3 ?? 0),
1892
- listItem("Learning entries", counts.learningEntries ?? 0),
1893
- listItem("Usage events", counts.usageEvents ?? 0),
1894
- listItem("Eval signals", counts.evalSignals ?? 0),
1895
- listItem("Check captures", counts.checkCaptures ?? 0),
1896
- listItem("Workspace next actions", counts.workspaceNextActions ?? 0),
1897
- ];
1898
-
1899
- const readiness = payload.readiness && typeof payload.readiness === "object" ? payload.readiness : null;
1900
- if (readiness) {
1901
- lines.push(
1902
- "",
1903
- "## Signal Readiness",
1904
- "",
1905
- listItem("Status", readiness.status || payload.signalStatus || "unknown"),
1906
- listItem("Summary", readiness.summary || ""),
1907
- listItem("Required ready", yesNo(Boolean(readiness.requiredReady))),
1908
- listItem("Required checks", `${readiness.requiredPassCount ?? 0}/${readiness.requiredCount ?? 0}`),
1909
- listItem("Blocking checks", readiness.blockingCount ?? 0),
1910
- listItem("Optional gaps", readiness.optionalGapCount ?? 0),
1911
- );
1912
- const checks = Array.isArray(readiness.checks) ? readiness.checks : [];
1913
- renderReadinessCheckIndex(lines, readiness);
1914
- if (checks.length > 0) {
1915
- lines.push("", "Readiness checks:");
1916
- for (const check of checks) {
1917
- const required = check.required ? "required" : "optional";
1918
- lines.push(`- ${check.id || "unknown"} [${required}] ${check.status || "unknown"}: ${check.summary || ""}`);
1919
- }
1920
- }
1921
- renderOptionalGapDetails(lines, readiness);
1922
- }
1923
-
1924
- lines.push("", "## Backlog Actions", "");
1925
-
1926
- if (actions.length === 0) {
1927
- lines.push("No agent development backlog actions emitted.");
1928
- } else {
1929
- for (const action of actions) {
1930
- lines.push(`### ${action.rank}. ${action.title}`);
1931
- lines.push("");
1932
- lines.push(listItem("Id", action.id));
1933
- lines.push(listItem("Priority", action.priority));
1934
- lines.push(listItem("Category", action.category));
1935
- lines.push(listItem("Rationale", action.rationale));
1936
- if (action.command) {
1937
- lines.push("");
1938
- lines.push("Command:");
1939
- lines.push("");
1940
- lines.push("```bash");
1941
- lines.push(action.command);
1942
- lines.push("```");
1943
- }
1944
- const evidence = action.evidence && typeof action.evidence === "object" ? action.evidence : {};
1945
- const evidenceItems = Object.entries(evidence);
1946
- if (evidenceItems.length > 0) {
1947
- lines.push("");
1948
- lines.push("Evidence:");
1949
- for (const [key, value] of evidenceItems) {
1950
- const rendered = typeof value === "object" ? JSON.stringify(value) : String(value);
1951
- lines.push(`- ${key}: ${rendered}`);
1952
- }
1953
- }
1954
- lines.push("");
1955
- }
1956
- }
1957
-
1958
- const actionPlan = payload.actionPlan || {};
1959
- const planSteps = Array.isArray(actionPlan.steps) ? actionPlan.steps : [];
1960
- lines.push("## Action Plan", "");
1961
- const safetySummary = actionPlan.safetySummary && typeof actionPlan.safetySummary === "object" ? actionPlan.safetySummary : null;
1962
- if (safetySummary) {
1963
- lines.push("Safety summary:");
1964
- lines.push(`- Read-only: ${safetySummary.readOnly ?? 0}`);
1965
- lines.push(`- Writes local file: ${safetySummary.writesLocalFile ?? 0}`);
1966
- lines.push(`- Mutates local state: ${safetySummary.mutatesLocalState ?? 0}`);
1967
- lines.push(`- Requires clean workspace: ${safetySummary.requiresCleanWorkspace ?? 0}`);
1968
- lines.push(`- Requires mutation review: ${safetySummary.requiresReviewBeforeMutation ?? 0}`);
1969
- lines.push("");
1970
- }
1971
- const executionQueue = actionPlan.executionQueue && typeof actionPlan.executionQueue === "object" ? actionPlan.executionQueue : null;
1972
- if (executionQueue) {
1973
- lines.push("Execution queue:");
1974
- lines.push(`- Preview/read-only commands: ${executionQueue.previewCount ?? 0}`);
1975
- lines.push(`- Local file-write review commands: ${executionQueue.fileWriteReviewCount ?? 0}`);
1976
- lines.push(`- Local mutation review commands: ${executionQueue.mutationReviewCount ?? 0}`);
1977
- lines.push(`- Ordered commands: ${executionQueue.orderedCount ?? 0}`);
1978
- lines.push(`- Command manifest entries: ${executionQueue.commandManifestCount ?? 0}`);
1979
- const commandEffectSummary = executionQueue.commandEffectSummary && typeof executionQueue.commandEffectSummary === "object"
1980
- ? executionQueue.commandEffectSummary
1981
- : null;
1982
- if (commandEffectSummary) {
1983
- lines.push(`- Command effect targets: output ${commandEffectSummary.outputTargetCount ?? 0}, profile ${commandEffectSummary.profileTargetCount ?? 0}, usage ${commandEffectSummary.usageTargetCount ?? 0}, mutation flags ${commandEffectSummary.mutationFlagCount ?? 0}`);
1984
- }
1985
- const commandEffectReview = executionQueue.commandEffectReview && typeof executionQueue.commandEffectReview === "object"
1986
- ? executionQueue.commandEffectReview
1987
- : null;
1988
- if (commandEffectReview?.headline) {
1989
- lines.push(`- Command effect review: ${commandEffectReview.headline}`);
1990
- const reviewChecklist = Array.isArray(commandEffectReview.checklist) ? commandEffectReview.checklist : [];
1991
- for (const item of reviewChecklist) {
1992
- lines.push(` - ${item}`);
1993
- }
1994
- const gatePhaseSummary = commandEffectReview.gatePhaseSummary && typeof commandEffectReview.gatePhaseSummary === "object"
1995
- ? commandEffectReview.gatePhaseSummary
1996
- : null;
1997
- if (gatePhaseSummary) {
1998
- const phases = Array.isArray(gatePhaseSummary.phases) && gatePhaseSummary.phases.length > 0
1999
- ? gatePhaseSummary.phases.join(", ")
2000
- : "none";
2001
- lines.push(`- Command effect gate phases: ${phases} (${gatePhaseSummary.requiredCount ?? 0}/${gatePhaseSummary.count ?? 0} required)`);
2002
- }
2003
- const gateRunbook = commandEffectReview.gateRunbook && typeof commandEffectReview.gateRunbook === "object"
2004
- ? commandEffectReview.gateRunbook
2005
- : null;
2006
- if (gateRunbook) {
2007
- lines.push(`- Command effect gate runbook: before ${Array.isArray(gateRunbook.before) ? gateRunbook.before.length : 0}, after ${Array.isArray(gateRunbook.after) ? gateRunbook.after.length : 0}, refresh ${Array.isArray(gateRunbook.refresh) ? gateRunbook.refresh.length : 0}`);
2008
- }
2009
- const gateCommands = Array.isArray(commandEffectReview.gateCommands) ? commandEffectReview.gateCommands : [];
2010
- if (gateCommands.length > 0) {
2011
- lines.push("- Command effect gates:");
2012
- for (const item of gateCommands) {
2013
- const phase = item.phase ? `${item.phase}: ` : "";
2014
- lines.push(` - ${phase}${item.label || "Review gate"}: \`${item.command || ""}\``);
2015
- }
2016
- }
2017
- }
2018
- const operatorRunbook = executionQueue.operatorRunbook && typeof executionQueue.operatorRunbook === "object"
2019
- ? executionQueue.operatorRunbook
2020
- : null;
2021
- if (operatorRunbook) {
2022
- lines.push(`- Operator runbook: ${operatorRunbook.stageCount ?? 0} stage(s), ${operatorRunbook.commandCount ?? 0} command(s), ${operatorRunbook.requiredCommandCount ?? 0} required`);
2023
- if (operatorRunbook.nextCommand) {
2024
- lines.push(`- Operator next command: ${operatorRunbook.nextStage || "unknown"}: \`${operatorRunbook.nextCommand}\``);
2025
- }
2026
- const operatorSelection = operatorRunbook.nextCommandSelection && typeof operatorRunbook.nextCommandSelection === "object"
2027
- ? operatorRunbook.nextCommandSelection
2028
- : null;
2029
- if (operatorSelection) {
2030
- lines.push(`- Operator next command selection: ${operatorSelection.strategy || "unknown"} (${operatorSelection.reason || "no reason provided"})`);
2031
- }
2032
- }
2033
- if (executionQueue.nextActionId) lines.push(`- Recommended next action: ${executionQueue.nextActionId}`);
2034
- if (executionQueue.nextCommandRunPolicy) lines.push(`- Recommended next command policy: ${executionQueue.nextCommandRunPolicy}`);
2035
- const nextCommandSelection = executionQueue.nextCommandSelection && typeof executionQueue.nextCommandSelection === "object"
2036
- ? executionQueue.nextCommandSelection
2037
- : null;
2038
- if (nextCommandSelection) {
2039
- lines.push(`- Recommended next command selection: ${nextCommandSelection.strategy || "unknown"} (${nextCommandSelection.reason || "no reason provided"})`);
2040
- if (nextCommandSelection.planNextActionId) {
2041
- lines.push(`- Ranked next action: ${nextCommandSelection.planNextActionId}; matches recommended command: ${nextCommandSelection.matchesPlanNextAction ? "yes" : "no"}`);
2042
- }
2043
- }
2044
- const nextCommandAlignment = executionQueue.nextCommandAlignment && typeof executionQueue.nextCommandAlignment === "object"
2045
- ? executionQueue.nextCommandAlignment
2046
- : null;
2047
- if (nextCommandAlignment) {
2048
- lines.push(`- Operator/queue next command alignment: ${nextCommandAlignment.matchesQueueNextCommand ? "same" : "different"} (${nextCommandAlignment.reason || "no reason provided"})`);
2049
- }
2050
- const operatorHandoff = executionQueue.operatorHandoff && typeof executionQueue.operatorHandoff === "object"
2051
- ? executionQueue.operatorHandoff
2052
- : null;
2053
- if (operatorHandoff) {
2054
- const phase = operatorHandoff.phase ? `${operatorHandoff.phase} ` : "";
2055
- const decision = operatorHandoff.decision ? `${operatorHandoff.decision}; ` : "";
2056
- lines.push(`- Operator handoff: ${phase}${operatorHandoff.source || "unknown"} (${decision}${operatorHandoff.reason || "no reason provided"})`);
2057
- const handoffState = operatorHandoff.state && typeof operatorHandoff.state === "object" ? operatorHandoff.state : null;
2058
- if (handoffState) {
2059
- lines.push(`- Operator handoff state: ${handoffState.status || "unknown"}; ready ${handoffState.ready ? "yes" : "no"}; can run without review ${handoffState.canRunWithoutReview ? "yes" : "no"}; refresh ${handoffState.requiresRefresh ? "required" : "optional"}`);
2060
- if (handoffState.summary) {
2061
- lines.push(`- Operator handoff summary: ${handoffState.summary}`);
2062
- }
2063
- }
2064
- if (operatorHandoff.refreshCommand) {
2065
- lines.push(`- Operator handoff refresh: ${operatorHandoff.refreshCommand}`);
2066
- }
2067
- }
2068
- if (executionQueue.nextCommand) {
2069
- lines.push("");
2070
- lines.push("Recommended next command:");
2071
- lines.push("");
2072
- lines.push("```bash");
2073
- lines.push(executionQueue.nextCommand);
2074
- lines.push("```");
2075
- }
2076
- const orderedItems = Array.isArray(executionQueue.ordered) ? executionQueue.ordered : [];
2077
- if (orderedItems.length > 0) {
2078
- lines.push("");
2079
- lines.push("Queue order:");
2080
- for (const item of orderedItems) {
2081
- lines.push(`${item.rank}. ${item.actionId || "unknown-action"} (${item.safetyLevel || "unknown"}, ${item.runPolicy || "manual-review"})`);
2082
- }
2083
- }
2084
- const commandManifest = Array.isArray(executionQueue.commandManifest) ? executionQueue.commandManifest : [];
2085
- if (commandManifest.length > 0) {
2086
- lines.push("");
2087
- lines.push("Command manifest:");
2088
- for (const item of commandManifest) {
2089
- lines.push(`${item.rank}. ${item.actionId || "unknown-action"} - ${item.runPolicy || "manual-review"} (${summarizeAgentBacklogCommandEffects(item.commandEffects)})`);
2090
- }
2091
- }
2092
- lines.push("");
2093
- }
2094
- if (planSteps.length === 0) {
2095
- lines.push("No execution steps emitted.");
2096
- } else {
2097
- for (const step of planSteps) {
2098
- lines.push(`### Step ${step.rank}. ${step.title}`);
2099
- lines.push("");
2100
- lines.push(listItem("Action id", step.actionId));
2101
- lines.push(listItem("Priority", step.priority));
2102
- lines.push(listItem("Category", step.category));
2103
- const commandSafety = step.commandSafety && typeof step.commandSafety === "object" ? step.commandSafety : {};
2104
- lines.push(listItem("Command safety", commandSafety.level || "unknown"));
2105
- lines.push(listItem("Writes local files", yesNo(Boolean(commandSafety.writesLocalFiles))));
2106
- lines.push(listItem("Mutates local state", yesNo(Boolean(commandSafety.mutatesLocalState))));
2107
- lines.push(listItem("Requires mutation review", yesNo(Boolean(step.requiresReviewBeforeMutation))));
2108
- if (commandSafety.reason) lines.push(listItem("Safety reason", commandSafety.reason));
2109
- if (step.expectedOutcome) lines.push(listItem("Expected outcome", step.expectedOutcome));
2110
- if (step.command) {
2111
- lines.push("");
2112
- lines.push("Command:");
2113
- lines.push("");
2114
- lines.push("```bash");
2115
- lines.push(step.command);
2116
- lines.push("```");
2117
- }
2118
- if (step.applyCommand) {
2119
- const applySafety = step.applyCommandSafety && typeof step.applyCommandSafety === "object" ? step.applyCommandSafety : {};
2120
- lines.push("");
2121
- lines.push("Apply command after review:");
2122
- lines.push("");
2123
- lines.push("```bash");
2124
- lines.push(step.applyCommand);
2125
- lines.push("```");
2126
- lines.push(listItem("Apply command safety", applySafety.level || "unknown"));
2127
- lines.push(listItem("Apply requires mutation review", yesNo(Boolean(step.applyRequiresReviewBeforeMutation))));
2128
- }
2129
- const verification = Array.isArray(step.verification) ? step.verification : [];
2130
- if (verification.length > 0) {
2131
- lines.push("");
2132
- lines.push("Verification:");
2133
- for (const item of verification) {
2134
- lines.push(`- ${item}`);
2135
- }
2136
- }
2137
- lines.push("");
2138
- }
2139
- }
2140
-
2141
- const recommendations = Array.isArray(payload.recommendations) ? payload.recommendations : [];
2142
- lines.push("## Recommendations", "");
2143
- if (recommendations.length === 0) {
2144
- lines.push("No recommendations emitted.");
2145
- } else {
2146
- for (const recommendation of recommendations) {
2147
- lines.push(`- ${recommendation.level}: ${recommendation.text}`);
2148
- }
2149
- }
2150
-
2151
- const commands = payload.commands || {};
2152
- lines.push("", "## Follow-Up Commands", "");
2153
- if (commands.signalsJson) {
2154
- lines.push("Signal registry JSON:");
2155
- lines.push("");
2156
- lines.push("```bash");
2157
- lines.push(commands.signalsJson);
2158
- lines.push("```");
2159
- lines.push("");
2160
- }
2161
- if (commands.signalsReport) {
2162
- lines.push("Signal registry Markdown:");
2163
- lines.push("");
2164
- lines.push("```bash");
2165
- lines.push(commands.signalsReport);
2166
- lines.push("```");
2167
- lines.push("");
2168
- }
2169
-
2170
- const privacy = payload.privacy || {};
2171
- lines.push("## Privacy And Boundaries", "");
2172
- lines.push(listItem("Mutates learning profile", yesNo(Boolean(privacy.mutatesProfile))));
2173
- lines.push(listItem("Mutates skill files", yesNo(Boolean(privacy.mutatesSkillFiles))));
2174
- lines.push(listItem("Calls external AI APIs", yesNo(Boolean(privacy.callsExternalAiApis))));
2175
- lines.push(listItem("Stores raw brief text", yesNo(Boolean(privacy.storesRawBriefText))));
2176
- lines.push(listItem("Reads signal files only", yesNo(Boolean(privacy.readsSignalFilesOnly))));
2177
- lines.push("", "This report is read-only evidence; it does not mutate learning profiles, usage sidecars, eval files, skill files, or target repositories.");
2178
-
2179
- return `${lines.join("\n")}\n`;
2180
- }
2181
-
2182
- export function renderLearningSignalReport(payload, {
2183
- generatedAt = new Date(),
2184
- } = {}) {
2185
- const generatedAtText = generatedAt instanceof Date ? generatedAt.toISOString() : String(generatedAt || "");
2186
- const learning = payload.learning || {};
2187
- const usage = payload.usage || {};
2188
- const evals = payload.evals || {};
2189
- const workspace = payload.workspace || {};
2190
- const agentDevelopment = payload.agentDevelopment || {};
2191
- const readiness = payload.readiness || {};
2192
- const lines = [
2193
- "# Learning Signal Registry Report",
2194
- "",
2195
- listItem("Generated", generatedAtText),
2196
- listItem("Status", payload.status || "unknown"),
2197
- listItem("Learning file", payload.file || ""),
2198
- listItem("Signal source", payload.signalSource || ""),
2199
- "",
2200
- "## Readiness Summary",
2201
- "",
2202
- listItem("Status", readiness.status || payload.status || "unknown"),
2203
- listItem("Summary", readiness.summary || ""),
2204
- listItem("Required ready", yesNo(Boolean(readiness.requiredReady))),
2205
- listItem("Required checks", `${readiness.requiredPassCount ?? 0}/${readiness.requiredCount ?? 0}`),
2206
- listItem("Blocking checks", readiness.blockingCount ?? 0),
2207
- listItem("Optional gaps", readiness.optionalGapCount ?? 0),
2208
- ];
2209
-
2210
- const readinessChecks = Array.isArray(readiness.checks) ? readiness.checks : [];
2211
- renderReadinessCheckIndex(lines, readiness);
2212
- if (readinessChecks.length > 0) {
2213
- lines.push("", "Readiness checks:");
2214
- for (const check of readinessChecks) {
2215
- const required = check.required ? "required" : "optional";
2216
- lines.push(`- ${check.id || "unknown"} [${required}] ${check.status || "unknown"}: ${check.summary || ""}`);
2217
- }
2218
- }
2219
- renderOptionalGapDetails(lines, readiness);
2220
-
2221
- lines.push(
2222
- "",
2223
- "## Learning Profile",
2224
- "",
2225
- listItem("Exists", yesNo(Boolean(learning.exists))),
2226
- listItem("Version", learning.version ?? ""),
2227
- listItem("Updated at", learning.updatedAt || ""),
2228
- listItem("Entries", learning.count ?? 0),
2229
- listItem("Audit status", learning.auditSummary?.status || "unknown"),
2230
- listItem("Audit failures", learning.auditSummary?.failures ?? 0),
2231
- listItem("Audit warnings", learning.auditSummary?.warnings ?? 0),
2232
- "",
2233
- "## Usage Signals",
2234
- "",
2235
- listItem("Usage file", usage.usageFile || ""),
2236
- listItem("Exists", yesNo(Boolean(usage.exists))),
2237
- listItem("Events", usage.eventCount ?? 0),
2238
- listItem("Used entries", usage.usedEntryCount ?? 0),
2239
- listItem("Unused entries", usage.unusedEntryCount ?? 0),
2240
- listItem("Stale selected ids", usage.staleSelectedEntryCount ?? 0),
2241
- listItem("Stores raw brief text", yesNo(Boolean(usage.privacy?.storesRawBriefText))),
2242
- "",
2243
- "## Eval Signals",
2244
- "",
2245
- listItem("Source", evals.source || ""),
2246
- listItem("Files", evals.count ?? 0),
2247
- listItem("Reports", evals.reports ?? 0),
2248
- listItem("Templates", evals.templates ?? 0),
2249
- listItem("Passed", evals.passed ?? 0),
2250
- listItem("Warned", evals.warned ?? 0),
2251
- listItem("Failed", evals.failed ?? 0),
2252
- );
2253
-
2254
- const evalFiles = Array.isArray(evals.files) ? evals.files : [];
2255
- if (evalFiles.length > 0) {
2256
- lines.push("", "Eval files:");
2257
- for (const item of evalFiles) {
2258
- const counts = item.shape === "report"
2259
- ? ` pass ${item.passed} / warn ${item.warned} / fail ${item.failed}`
2260
- : `${item.caseCount} case(s)`;
2261
- lines.push(`- \`${item.file}\`: ${item.kind} ${item.shape} ${item.status} (${counts})`);
2262
- if (item.error) lines.push(` - ${item.error}`);
2263
- }
2264
- }
2265
-
2266
- const latestCaptures = Array.isArray(payload.checkCapture?.latestEntries)
2267
- ? payload.checkCapture.latestEntries
2268
- : [];
2269
- lines.push("", "## Check Capture", "");
2270
- lines.push(listItem("Entries", payload.checkCapture?.count ?? 0));
2271
- if (latestCaptures.length > 0) {
2272
- lines.push("", "Recent captures:");
2273
- for (const entry of latestCaptures) {
2274
- lines.push(`- \`${entry.id}\` [${entry.category}] ${entry.source}`);
2275
- if (entry.textPreview) lines.push(` - ${entry.textPreview}`);
2276
- }
2277
- }
2278
-
2279
- lines.push("", "## Workspace Readiness", "");
2280
- lines.push(listItem("Root", workspace.root || ""));
2281
- lines.push(listItem("Branch", workspace.git?.branch || "unknown"));
2282
- lines.push(listItem("Clean", yesNo(Boolean(workspace.git?.clean))));
2283
- lines.push(listItem("Repository status", workspace.repository?.status || "unknown"));
2284
- lines.push(listItem("Learning status", workspace.learning?.status || "unknown"));
2285
- lines.push(listItem("Usage status", workspace.learningUsage?.status || "unknown"));
2286
- lines.push(listItem("Eval status", workspace.learningEval?.status || "not checked"));
2287
- lines.push(listItem("Next actions", workspace.nextActionCount ?? 0));
2288
-
2289
- const actions = Array.isArray(agentDevelopment.actions) ? agentDevelopment.actions : [];
2290
- lines.push("", "## Agent Development Backlog", "");
2291
- lines.push(listItem("Status", agentDevelopment.status || "unknown"));
2292
- lines.push(listItem("Actions", agentDevelopment.actionCount ?? actions.length));
2293
- lines.push(listItem("P0", agentDevelopment.p0Count ?? 0));
2294
- lines.push(listItem("P1", agentDevelopment.p1Count ?? 0));
2295
- lines.push(listItem("P2", agentDevelopment.p2Count ?? 0));
2296
- lines.push(listItem("P3", agentDevelopment.p3Count ?? 0));
2297
- if (actions.length > 0) {
2298
- lines.push("");
2299
- for (const action of actions) {
2300
- lines.push(`### ${action.rank}. ${action.title}`);
2301
- lines.push("");
2302
- lines.push(listItem("Id", action.id));
2303
- lines.push(listItem("Priority", action.priority));
2304
- lines.push(listItem("Category", action.category));
2305
- lines.push(listItem("Rationale", action.rationale));
2306
- if (action.command) {
2307
- lines.push("");
2308
- lines.push("Command:");
2309
- lines.push("");
2310
- lines.push("```bash");
2311
- lines.push(action.command);
2312
- lines.push("```");
2313
- }
2314
- lines.push("");
2315
- }
2316
- }
2317
-
2318
- const recommendations = Array.isArray(payload.recommendations) ? payload.recommendations : [];
2319
- lines.push("## Recommendations", "");
2320
- if (recommendations.length === 0) {
2321
- lines.push("No recommendations emitted.");
2322
- } else {
2323
- for (const recommendation of recommendations) {
2324
- lines.push(`- ${recommendation.level}: ${recommendation.text}`);
2325
- }
2326
- }
2327
-
2328
- const privacy = payload.privacy || {};
2329
- lines.push("", "## Privacy And Boundaries", "");
2330
- lines.push(listItem("Mutates learning profile", yesNo(Boolean(privacy.mutatesProfile))));
2331
- lines.push(listItem("Stores raw brief text", yesNo(Boolean(privacy.storesRawBriefText))));
2332
- lines.push(listItem("Reads signal files only", yesNo(Boolean(privacy.readsSignalFilesOnly))));
2333
- lines.push(listItem("External AI APIs", "no"));
2334
- lines.push("", "This report is read-only evidence; it does not mutate learning profiles, usage sidecars, eval files, skill files, or target repositories.");
2335
-
2336
- return `${lines.join("\n")}\n`;
2337
- }
1
+ // Public learning signal surface for `design-ai learn --signals` — re-exports the signals-* modules.
2
+
3
+ export {
4
+ DEFAULT_SIGNAL_EVAL_FILES,
5
+ summarizeSignalEvalFile,
6
+ } from "./signals-eval.mjs";
7
+
8
+ export {
9
+ agentBacklogReport,
10
+ learningSignalRegistry,
11
+ } from "./signals-registry.mjs";
12
+
13
+ export {
14
+ renderAgentBacklogReport,
15
+ renderLearningSignalReport,
16
+ } from "./signals-render.mjs";