@topogram/cli 0.3.64 → 0.3.65

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 (245) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan/index.js +703 -0
  3. package/src/adoption/plan.js +12 -703
  4. package/src/agent-ops/query-builders/auth.js +375 -0
  5. package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
  6. package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
  7. package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
  8. package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
  9. package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
  10. package/src/agent-ops/query-builders/change-risk.js +25 -0
  11. package/src/agent-ops/query-builders/common.js +149 -0
  12. package/src/agent-ops/query-builders/maintained-risk.js +539 -0
  13. package/src/agent-ops/query-builders/maintained-shared.js +120 -0
  14. package/src/agent-ops/query-builders/multi-agent.js +547 -0
  15. package/src/agent-ops/query-builders/projection-impacts.js +514 -0
  16. package/src/agent-ops/query-builders/work-packets.js +417 -0
  17. package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
  18. package/src/agent-ops/query-builders/workflow-context.js +398 -0
  19. package/src/agent-ops/query-builders/workflow-presets-core.js +676 -0
  20. package/src/agent-ops/query-builders/workflow-presets.js +341 -0
  21. package/src/agent-ops/query-builders.d.ts +26 -26
  22. package/src/agent-ops/query-builders.js +42 -5021
  23. package/src/catalog/constants.js +10 -0
  24. package/src/catalog/copy.js +60 -0
  25. package/src/catalog/diagnostics.js +15 -0
  26. package/src/catalog/entries.js +42 -0
  27. package/src/catalog/files.js +67 -0
  28. package/src/catalog/provenance.js +122 -0
  29. package/src/catalog/source.js +150 -0
  30. package/src/catalog/validation.js +252 -0
  31. package/src/catalog.d.ts +2 -0
  32. package/src/catalog.js +18 -746
  33. package/src/cli/commands/catalog/check.js +31 -0
  34. package/src/cli/commands/catalog/copy.js +59 -0
  35. package/src/cli/commands/catalog/doctor.js +248 -0
  36. package/src/cli/commands/catalog/help.js +21 -0
  37. package/src/cli/commands/catalog/list.js +52 -0
  38. package/src/cli/commands/catalog/runner.js +92 -0
  39. package/src/cli/commands/catalog/shared.js +17 -0
  40. package/src/cli/commands/catalog/show.js +134 -0
  41. package/src/cli/commands/catalog.js +30 -615
  42. package/src/cli/commands/generator-policy/package-info.js +162 -0
  43. package/src/cli/commands/generator-policy/payloads.js +372 -0
  44. package/src/cli/commands/generator-policy/printers.js +159 -0
  45. package/src/cli/commands/generator-policy/runner.js +81 -0
  46. package/src/cli/commands/generator-policy/shared.js +39 -0
  47. package/src/cli/commands/generator-policy.js +15 -783
  48. package/src/cli/commands/import/adopt.js +170 -0
  49. package/src/cli/commands/import/check.js +91 -0
  50. package/src/cli/commands/import/diff.js +84 -0
  51. package/src/cli/commands/import/help.js +47 -0
  52. package/src/cli/commands/import/paths.js +277 -0
  53. package/src/cli/commands/import/plan.js +284 -0
  54. package/src/cli/commands/import/refresh.js +470 -0
  55. package/src/cli/commands/import/status-history.js +196 -0
  56. package/src/cli/commands/import/workspace.js +230 -0
  57. package/src/cli/commands/import.js +33 -1732
  58. package/src/cli/commands/package/constants.js +17 -0
  59. package/src/cli/commands/package/doctor.js +240 -0
  60. package/src/cli/commands/package/help.js +27 -0
  61. package/src/cli/commands/package/lockfile.js +135 -0
  62. package/src/cli/commands/package/npm.js +97 -0
  63. package/src/cli/commands/package/reporting.js +35 -0
  64. package/src/cli/commands/package/runner.js +33 -0
  65. package/src/cli/commands/package/shared.js +9 -0
  66. package/src/cli/commands/package/update-cli.js +252 -0
  67. package/src/cli/commands/package/versions.js +35 -0
  68. package/src/cli/commands/package.js +29 -813
  69. package/src/cli/commands/query/change-plan.js +68 -0
  70. package/src/cli/commands/query/definitions.js +202 -0
  71. package/src/cli/commands/query/import-adopt.js +121 -0
  72. package/src/cli/commands/query/runner/artifacts.js +102 -0
  73. package/src/cli/commands/query/runner/boundaries.js +211 -0
  74. package/src/cli/commands/query/runner/change.js +182 -0
  75. package/src/cli/commands/query/runner/import-adopt.js +111 -0
  76. package/src/cli/commands/query/runner/index.js +31 -0
  77. package/src/cli/commands/query/runner/output.js +12 -0
  78. package/src/cli/commands/query/runner/workflow.js +241 -0
  79. package/src/cli/commands/query/runner.js +3 -0
  80. package/src/cli/commands/query/workflow-context.js +5 -0
  81. package/src/cli/commands/query/workspace.js +274 -0
  82. package/src/cli/commands/query.js +9 -1300
  83. package/src/cli/commands/template/baseline.js +100 -0
  84. package/src/cli/commands/template/check.js +466 -0
  85. package/src/cli/commands/template/constants.js +8 -0
  86. package/src/cli/commands/template/diagnostics.js +26 -0
  87. package/src/cli/commands/template/help.js +28 -0
  88. package/src/cli/commands/template/lifecycle.js +404 -0
  89. package/src/cli/commands/template/list-show.js +287 -0
  90. package/src/cli/commands/template/policy.js +422 -0
  91. package/src/cli/commands/template/shared.js +127 -0
  92. package/src/cli/commands/template/updates.js +352 -0
  93. package/src/cli/commands/template.js +41 -2143
  94. package/src/generator/api/contracts.js +497 -0
  95. package/src/generator/api/metadata.js +221 -0
  96. package/src/generator/api/openapi.js +559 -0
  97. package/src/generator/api/schema.js +124 -0
  98. package/src/generator/api/types.d.ts +98 -0
  99. package/src/generator/api.js +3 -1195
  100. package/src/generator/context/shared/domain-sdlc.js +282 -0
  101. package/src/generator/context/shared/maintained-boundary.js +665 -0
  102. package/src/generator/context/shared/metrics.js +85 -0
  103. package/src/generator/context/shared/primitives.js +64 -0
  104. package/src/generator/context/shared/relationships.js +453 -0
  105. package/src/generator/context/shared/summaries.js +263 -0
  106. package/src/generator/context/shared/types.d.ts +207 -0
  107. package/src/generator/context/shared.d.ts +42 -0
  108. package/src/generator/context/shared.js +80 -1390
  109. package/src/generator/context/slice/core.js +397 -0
  110. package/src/generator/context/slice/sdlc.js +417 -0
  111. package/src/generator/context/slice/ui-packets.js +183 -0
  112. package/src/generator/context/slice.js +2 -859
  113. package/src/generator/registry/index.js +507 -0
  114. package/src/generator/registry.js +18 -504
  115. package/src/generator/runtime/environment/index.js +666 -0
  116. package/src/generator/runtime/environment.js +4 -666
  117. package/src/generator/runtime/runtime-check/index.js +554 -0
  118. package/src/generator/runtime/runtime-check.js +4 -554
  119. package/src/generator/runtime/shared/index.js +572 -0
  120. package/src/generator/runtime/shared.js +19 -570
  121. package/src/generator/shared.d.ts +2 -0
  122. package/src/generator/surfaces/shared.d.ts +3 -0
  123. package/src/generator/widget-conformance/behavior-report.js +258 -0
  124. package/src/generator/widget-conformance/checks.js +371 -0
  125. package/src/generator/widget-conformance/projection-context.js +200 -0
  126. package/src/generator/widget-conformance/report.js +166 -0
  127. package/src/generator/widget-conformance/types.d.ts +121 -0
  128. package/src/generator/widget-conformance.js +3 -824
  129. package/src/import/core/context.d.ts +3 -0
  130. package/src/import/core/contracts.d.ts +1 -0
  131. package/src/import/core/registry.d.ts +4 -0
  132. package/src/import/core/runner/candidates.js +217 -0
  133. package/src/import/core/runner/options.js +22 -0
  134. package/src/import/core/runner/reports.js +50 -0
  135. package/src/import/core/runner/run.js +79 -0
  136. package/src/import/core/runner/tracks.js +150 -0
  137. package/src/import/core/runner/ui-drafts.js +337 -0
  138. package/src/import/core/runner.js +3 -698
  139. package/src/import/core/shared/api-routes.js +221 -0
  140. package/src/import/core/shared/candidates.js +97 -0
  141. package/src/import/core/shared/files.js +177 -0
  142. package/src/import/core/shared/next-app.js +389 -0
  143. package/src/import/core/shared/types.d.ts +51 -0
  144. package/src/import/core/shared/ui-routes.js +230 -0
  145. package/src/import/core/shared.js +60 -861
  146. package/src/new-project/constants.js +128 -0
  147. package/src/new-project/create.js +83 -0
  148. package/src/new-project/json.js +28 -0
  149. package/src/new-project/metadata.js +96 -0
  150. package/src/new-project/package-spec.js +161 -0
  151. package/src/new-project/project-files.js +348 -0
  152. package/src/new-project/template-policy.js +269 -0
  153. package/src/new-project/template-resolution.js +368 -0
  154. package/src/new-project/template-snapshots.js +430 -0
  155. package/src/new-project/template-updates.js +512 -0
  156. package/src/new-project/types.d.ts +83 -0
  157. package/src/new-project.js +6 -2277
  158. package/src/parser.d.ts +87 -1
  159. package/src/parser.js +118 -0
  160. package/src/policy/review-boundaries.d.ts +15 -0
  161. package/src/project-config/index.js +564 -0
  162. package/src/project-config.js +19 -561
  163. package/src/resolver/enrich/acceptance-criterion.js +2 -0
  164. package/src/resolver/enrich/bug.js +2 -0
  165. package/src/resolver/enrich/pitch.js +2 -0
  166. package/src/resolver/enrich/requirement.js +2 -0
  167. package/src/resolver/enrich/task.js +2 -0
  168. package/src/resolver/index.js +19 -2089
  169. package/src/resolver/normalize.js +384 -1
  170. package/src/resolver/plans.js +168 -0
  171. package/src/resolver/projections-api.js +494 -0
  172. package/src/resolver/projections-db.js +133 -0
  173. package/src/resolver/projections-ui.js +317 -0
  174. package/src/resolver/shapes.js +251 -0
  175. package/src/resolver/shared.js +278 -0
  176. package/src/resolver/widgets.js +132 -0
  177. package/src/template-trust/constants.js +62 -0
  178. package/src/template-trust/content.js +258 -0
  179. package/src/template-trust/diff.js +92 -0
  180. package/src/template-trust/policy.js +61 -0
  181. package/src/template-trust/record.js +90 -0
  182. package/src/template-trust/status.js +182 -0
  183. package/src/template-trust.js +24 -687
  184. package/src/text-helpers.d.ts +1 -0
  185. package/src/topogram-types.d.ts +69 -0
  186. package/src/validator/common.js +488 -0
  187. package/src/validator/data-model.js +237 -0
  188. package/src/validator/docs.js +167 -0
  189. package/src/validator/expressions.js +146 -1
  190. package/src/validator/index.d.ts +23 -0
  191. package/src/validator/index.js +32 -3585
  192. package/src/validator/kinds.d.ts +41 -0
  193. package/src/validator/kinds.js +2 -0
  194. package/src/validator/model-helpers.js +46 -0
  195. package/src/validator/per-kind/acceptance-criterion.js +5 -0
  196. package/src/validator/per-kind/bug.js +6 -0
  197. package/src/validator/per-kind/domain.js +15 -2
  198. package/src/validator/per-kind/pitch.js +7 -0
  199. package/src/validator/per-kind/requirement.js +5 -0
  200. package/src/validator/per-kind/task.js +7 -0
  201. package/src/validator/per-kind/widget.js +14 -0
  202. package/src/validator/projections/api-http-async.js +410 -0
  203. package/src/validator/projections/api-http-authz.js +88 -0
  204. package/src/validator/projections/api-http-core.js +205 -0
  205. package/src/validator/projections/api-http-policies.js +339 -0
  206. package/src/validator/projections/api-http-responses.js +233 -0
  207. package/src/validator/projections/api-http.js +44 -0
  208. package/src/validator/projections/db.js +353 -0
  209. package/src/validator/projections/generator-defaults.js +45 -0
  210. package/src/validator/projections/helpers.js +87 -0
  211. package/src/validator/projections/ui-helpers.js +214 -0
  212. package/src/validator/projections/ui-navigation.js +344 -0
  213. package/src/validator/projections/ui-structure.js +364 -0
  214. package/src/validator/projections/ui-widgets.js +493 -0
  215. package/src/validator/projections/ui.js +46 -0
  216. package/src/validator/registry.js +48 -1
  217. package/src/validator/utils.d.ts +20 -0
  218. package/src/validator/utils.js +115 -12
  219. package/src/widget-behavior.d.ts +1 -0
  220. package/src/workflows/import-app/api/collect.js +221 -0
  221. package/src/workflows/import-app/api/openapi.js +257 -0
  222. package/src/workflows/import-app/api/routes.js +327 -0
  223. package/src/workflows/import-app/api/sources.js +22 -0
  224. package/src/workflows/import-app/api.js +2 -797
  225. package/src/workflows/reconcile/adoption-plan/build.js +208 -0
  226. package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
  227. package/src/workflows/reconcile/adoption-plan/outputs.js +143 -0
  228. package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
  229. package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
  230. package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
  231. package/src/workflows/reconcile/adoption-plan.js +30 -740
  232. package/src/workflows/reconcile/auth/closures.js +115 -0
  233. package/src/workflows/reconcile/auth/formatters.js +142 -0
  234. package/src/workflows/reconcile/auth/inference.js +330 -0
  235. package/src/workflows/reconcile/auth/roles.js +122 -0
  236. package/src/workflows/reconcile/auth.js +35 -690
  237. package/src/workflows/reconcile/bundle-core/index.js +600 -0
  238. package/src/workflows/reconcile/bundle-core.js +12 -598
  239. package/src/workflows/reconcile/canonical-surface.js +1 -1
  240. package/src/workflows/reconcile/impacts/adoption-plan.js +192 -0
  241. package/src/workflows/reconcile/impacts/indexes.js +101 -0
  242. package/src/workflows/reconcile/impacts/patches.js +252 -0
  243. package/src/workflows/reconcile/impacts/reports.js +80 -0
  244. package/src/workflows/reconcile/impacts.js +14 -623
  245. package/src/workspace-docs.d.ts +29 -0
@@ -0,0 +1,676 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import {
5
+ CANONICAL_TASK_MODES,
6
+ PROVIDER_PRESET_MANUAL_DECISION_CATEGORIES,
7
+ WORKFLOW_REVIEW_BLOCKERS,
8
+ stableOrderedUnion,
9
+ stableSortedStrings
10
+ } from "./common.js";
11
+ export function workflowPresetReviewClass(preset) {
12
+ const categories = stableSortedStrings([
13
+ ...(preset?.review_policy?.escalate_categories || []),
14
+ ...(preset?.review_escalation_categories || [])
15
+ ]);
16
+ if (categories.some((category) => PROVIDER_PRESET_MANUAL_DECISION_CATEGORIES.has(category))) {
17
+ return "manual_decision";
18
+ }
19
+ return "review_required";
20
+ }
21
+
22
+ export function readJsonArtifactsFromDir(dirPath) {
23
+ if (!dirPath || !fs.existsSync(dirPath)) {
24
+ return [];
25
+ }
26
+ return fs.readdirSync(dirPath)
27
+ .filter((entry) => entry.endsWith(".json"))
28
+ .map((entry) => {
29
+ const filePath = path.join(dirPath, entry);
30
+ try {
31
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
32
+ return { parsed, filePath };
33
+ } catch {
34
+ return null;
35
+ }
36
+ })
37
+ .filter(Boolean);
38
+ }
39
+
40
+ export function normalizePresetAppliesTo(appliesTo = {}) {
41
+ return {
42
+ task_classes: stableSortedStrings(appliesTo.task_classes || appliesTo.task_modes || []),
43
+ provider_ids: stableSortedStrings(appliesTo.provider_ids || []),
44
+ provider_kinds: stableSortedStrings(appliesTo.provider_kinds || []),
45
+ outputs: stableSortedStrings(appliesTo.outputs || []),
46
+ integration_categories: stableSortedStrings(appliesTo.integration_categories || []),
47
+ query_families: stableSortedStrings(appliesTo.query_families || [])
48
+ };
49
+ }
50
+
51
+ export function normalizePresetActivation(activation = {}) {
52
+ return {
53
+ ...normalizePresetAppliesTo(activation || {}),
54
+ manual_only: Boolean(activation?.manual_only)
55
+ };
56
+ }
57
+
58
+ export function normalizeProviderWorkflowPresetManifest(manifest, {
59
+ filePath = null
60
+ } = {}) {
61
+ if (!manifest || typeof manifest !== "object") return null;
62
+ const provider = manifest.provider && typeof manifest.provider === "object" ? manifest.provider : {};
63
+ if (!provider.id) return null;
64
+ const exportsSection = manifest.exports && typeof manifest.exports === "object" ? manifest.exports : {};
65
+ const reviewDefaults = manifest.review_defaults && typeof manifest.review_defaults === "object" ? manifest.review_defaults : {};
66
+ const requirements = manifest.requirements && typeof manifest.requirements === "object" ? manifest.requirements : {};
67
+ const rawWorkflowPresets = Array.isArray(exportsSection.workflow_presets) ? exportsSection.workflow_presets : [];
68
+ const workflowPresets = rawWorkflowPresets
69
+ .filter((entry) => entry && typeof entry === "object")
70
+ .map((entry) => {
71
+ const recommendedTaskMode = entry.recommended_task_mode || null;
72
+ const validation = {
73
+ missing_path: !entry.path,
74
+ invalid_kind: Boolean(entry.kind) && entry.kind !== "provider_workflow_preset",
75
+ invalid_recommended_task_mode: Boolean(recommendedTaskMode) && !CANONICAL_TASK_MODES.has(recommendedTaskMode),
76
+ invalid_review_default: Boolean(reviewDefaults.workflow_presets) && !WORKFLOW_REVIEW_BLOCKERS.has(reviewDefaults.workflow_presets) && reviewDefaults.workflow_presets !== "safe"
77
+ };
78
+ return {
79
+ id: entry.id || path.basename(entry.path || "", path.extname(entry.path || "")) || null,
80
+ label: entry.label || entry.id || null,
81
+ path: entry.path || null,
82
+ kind: entry.kind || "provider_workflow_preset",
83
+ applies_to: normalizePresetAppliesTo(entry.applies_to || {}),
84
+ proof_ref: entry.proof_ref || null,
85
+ recommended_task_mode: recommendedTaskMode,
86
+ validation,
87
+ valid: !Object.values(validation).some(Boolean)
88
+ };
89
+ })
90
+ .filter((entry) => entry.id);
91
+ return {
92
+ provider: {
93
+ id: provider.id,
94
+ kind: provider.kind || null,
95
+ display_name: provider.display_name || provider.name || provider.id,
96
+ version: provider.version || null
97
+ },
98
+ file_path: filePath,
99
+ workflow_core_version: requirements.workflow_core_version || null,
100
+ review_defaults: {
101
+ workflow_presets: reviewDefaults.workflow_presets || "review_required"
102
+ },
103
+ exports: {
104
+ workflow_presets: workflowPresets
105
+ }
106
+ };
107
+ }
108
+
109
+ export function normalizeWorkflowPresetArtifact(preset, {
110
+ kind,
111
+ filePath = null,
112
+ provenance = null
113
+ } = {}) {
114
+ if (!preset || typeof preset !== "object") {
115
+ return null;
116
+ }
117
+
118
+ const normalizedKind = preset.kind || kind || null;
119
+ if (!["provider_workflow_preset", "team_workflow_preset"].includes(normalizedKind)) {
120
+ return null;
121
+ }
122
+
123
+ const adoptionState = preset.adoption_state || preset.state || (normalizedKind === "team_workflow_preset" ? "accept" : "stage");
124
+ const sourcePriority = Number.isFinite(preset.source_priority)
125
+ ? preset.source_priority
126
+ : (normalizedKind === "team_workflow_preset" ? 200 : 100);
127
+ const priority = Number.isFinite(preset.priority) ? preset.priority : sourcePriority;
128
+ const toolHints = preset.tool_hints && typeof preset.tool_hints === "object" ? preset.tool_hints : {};
129
+ const provider = preset.provider && typeof preset.provider === "object" ? preset.provider : {};
130
+
131
+ return {
132
+ id: preset.id || path.basename(filePath || "", ".json") || null,
133
+ label: preset.label || preset.id || path.basename(filePath || "", ".json") || null,
134
+ kind: normalizedKind,
135
+ applies_to: normalizePresetAppliesTo(preset.applies_to || {}),
136
+ recommended_task_mode: preset.recommended_task_mode || null,
137
+ preferred_queries: stableOrderedUnion(preset.preferred_queries || []),
138
+ artifact_load_order: stableOrderedUnion(preset.artifact_load_order || []),
139
+ review_policy: {
140
+ block_on: stableSortedStrings((preset.review_policy?.block_on || []).filter((entry) => WORKFLOW_REVIEW_BLOCKERS.has(entry))),
141
+ escalate_categories: stableSortedStrings(preset.review_policy?.escalate_categories || [])
142
+ },
143
+ verification_policy: {
144
+ required: stableOrderedUnion(preset.verification_policy?.required || []),
145
+ recommended: stableOrderedUnion(preset.verification_policy?.recommended || []),
146
+ require_output_specific_checks: Boolean(preset.verification_policy?.require_output_specific_checks),
147
+ require_maintained_checks_when_seams_affected: Boolean(preset.verification_policy?.require_maintained_checks_when_seams_affected)
148
+ },
149
+ multi_agent_policy: {
150
+ allowed: preset.multi_agent_policy?.allowed,
151
+ default_strategy: preset.multi_agent_policy?.default_strategy || null
152
+ },
153
+ handoff_defaults: {
154
+ required_fields: stableOrderedUnion(preset.handoff_defaults?.required_fields || [])
155
+ },
156
+ tool_hints: toolHints,
157
+ active: preset.active !== false,
158
+ activation: normalizePresetActivation(preset.activation || {}),
159
+ priority,
160
+ provenance: preset.provenance || provenance || {
161
+ source_path: filePath,
162
+ provider_id: provider.id || preset.provider_id || null
163
+ },
164
+ source_priority: sourcePriority,
165
+ adoption_state: adoptionState,
166
+ provider: {
167
+ id: provider.id || preset.provider_id || null,
168
+ kind: provider.kind || preset.provider_kind || null
169
+ },
170
+ refresh_baseline: preset.refresh_baseline || null,
171
+ derived_from: preset.derived_from || null,
172
+ file_path: filePath,
173
+ review_class: workflowPresetReviewClass(preset)
174
+ };
175
+ }
176
+
177
+ export function loadWorkflowPresetArtifacts(workspaceRoot) {
178
+ if (!workspaceRoot) {
179
+ return { provider_presets: [], team_presets: [], provider_manifests: [] };
180
+ }
181
+ const topogramRoot = path.basename(workspaceRoot) === "topogram" ? workspaceRoot : path.join(workspaceRoot, "topogram");
182
+ const providerDir = path.join(topogramRoot, "candidates", "providers", "workflow-presets");
183
+ const providerManifestDir = path.join(topogramRoot, "candidates", "providers", "manifests");
184
+ const teamDirs = [
185
+ path.join(topogramRoot, "workflow-presets"),
186
+ path.join(topogramRoot, "topogram", "workflow-presets")
187
+ ];
188
+
189
+ const providerPresets = readJsonArtifactsFromDir(providerDir)
190
+ .map(({ parsed, filePath }) => normalizeWorkflowPresetArtifact(parsed, {
191
+ kind: "provider_workflow_preset",
192
+ filePath,
193
+ provenance: {
194
+ source_path: filePath,
195
+ provider_id: parsed?.provider?.id || parsed?.provider_id || null
196
+ }
197
+ }))
198
+ .filter(Boolean);
199
+
200
+ const teamPresets = teamDirs.flatMap((teamDir) => readJsonArtifactsFromDir(teamDir))
201
+ .map(({ parsed, filePath }) => normalizeWorkflowPresetArtifact(parsed, {
202
+ kind: "team_workflow_preset",
203
+ filePath,
204
+ provenance: {
205
+ source_path: filePath,
206
+ provider_id: parsed?.provider?.id || parsed?.provider_id || null
207
+ }
208
+ }))
209
+ .filter(Boolean);
210
+
211
+ const providerManifests = readJsonArtifactsFromDir(providerManifestDir)
212
+ .map(({ parsed, filePath }) => normalizeProviderWorkflowPresetManifest(parsed, { filePath }))
213
+ .filter(Boolean);
214
+
215
+ return {
216
+ provider_presets: providerPresets,
217
+ team_presets: teamPresets,
218
+ provider_manifests: providerManifests
219
+ };
220
+ }
221
+
222
+ export function activeProviderPreset(preset) {
223
+ return preset.active !== false && ["accept", "accepted"].includes(preset.adoption_state);
224
+ }
225
+
226
+ export function activeTeamPreset(preset) {
227
+ if (preset.active === false) return false;
228
+ return !["reject", "rejected", "stage", "staged"].includes(preset.adoption_state);
229
+ }
230
+
231
+ export function presetAppliesToContext(preset, selectors = {}) {
232
+ const applies = preset.applies_to || {};
233
+ const selectedOutputs = stableSortedStrings(selectors.outputs || []);
234
+ const presetId = selectors.preset_id || null;
235
+ const providerId = selectors.provider_id || null;
236
+ const providerKinds = stableSortedStrings(selectors.provider_kinds || []);
237
+ const taskClass = selectors.task_class || selectors.mode || null;
238
+ const queryFamily = selectors.query_family || null;
239
+ const integrationCategories = stableSortedStrings(selectors.integration_categories || []);
240
+
241
+ if (presetId && preset.id !== presetId) {
242
+ return false;
243
+ }
244
+ if (providerId && (preset.provider?.id || preset.provenance?.provider_id) && (preset.provider?.id || preset.provenance?.provider_id) !== providerId) {
245
+ return false;
246
+ }
247
+ if (applies.task_classes.length > 0 && (!taskClass || !applies.task_classes.includes(taskClass))) {
248
+ return false;
249
+ }
250
+ if (applies.provider_ids.length > 0 && (!providerId || !applies.provider_ids.includes(providerId))) {
251
+ return false;
252
+ }
253
+ if (applies.provider_kinds.length > 0 && !applies.provider_kinds.some((kind) => providerKinds.includes(kind))) {
254
+ return false;
255
+ }
256
+ if (applies.outputs.length > 0 && !applies.outputs.some((outputId) => selectedOutputs.includes(outputId))) {
257
+ return false;
258
+ }
259
+ if (applies.integration_categories.length > 0 && !applies.integration_categories.some((category) => integrationCategories.includes(category))) {
260
+ return false;
261
+ }
262
+ if (applies.query_families.length > 0 && (!queryFamily || !applies.query_families.includes(queryFamily))) {
263
+ return false;
264
+ }
265
+ const activation = preset.activation || normalizePresetActivation();
266
+ if (activation.task_classes.length > 0 && (!taskClass || !activation.task_classes.includes(taskClass))) {
267
+ return false;
268
+ }
269
+ if (activation.provider_ids.length > 0 && (!providerId || !activation.provider_ids.includes(providerId))) {
270
+ return false;
271
+ }
272
+ if (activation.provider_kinds.length > 0 && !activation.provider_kinds.some((kind) => providerKinds.includes(kind))) {
273
+ return false;
274
+ }
275
+ if (activation.outputs.length > 0 && !activation.outputs.some((outputId) => selectedOutputs.includes(outputId))) {
276
+ return false;
277
+ }
278
+ if (activation.integration_categories.length > 0 && !activation.integration_categories.some((category) => integrationCategories.includes(category))) {
279
+ return false;
280
+ }
281
+ if (activation.query_families.length > 0 && (!queryFamily || !activation.query_families.includes(queryFamily))) {
282
+ return false;
283
+ }
284
+ if (activation.manual_only && selectors.manual_context !== true) {
285
+ return false;
286
+ }
287
+ return true;
288
+ }
289
+
290
+ export function summarizeWorkflowPreset(preset, selectors = {}) {
291
+ const escalateCategories = preset.review_policy?.escalate_categories || [];
292
+ const verificationImpact = stableOrderedUnion([
293
+ ...(preset.verification_policy?.required || []),
294
+ ...(preset.verification_policy?.recommended || [])
295
+ ]);
296
+ return {
297
+ id: preset.id,
298
+ label: preset.label,
299
+ kind: preset.kind,
300
+ provider_id: preset.provider?.id || preset.provenance?.provider_id || null,
301
+ provider_kind: preset.provider?.kind || null,
302
+ adoption_state: preset.adoption_state,
303
+ active: preset.active !== false,
304
+ activation: preset.activation || normalizePresetActivation(),
305
+ priority: preset.priority ?? preset.source_priority,
306
+ applies_to: preset.applies_to,
307
+ recommended_task_mode: preset.recommended_task_mode || null,
308
+ review_class: preset.review_class,
309
+ review_escalation_categories: escalateCategories,
310
+ verification_impact: verificationImpact,
311
+ multi_agent_hint_impact: {
312
+ allowed: preset.multi_agent_policy?.allowed,
313
+ default_strategy: preset.multi_agent_policy?.default_strategy || null
314
+ },
315
+ indirect_maintained_impact: Boolean(
316
+ escalateCategories.includes("maintained_boundary") ||
317
+ (preset.applies_to?.outputs || []).some((outputId) => String(outputId).startsWith("maintained"))
318
+ ),
319
+ active_for_context: presetAppliesToContext(preset, selectors),
320
+ provenance: preset.provenance || null
321
+ };
322
+ }
323
+
324
+ export function workflowPresetComparableShape(preset) {
325
+ return {
326
+ recommended_task_mode: preset?.recommended_task_mode || null,
327
+ preferred_queries: stableOrderedUnion(preset?.preferred_queries || []),
328
+ artifact_load_order: stableOrderedUnion(preset?.artifact_load_order || []),
329
+ review_policy: {
330
+ block_on: stableSortedStrings(preset?.review_policy?.block_on || []),
331
+ escalate_categories: stableSortedStrings(preset?.review_policy?.escalate_categories || [])
332
+ },
333
+ verification_policy: {
334
+ required: stableOrderedUnion(preset?.verification_policy?.required || []),
335
+ recommended: stableOrderedUnion(preset?.verification_policy?.recommended || []),
336
+ require_output_specific_checks: Boolean(preset?.verification_policy?.require_output_specific_checks),
337
+ require_maintained_checks_when_seams_affected: Boolean(preset?.verification_policy?.require_maintained_checks_when_seams_affected)
338
+ },
339
+ multi_agent_policy: {
340
+ allowed: preset?.multi_agent_policy?.allowed,
341
+ default_strategy: preset?.multi_agent_policy?.default_strategy || null
342
+ },
343
+ handoff_defaults: {
344
+ required_fields: stableOrderedUnion(preset?.handoff_defaults?.required_fields || [])
345
+ },
346
+ tool_hints: preset?.tool_hints || {},
347
+ applies_to: normalizePresetAppliesTo(preset?.applies_to || {}),
348
+ review_class: preset?.review_class || workflowPresetReviewClass(preset || {})
349
+ };
350
+ }
351
+
352
+ export function workflowPresetFingerprint(preset) {
353
+ return JSON.stringify(workflowPresetComparableShape(preset));
354
+ }
355
+
356
+ export function workflowPresetDerivedSource(derivedFrom = null) {
357
+ if (!derivedFrom || typeof derivedFrom !== "object") return null;
358
+ return {
359
+ provider_id: derivedFrom.provider_id || null,
360
+ provider_preset_id: derivedFrom.provider_preset_id || derivedFrom.preset_id || derivedFrom.id || null,
361
+ source_path: derivedFrom.source_path || null,
362
+ source_fingerprint: derivedFrom.source_fingerprint || null
363
+ };
364
+ }
365
+
366
+ export function providerPresetMatchKey(preset) {
367
+ return `${preset?.provider?.id || preset?.provenance?.provider_id || "unknown"}::${preset?.id || "unknown"}`;
368
+ }
369
+
370
+ export function findDerivedTeamPreset(teamPresets = [], providerPreset) {
371
+ const matchKey = providerPresetMatchKey(providerPreset);
372
+ return teamPresets.find((preset) => {
373
+ const derived = workflowPresetDerivedSource(preset?.derived_from);
374
+ if (!derived) return false;
375
+ const derivedKey = `${derived.provider_id || "unknown"}::${derived.provider_preset_id || "unknown"}`;
376
+ return derivedKey === matchKey;
377
+ }) || null;
378
+ }
379
+
380
+ export function diffArrayValues(current = [], previous = []) {
381
+ const normalizedCurrent = stableOrderedUnion(current);
382
+ const normalizedPrevious = stableOrderedUnion(previous);
383
+ return {
384
+ current: normalizedCurrent,
385
+ previous: normalizedPrevious,
386
+ added: normalizedCurrent.filter((entry) => !normalizedPrevious.includes(entry)),
387
+ removed: normalizedPrevious.filter((entry) => !normalizedCurrent.includes(entry))
388
+ };
389
+ }
390
+
391
+ export function diffObjectKeys(current = {}, previous = {}) {
392
+ const keys = stableSortedStrings([...Object.keys(current || {}), ...Object.keys(previous || {})]);
393
+ const changed = {};
394
+ for (const key of keys) {
395
+ const currentValue = current?.[key];
396
+ const previousValue = previous?.[key];
397
+ if (JSON.stringify(currentValue) !== JSON.stringify(previousValue)) {
398
+ changed[key] = {
399
+ current: currentValue,
400
+ previous: previousValue
401
+ };
402
+ }
403
+ }
404
+ return changed;
405
+ }
406
+
407
+ export function workflowPresetChangedFields(currentPreset, previousPreset) {
408
+ const current = workflowPresetComparableShape(currentPreset);
409
+ const previous = workflowPresetComparableShape(previousPreset);
410
+ const changedFields = [];
411
+ for (const field of [
412
+ "recommended_task_mode",
413
+ "preferred_queries",
414
+ "artifact_load_order",
415
+ "review_policy",
416
+ "verification_policy",
417
+ "multi_agent_policy",
418
+ "handoff_defaults",
419
+ "tool_hints",
420
+ "applies_to",
421
+ "review_class"
422
+ ]) {
423
+ if (JSON.stringify(current[field]) !== JSON.stringify(previous[field])) {
424
+ changedFields.push(field);
425
+ }
426
+ }
427
+ return changedFields;
428
+ }
429
+
430
+ export function workflowPresetDeltaPayload(currentPreset, previousPreset) {
431
+ const current = workflowPresetComparableShape(currentPreset);
432
+ const previous = workflowPresetComparableShape(previousPreset);
433
+ return {
434
+ changed_fields: workflowPresetChangedFields(currentPreset, previousPreset),
435
+ review_class_delta: {
436
+ current: current.review_class,
437
+ previous: previous.review_class
438
+ },
439
+ recommended_task_mode_delta: {
440
+ current: current.recommended_task_mode,
441
+ previous: previous.recommended_task_mode
442
+ },
443
+ preferred_queries_delta: diffArrayValues(current.preferred_queries, previous.preferred_queries),
444
+ review_policy_delta: {
445
+ block_on: diffArrayValues(current.review_policy.block_on, previous.review_policy.block_on),
446
+ escalate_categories: diffArrayValues(current.review_policy.escalate_categories, previous.review_policy.escalate_categories)
447
+ },
448
+ verification_policy_delta: {
449
+ required: diffArrayValues(current.verification_policy.required, previous.verification_policy.required),
450
+ recommended: diffArrayValues(current.verification_policy.recommended, previous.verification_policy.recommended),
451
+ flags: diffObjectKeys({
452
+ require_output_specific_checks: current.verification_policy.require_output_specific_checks,
453
+ require_maintained_checks_when_seams_affected: current.verification_policy.require_maintained_checks_when_seams_affected
454
+ }, {
455
+ require_output_specific_checks: previous.verification_policy.require_output_specific_checks,
456
+ require_maintained_checks_when_seams_affected: previous.verification_policy.require_maintained_checks_when_seams_affected
457
+ })
458
+ },
459
+ multi_agent_policy_delta: diffObjectKeys(current.multi_agent_policy, previous.multi_agent_policy),
460
+ tool_hints_delta: diffObjectKeys(current.tool_hints, previous.tool_hints)
461
+ };
462
+ }
463
+
464
+ export function refreshBaselineForProviderPreset(preset) {
465
+ const baseline = preset?.refresh_baseline;
466
+ if (!baseline || typeof baseline !== "object") return null;
467
+ return normalizeWorkflowPresetArtifact({
468
+ ...baseline,
469
+ kind: "provider_workflow_preset",
470
+ provider: {
471
+ ...(preset?.provider || {}),
472
+ ...(baseline.provider || {})
473
+ }
474
+ }, {
475
+ kind: "provider_workflow_preset",
476
+ provenance: {
477
+ source_path: baseline.source_path || null,
478
+ provider_id: baseline?.provider?.id || preset?.provider?.id || null
479
+ }
480
+ });
481
+ }
482
+
483
+ export function workflowPresetRequiresFreshReview(changeStatus, currentPreset, delta) {
484
+ if (changeStatus === "orphaned_customization") return true;
485
+ if (changeStatus === "locally_customized") return true;
486
+ if (changeStatus !== "changed") return false;
487
+ return Boolean(
488
+ (delta.changed_fields || []).some((field) => [
489
+ "review_policy",
490
+ "verification_policy",
491
+ "multi_agent_policy",
492
+ "recommended_task_mode",
493
+ "review_class"
494
+ ].includes(field))
495
+ );
496
+ }
497
+
498
+ export function defaultLocalWorkflowPresetPath(providerId, presetId) {
499
+ return `workflow-presets/provider.${providerId}.${presetId}.json`;
500
+ }
501
+
502
+ export function customizationStatusForPreset(currentPreset, derivedTeamPreset = null) {
503
+ const providerId = currentPreset?.provider?.id || currentPreset?.provenance?.provider_id || null;
504
+ const recommendedLocalPath = defaultLocalWorkflowPresetPath(providerId || "provider", currentPreset?.id || "preset");
505
+ const sourceFingerprint = currentPreset ? workflowPresetFingerprint(currentPreset) : null;
506
+ const derivedSource = workflowPresetDerivedSource(derivedTeamPreset?.derived_from);
507
+ const fingerprintMatches = Boolean(
508
+ derivedTeamPreset &&
509
+ derivedSource?.source_fingerprint &&
510
+ sourceFingerprint &&
511
+ derivedSource.source_fingerprint === sourceFingerprint
512
+ );
513
+ let status = null;
514
+ if (currentPreset?.adoption_state === "customize") {
515
+ status = !derivedTeamPreset
516
+ ? "ready_to_customize"
517
+ : fingerprintMatches
518
+ ? "customization_present"
519
+ : "customization_stale";
520
+ } else if (derivedTeamPreset) {
521
+ status = fingerprintMatches ? "customization_present" : "customization_stale";
522
+ }
523
+ return {
524
+ status,
525
+ derived_preset_exists: Boolean(derivedTeamPreset),
526
+ fingerprint_matches_current_source: fingerprintMatches,
527
+ recommended_local_path: recommendedLocalPath,
528
+ source_fingerprint: sourceFingerprint
529
+ };
530
+ }
531
+
532
+ export function recommendedCustomizationAction(currentPreset, customizationStatus) {
533
+ if (!customizationStatus) return null;
534
+ if (customizationStatus.status === "customization_orphaned") {
535
+ return "remove_or_replace_orphaned_customization";
536
+ }
537
+ if (currentPreset?.adoption_state === "customize" && customizationStatus.status === "ready_to_customize") {
538
+ return "create_local_customization";
539
+ }
540
+ if (customizationStatus.status === "customization_stale") {
541
+ return "refresh_local_customization";
542
+ }
543
+ if (customizationStatus.status === "customization_present") {
544
+ return "review_existing_customization";
545
+ }
546
+ return null;
547
+ }
548
+
549
+ export function providerManifestWorkflowPresetDeclarations(providerManifests = [], providerPresets = []) {
550
+ return providerManifests.flatMap((manifest) =>
551
+ (manifest.exports?.workflow_presets || []).map((entry) => {
552
+ const importedPreset = providerPresets.find((preset) =>
553
+ (preset.provider?.id || preset.provenance?.provider_id) === manifest.provider.id &&
554
+ preset.id === entry.id
555
+ ) || null;
556
+ return {
557
+ provider_id: manifest.provider.id,
558
+ provider_kind: manifest.provider.kind || null,
559
+ manifest_path: manifest.file_path || null,
560
+ workflow_core_version: manifest.workflow_core_version || null,
561
+ review_default: manifest.review_defaults?.workflow_presets || "review_required",
562
+ declaration_id: entry.id,
563
+ label: entry.label || entry.id,
564
+ path: entry.path || null,
565
+ applies_to: entry.applies_to,
566
+ proof_ref: entry.proof_ref || null,
567
+ valid: entry.valid,
568
+ validation: entry.validation,
569
+ imported: Boolean(importedPreset),
570
+ imported_preset_id: importedPreset?.id || null,
571
+ import_status: importedPreset ? "imported" : "not_imported"
572
+ };
573
+ })
574
+ ).sort((a, b) => `${a.provider_id}:${a.declaration_id}`.localeCompare(`${b.provider_id}:${b.declaration_id}`));
575
+ }
576
+
577
+ export function skippedPresetReason(preset, selectors = {}) {
578
+ if (preset.active === false) return "inactive";
579
+ const applies = preset.applies_to || {};
580
+ const activation = preset.activation || normalizePresetActivation();
581
+ const selectedOutputs = stableSortedStrings(selectors.outputs || []);
582
+ const providerId = selectors.provider_id || null;
583
+ const providerKinds = stableSortedStrings(selectors.provider_kinds || []);
584
+ const taskClass = selectors.task_class || selectors.mode || null;
585
+ const queryFamily = selectors.query_family || null;
586
+ const integrationCategories = stableSortedStrings(selectors.integration_categories || []);
587
+ if (applies.task_classes.length > 0 && (!taskClass || !applies.task_classes.includes(taskClass))) return "applies_to_task_class_mismatch";
588
+ if (applies.provider_ids.length > 0 && (!providerId || !applies.provider_ids.includes(providerId))) return "applies_to_provider_mismatch";
589
+ if (applies.provider_kinds.length > 0 && !applies.provider_kinds.some((kind) => providerKinds.includes(kind))) return "applies_to_provider_kind_mismatch";
590
+ if (applies.outputs.length > 0 && !applies.outputs.some((outputId) => selectedOutputs.includes(outputId))) return "applies_to_output_mismatch";
591
+ if (applies.integration_categories.length > 0 && !applies.integration_categories.some((category) => integrationCategories.includes(category))) return "applies_to_integration_category_mismatch";
592
+ if (applies.query_families.length > 0 && (!queryFamily || !applies.query_families.includes(queryFamily))) return "applies_to_query_family_mismatch";
593
+ if (activation.task_classes.length > 0 && (!taskClass || !activation.task_classes.includes(taskClass))) return "activation_task_class_mismatch";
594
+ if (activation.provider_ids.length > 0 && (!providerId || !activation.provider_ids.includes(providerId))) return "activation_provider_mismatch";
595
+ if (activation.provider_kinds.length > 0 && !activation.provider_kinds.some((kind) => providerKinds.includes(kind))) return "activation_provider_kind_mismatch";
596
+ if (activation.outputs.length > 0 && !activation.outputs.some((outputId) => selectedOutputs.includes(outputId))) return "activation_output_mismatch";
597
+ if (activation.integration_categories.length > 0 && !activation.integration_categories.some((category) => integrationCategories.includes(category))) return "activation_integration_category_mismatch";
598
+ if (activation.query_families.length > 0 && (!queryFamily || !activation.query_families.includes(queryFamily))) return "activation_query_family_mismatch";
599
+ if (activation.manual_only && selectors.manual_context !== true) return "manual_only";
600
+ return "not_applicable";
601
+ }
602
+
603
+ export function customizationTemplateFromProviderPreset(currentPreset, workspaceRoot = null) {
604
+ const providerId = currentPreset?.provider?.id || currentPreset?.provenance?.provider_id || "provider";
605
+ const presetId = currentPreset?.id || "preset";
606
+ const resolvedWorkspaceRoot = workspaceRoot ? path.resolve(workspaceRoot) : null;
607
+ const sourcePath = currentPreset?.file_path
608
+ ? resolvedWorkspaceRoot
609
+ ? path.relative(resolvedWorkspaceRoot, currentPreset.file_path)
610
+ : currentPreset.file_path
611
+ : currentPreset?.provenance?.source_path || null;
612
+ const template = {
613
+ id: `provider.${providerId}.${presetId}`,
614
+ label: `Customized ${currentPreset?.label || presetId}`,
615
+ kind: "team_workflow_preset",
616
+ adoption_state: "accept",
617
+ derived_from: {
618
+ provider_id: providerId,
619
+ provider_preset_id: presetId,
620
+ source_path: sourcePath,
621
+ source_fingerprint: workflowPresetFingerprint(currentPreset)
622
+ },
623
+ applies_to: normalizePresetAppliesTo(currentPreset?.applies_to || {}),
624
+ recommended_task_mode: currentPreset?.recommended_task_mode || null,
625
+ preferred_queries: stableOrderedUnion(currentPreset?.preferred_queries || []),
626
+ artifact_load_order: stableOrderedUnion(currentPreset?.artifact_load_order || []),
627
+ review_policy: {
628
+ block_on: stableSortedStrings(currentPreset?.review_policy?.block_on || []),
629
+ escalate_categories: stableSortedStrings(currentPreset?.review_policy?.escalate_categories || [])
630
+ },
631
+ verification_policy: {
632
+ required: stableOrderedUnion(currentPreset?.verification_policy?.required || []),
633
+ recommended: stableOrderedUnion(currentPreset?.verification_policy?.recommended || []),
634
+ require_output_specific_checks: Boolean(currentPreset?.verification_policy?.require_output_specific_checks),
635
+ require_maintained_checks_when_seams_affected: Boolean(currentPreset?.verification_policy?.require_maintained_checks_when_seams_affected)
636
+ },
637
+ multi_agent_policy: {
638
+ allowed: currentPreset?.multi_agent_policy?.allowed,
639
+ default_strategy: currentPreset?.multi_agent_policy?.default_strategy || null
640
+ },
641
+ handoff_defaults: {
642
+ required_fields: stableOrderedUnion(currentPreset?.handoff_defaults?.required_fields || [])
643
+ },
644
+ tool_hints: currentPreset?.tool_hints || {}
645
+ };
646
+ return template;
647
+ }
648
+
649
+ export function buildImportPlanNextAction(defaultNextAction, workflowPresetState = null) {
650
+ const missingDeclaredCount = workflowPresetState?.provider_manifest_summary?.missing_declared_workflow_preset_count || 0;
651
+ if (missingDeclaredCount > 0) {
652
+ return {
653
+ kind: "import_declared_workflow_preset",
654
+ label: "Import declared workflow presets",
655
+ reason: `${missingDeclaredCount} manifest-declared workflow preset(s) are available but not yet imported into candidate space.`
656
+ };
657
+ }
658
+ const surfaces = workflowPresetState?.workflow_preset_surfaces || [];
659
+ const refreshSurface = surfaces.find((surface) => surface.recommended_customization_action === "refresh_local_customization");
660
+ if (refreshSurface) {
661
+ return {
662
+ kind: "refresh_workflow_preset_customization",
663
+ label: "Refresh workflow preset customization",
664
+ reason: `Provider workflow preset ${refreshSurface.id} has a stale local customization that should be refreshed before adoption proceeds.`
665
+ };
666
+ }
667
+ const createSurface = surfaces.find((surface) => surface.recommended_customization_action === "create_local_customization");
668
+ if (createSurface) {
669
+ return {
670
+ kind: "customize_workflow_preset",
671
+ label: "Create local workflow preset customization",
672
+ reason: `Provider workflow preset ${createSurface.id} is marked customize and needs a derived local team preset.`
673
+ };
674
+ }
675
+ return defaultNextAction || null;
676
+ }