@topogram/cli 0.3.63 → 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 (344) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan/index.js +703 -0
  3. package/src/adoption/plan.d.ts +6 -0
  4. package/src/adoption/plan.js +12 -703
  5. package/src/adoption/reporting.d.ts +10 -0
  6. package/src/adoption/review-groups.d.ts +6 -0
  7. package/src/agent-brief.d.ts +3 -0
  8. package/src/agent-brief.js +495 -0
  9. package/src/agent-ops/query-builders/auth.js +375 -0
  10. package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
  11. package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
  12. package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
  13. package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
  14. package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
  15. package/src/agent-ops/query-builders/change-risk.js +25 -0
  16. package/src/agent-ops/query-builders/common.js +149 -0
  17. package/src/agent-ops/query-builders/maintained-risk.js +539 -0
  18. package/src/agent-ops/query-builders/maintained-shared.js +120 -0
  19. package/src/agent-ops/query-builders/multi-agent.js +547 -0
  20. package/src/agent-ops/query-builders/projection-impacts.js +514 -0
  21. package/src/agent-ops/query-builders/work-packets.js +417 -0
  22. package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
  23. package/src/agent-ops/query-builders/workflow-context.js +398 -0
  24. package/src/agent-ops/query-builders/workflow-presets-core.js +676 -0
  25. package/src/agent-ops/query-builders/workflow-presets.js +341 -0
  26. package/src/agent-ops/query-builders.d.ts +26 -0
  27. package/src/agent-ops/query-builders.js +42 -5021
  28. package/src/archive/archive.d.ts +2 -0
  29. package/src/archive/compact.d.ts +1 -0
  30. package/src/archive/unarchive.d.ts +1 -0
  31. package/src/catalog/constants.js +10 -0
  32. package/src/catalog/copy.js +60 -0
  33. package/src/catalog/diagnostics.js +15 -0
  34. package/src/catalog/entries.js +42 -0
  35. package/src/catalog/files.js +67 -0
  36. package/src/catalog/provenance.js +122 -0
  37. package/src/catalog/source.js +150 -0
  38. package/src/catalog/validation.js +252 -0
  39. package/src/catalog.d.ts +12 -0
  40. package/src/catalog.js +18 -750
  41. package/src/cli/catalog-alias.d.ts +1 -0
  42. package/src/cli/command-parser.js +38 -0
  43. package/src/cli/command-parsers/core.js +102 -0
  44. package/src/cli/command-parsers/generator.js +39 -0
  45. package/src/cli/command-parsers/import.js +44 -0
  46. package/src/cli/command-parsers/legacy-workflow.js +21 -0
  47. package/src/cli/command-parsers/project.js +47 -0
  48. package/src/cli/command-parsers/sdlc.js +47 -0
  49. package/src/cli/command-parsers/shared.js +51 -0
  50. package/src/cli/command-parsers/template.js +48 -0
  51. package/src/cli/commands/agent.js +47 -0
  52. package/src/cli/commands/catalog/check.js +31 -0
  53. package/src/cli/commands/catalog/copy.js +59 -0
  54. package/src/cli/commands/catalog/doctor.js +248 -0
  55. package/src/cli/commands/catalog/help.js +21 -0
  56. package/src/cli/commands/catalog/list.js +52 -0
  57. package/src/cli/commands/catalog/runner.js +92 -0
  58. package/src/cli/commands/catalog/shared.js +17 -0
  59. package/src/cli/commands/catalog/show.js +134 -0
  60. package/src/cli/commands/catalog.js +32 -0
  61. package/src/cli/commands/check.js +268 -0
  62. package/src/cli/commands/doctor.js +268 -0
  63. package/src/cli/commands/emit.js +149 -0
  64. package/src/cli/commands/generate.js +96 -0
  65. package/src/cli/commands/generator-policy/package-info.js +162 -0
  66. package/src/cli/commands/generator-policy/payloads.js +372 -0
  67. package/src/cli/commands/generator-policy/printers.js +159 -0
  68. package/src/cli/commands/generator-policy/runner.js +81 -0
  69. package/src/cli/commands/generator-policy/shared.js +39 -0
  70. package/src/cli/commands/generator-policy.js +17 -0
  71. package/src/cli/commands/generator.js +443 -0
  72. package/src/cli/commands/import/adopt.js +170 -0
  73. package/src/cli/commands/import/check.js +91 -0
  74. package/src/cli/commands/import/diff.js +84 -0
  75. package/src/cli/commands/import/help.js +47 -0
  76. package/src/cli/commands/import/paths.js +277 -0
  77. package/src/cli/commands/import/plan.js +284 -0
  78. package/src/cli/commands/import/refresh.js +470 -0
  79. package/src/cli/commands/import/status-history.js +196 -0
  80. package/src/cli/commands/import/workspace.js +230 -0
  81. package/src/cli/commands/import-runner.js +157 -0
  82. package/src/cli/commands/import.js +35 -0
  83. package/src/cli/commands/inspect.js +55 -0
  84. package/src/cli/commands/new.js +94 -0
  85. package/src/cli/commands/package/constants.js +17 -0
  86. package/src/cli/commands/package/doctor.js +240 -0
  87. package/src/cli/commands/package/help.js +27 -0
  88. package/src/cli/commands/package/lockfile.js +135 -0
  89. package/src/cli/commands/package/npm.js +97 -0
  90. package/src/cli/commands/package/reporting.js +35 -0
  91. package/src/cli/commands/package/runner.js +33 -0
  92. package/src/cli/commands/package/shared.js +9 -0
  93. package/src/cli/commands/package/update-cli.js +252 -0
  94. package/src/cli/commands/package/versions.js +35 -0
  95. package/src/cli/commands/package.js +31 -0
  96. package/src/cli/commands/query/change-plan.js +68 -0
  97. package/src/cli/commands/query/definitions.js +202 -0
  98. package/src/cli/commands/query/import-adopt.js +121 -0
  99. package/src/cli/commands/query/runner/artifacts.js +102 -0
  100. package/src/cli/commands/query/runner/boundaries.js +211 -0
  101. package/src/cli/commands/query/runner/change.js +182 -0
  102. package/src/cli/commands/query/runner/import-adopt.js +111 -0
  103. package/src/cli/commands/query/runner/index.js +31 -0
  104. package/src/cli/commands/query/runner/output.js +12 -0
  105. package/src/cli/commands/query/runner/workflow.js +241 -0
  106. package/src/cli/commands/query/runner.js +3 -0
  107. package/src/cli/commands/query/workflow-context.js +5 -0
  108. package/src/cli/commands/query/workspace.js +274 -0
  109. package/src/cli/commands/query.js +11 -0
  110. package/src/cli/commands/release-rollout.js +257 -0
  111. package/src/cli/commands/release-shared.js +528 -0
  112. package/src/cli/commands/release-status.js +429 -0
  113. package/src/cli/commands/release.js +107 -0
  114. package/src/cli/commands/sdlc.js +168 -0
  115. package/src/cli/commands/setup.js +76 -0
  116. package/src/cli/commands/source.js +291 -0
  117. package/src/cli/commands/template/baseline.js +100 -0
  118. package/src/cli/commands/template/check.js +466 -0
  119. package/src/cli/commands/template/constants.js +8 -0
  120. package/src/cli/commands/template/diagnostics.js +26 -0
  121. package/src/cli/commands/template/help.js +28 -0
  122. package/src/cli/commands/template/lifecycle.js +404 -0
  123. package/src/cli/commands/template/list-show.js +287 -0
  124. package/src/cli/commands/template/policy.js +422 -0
  125. package/src/cli/commands/template/shared.js +127 -0
  126. package/src/cli/commands/template/updates.js +352 -0
  127. package/src/cli/commands/template-runner.js +198 -0
  128. package/src/cli/commands/template.js +43 -0
  129. package/src/cli/commands/trust.js +219 -0
  130. package/src/cli/commands/version.js +40 -0
  131. package/src/cli/commands/widget.js +168 -0
  132. package/src/cli/commands/workflow.js +63 -0
  133. package/src/cli/dispatcher.js +392 -0
  134. package/src/cli/help-dispatch.js +188 -0
  135. package/src/cli/help.js +296 -0
  136. package/src/cli/migration-guidance.js +59 -0
  137. package/src/cli/options.js +96 -0
  138. package/src/cli/output-safety.js +107 -0
  139. package/src/cli/path-normalization.js +29 -0
  140. package/src/cli.js +47 -11711
  141. package/src/example-implementation.d.ts +2 -0
  142. package/src/format.d.ts +1 -0
  143. package/src/generator/api/contracts.js +497 -0
  144. package/src/generator/api/metadata.js +221 -0
  145. package/src/generator/api/openapi.js +559 -0
  146. package/src/generator/api/schema.js +124 -0
  147. package/src/generator/api/types.d.ts +98 -0
  148. package/src/generator/api.js +3 -1195
  149. package/src/generator/check.d.ts +1 -0
  150. package/src/generator/context/bundle.d.ts +1 -0
  151. package/src/generator/context/shared/domain-sdlc.js +282 -0
  152. package/src/generator/context/shared/maintained-boundary.js +665 -0
  153. package/src/generator/context/shared/metrics.js +85 -0
  154. package/src/generator/context/shared/primitives.js +64 -0
  155. package/src/generator/context/shared/relationships.js +453 -0
  156. package/src/generator/context/shared/summaries.js +263 -0
  157. package/src/generator/context/shared/types.d.ts +207 -0
  158. package/src/generator/context/shared.d.ts +44 -0
  159. package/src/generator/context/shared.js +80 -1390
  160. package/src/generator/context/slice/core.js +397 -0
  161. package/src/generator/context/slice/sdlc.js +417 -0
  162. package/src/generator/context/slice/ui-packets.js +183 -0
  163. package/src/generator/context/slice.js +2 -859
  164. package/src/generator/native/parity-bundle.js +2 -1
  165. package/src/generator/registry/index.js +507 -0
  166. package/src/generator/registry.js +18 -504
  167. package/src/generator/runtime/environment/index.js +666 -0
  168. package/src/generator/runtime/environment.js +4 -666
  169. package/src/generator/runtime/runtime-check/index.js +554 -0
  170. package/src/generator/runtime/runtime-check.js +4 -554
  171. package/src/generator/runtime/shared/index.js +572 -0
  172. package/src/generator/runtime/shared.js +19 -570
  173. package/src/generator/shared.d.ts +2 -0
  174. package/src/generator/surfaces/shared.d.ts +3 -0
  175. package/src/generator/surfaces/web/html-escape.js +22 -0
  176. package/src/generator/surfaces/web/react.js +10 -8
  177. package/src/generator/surfaces/web/sveltekit.js +7 -5
  178. package/src/generator/surfaces/web/vanilla.js +8 -4
  179. package/src/generator/widget-conformance/behavior-report.js +258 -0
  180. package/src/generator/widget-conformance/checks.js +371 -0
  181. package/src/generator/widget-conformance/projection-context.js +200 -0
  182. package/src/generator/widget-conformance/report.js +166 -0
  183. package/src/generator/widget-conformance/types.d.ts +121 -0
  184. package/src/generator/widget-conformance.js +3 -824
  185. package/src/generator.d.ts +2 -0
  186. package/src/github-client.js +520 -0
  187. package/src/import/core/context.d.ts +3 -0
  188. package/src/import/core/contracts.d.ts +1 -0
  189. package/src/import/core/registry.d.ts +4 -0
  190. package/src/import/core/runner/candidates.js +217 -0
  191. package/src/import/core/runner/options.js +22 -0
  192. package/src/import/core/runner/reports.js +50 -0
  193. package/src/import/core/runner/run.js +79 -0
  194. package/src/import/core/runner/tracks.js +150 -0
  195. package/src/import/core/runner/ui-drafts.js +337 -0
  196. package/src/import/core/runner.js +3 -698
  197. package/src/import/core/shared/api-routes.js +221 -0
  198. package/src/import/core/shared/candidates.js +97 -0
  199. package/src/import/core/shared/files.js +177 -0
  200. package/src/import/core/shared/next-app.js +389 -0
  201. package/src/import/core/shared/types.d.ts +51 -0
  202. package/src/import/core/shared/ui-routes.js +230 -0
  203. package/src/import/core/shared.js +67 -910
  204. package/src/import/extractors/api/flutter-dio.js +4 -8
  205. package/src/import/extractors/api/react-native-repository.js +4 -8
  206. package/src/import/index.d.ts +4 -0
  207. package/src/import/provenance.d.ts +4 -0
  208. package/src/new-project/constants.js +128 -0
  209. package/src/new-project/create.js +83 -0
  210. package/src/new-project/json.js +28 -0
  211. package/src/new-project/metadata.js +96 -0
  212. package/src/new-project/package-spec.js +161 -0
  213. package/src/new-project/project-files.js +348 -0
  214. package/src/new-project/template-policy.js +269 -0
  215. package/src/new-project/template-resolution.js +368 -0
  216. package/src/new-project/template-snapshots.js +430 -0
  217. package/src/new-project/template-updates.js +512 -0
  218. package/src/new-project/types.d.ts +83 -0
  219. package/src/new-project.js +6 -2188
  220. package/src/npm-safety.js +79 -0
  221. package/src/parser.d.ts +87 -0
  222. package/src/parser.js +118 -0
  223. package/src/path-helpers.d.ts +1 -0
  224. package/src/path-helpers.js +20 -0
  225. package/src/policy/review-boundaries.d.ts +15 -0
  226. package/src/project-config/index.js +564 -0
  227. package/src/project-config.js +19 -560
  228. package/src/reconcile/docs.d.ts +8 -0
  229. package/src/reconcile/journeys.d.ts +1 -0
  230. package/src/resolver/enrich/acceptance-criterion.js +2 -0
  231. package/src/resolver/enrich/bug.js +2 -0
  232. package/src/resolver/enrich/pitch.js +2 -0
  233. package/src/resolver/enrich/requirement.js +2 -0
  234. package/src/resolver/enrich/task.js +2 -0
  235. package/src/resolver/index.js +19 -2089
  236. package/src/resolver/normalize.js +384 -1
  237. package/src/resolver/plans.js +168 -0
  238. package/src/resolver/projections-api.js +494 -0
  239. package/src/resolver/projections-db.js +133 -0
  240. package/src/resolver/projections-ui.js +317 -0
  241. package/src/resolver/shapes.js +251 -0
  242. package/src/resolver/shared.js +278 -0
  243. package/src/resolver/widgets.js +132 -0
  244. package/src/resolver.d.ts +1 -0
  245. package/src/runtime-support.js +29 -0
  246. package/src/sdlc/adopt.d.ts +1 -0
  247. package/src/sdlc/check.d.ts +1 -0
  248. package/src/sdlc/explain.d.ts +1 -0
  249. package/src/sdlc/release.d.ts +1 -0
  250. package/src/sdlc/scaffold.d.ts +1 -0
  251. package/src/sdlc/transition.d.ts +1 -0
  252. package/src/template-trust/constants.js +62 -0
  253. package/src/template-trust/content.js +258 -0
  254. package/src/template-trust/diff.js +92 -0
  255. package/src/template-trust/policy.js +61 -0
  256. package/src/template-trust/record.js +90 -0
  257. package/src/template-trust/status.js +182 -0
  258. package/src/template-trust.js +24 -687
  259. package/src/text-helpers.d.ts +7 -0
  260. package/src/text-helpers.js +245 -0
  261. package/src/topogram-config.js +306 -0
  262. package/src/topogram-types.d.ts +69 -0
  263. package/src/validator/common.js +488 -0
  264. package/src/validator/data-model.js +237 -0
  265. package/src/validator/docs.js +167 -0
  266. package/src/validator/expressions.js +146 -1
  267. package/src/validator/index.d.ts +23 -0
  268. package/src/validator/index.js +32 -3585
  269. package/src/validator/kinds.d.ts +41 -0
  270. package/src/validator/kinds.js +2 -0
  271. package/src/validator/model-helpers.js +46 -0
  272. package/src/validator/per-kind/acceptance-criterion.js +5 -0
  273. package/src/validator/per-kind/bug.js +6 -0
  274. package/src/validator/per-kind/domain.js +15 -2
  275. package/src/validator/per-kind/pitch.js +7 -0
  276. package/src/validator/per-kind/requirement.js +5 -0
  277. package/src/validator/per-kind/task.js +7 -0
  278. package/src/validator/per-kind/widget.js +14 -0
  279. package/src/validator/projections/api-http-async.js +410 -0
  280. package/src/validator/projections/api-http-authz.js +88 -0
  281. package/src/validator/projections/api-http-core.js +205 -0
  282. package/src/validator/projections/api-http-policies.js +339 -0
  283. package/src/validator/projections/api-http-responses.js +233 -0
  284. package/src/validator/projections/api-http.js +44 -0
  285. package/src/validator/projections/db.js +353 -0
  286. package/src/validator/projections/generator-defaults.js +45 -0
  287. package/src/validator/projections/helpers.js +87 -0
  288. package/src/validator/projections/ui-helpers.js +214 -0
  289. package/src/validator/projections/ui-navigation.js +344 -0
  290. package/src/validator/projections/ui-structure.js +364 -0
  291. package/src/validator/projections/ui-widgets.js +493 -0
  292. package/src/validator/projections/ui.js +46 -0
  293. package/src/validator/registry.js +48 -1
  294. package/src/validator/utils.d.ts +20 -0
  295. package/src/validator/utils.js +115 -12
  296. package/src/validator.d.ts +2 -0
  297. package/src/widget-behavior.d.ts +1 -0
  298. package/src/workflows/adoption/index.js +26 -0
  299. package/src/workflows/docs-generate.js +262 -0
  300. package/src/workflows/docs-scan.js +703 -0
  301. package/src/workflows/docs.js +15 -0
  302. package/src/workflows/import-app/api/collect.js +221 -0
  303. package/src/workflows/import-app/api/openapi.js +257 -0
  304. package/src/workflows/import-app/api/routes.js +327 -0
  305. package/src/workflows/import-app/api/sources.js +22 -0
  306. package/src/workflows/import-app/api.js +4 -0
  307. package/src/workflows/import-app/db.js +538 -0
  308. package/src/workflows/import-app/index.js +30 -0
  309. package/src/workflows/import-app/shared.js +218 -0
  310. package/src/workflows/import-app/ui.js +443 -0
  311. package/src/workflows/import-app/workflow.js +159 -0
  312. package/src/workflows/reconcile/adoption-plan/build.js +208 -0
  313. package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
  314. package/src/workflows/reconcile/adoption-plan/outputs.js +143 -0
  315. package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
  316. package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
  317. package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
  318. package/src/workflows/reconcile/adoption-plan.js +32 -0
  319. package/src/workflows/reconcile/auth/closures.js +115 -0
  320. package/src/workflows/reconcile/auth/formatters.js +142 -0
  321. package/src/workflows/reconcile/auth/inference.js +330 -0
  322. package/src/workflows/reconcile/auth/roles.js +122 -0
  323. package/src/workflows/reconcile/auth.js +37 -0
  324. package/src/workflows/reconcile/bundle-core/index.js +600 -0
  325. package/src/workflows/reconcile/bundle-core.js +14 -0
  326. package/src/workflows/reconcile/bundle-shared.js +75 -0
  327. package/src/workflows/reconcile/candidate-model.js +477 -0
  328. package/src/workflows/reconcile/canonical-surface.js +264 -0
  329. package/src/workflows/reconcile/gap-report.js +333 -0
  330. package/src/workflows/reconcile/ids.js +6 -0
  331. package/src/workflows/reconcile/impacts/adoption-plan.js +192 -0
  332. package/src/workflows/reconcile/impacts/indexes.js +101 -0
  333. package/src/workflows/reconcile/impacts/patches.js +252 -0
  334. package/src/workflows/reconcile/impacts/reports.js +80 -0
  335. package/src/workflows/reconcile/impacts.js +16 -0
  336. package/src/workflows/reconcile/index.js +7 -0
  337. package/src/workflows/reconcile/renderers.js +461 -0
  338. package/src/workflows/reconcile/summary.js +90 -0
  339. package/src/workflows/reconcile/workflow.js +309 -0
  340. package/src/workflows/shared.js +189 -0
  341. package/src/workflows/types.d.ts +93 -0
  342. package/src/workflows.d.ts +1 -0
  343. package/src/workflows.js +10 -7652
  344. package/src/workspace-docs.d.ts +29 -0
@@ -0,0 +1,120 @@
1
+ import {
2
+ severityRank,
3
+ stableSortedStrings
4
+ } from "./common.js";
5
+ export function normalizeProofStorySummary(story) {
6
+ return {
7
+ classification: story?.classification || null,
8
+ relativePath: story?.relativePath || null,
9
+ maintained_files: story?.maintained_files || story?.maintainedFiles || [],
10
+ seam_family_id: story?.seam_family_id || story?.seamFamilyId || null,
11
+ seam_family_label: story?.seam_family_label || story?.seamFamilyLabel || null,
12
+ review_boundary: story?.review_boundary || null
13
+ };
14
+ }
15
+
16
+ export function normalizeSeamSummary(seam, proofStoryMapper = normalizeProofStorySummary) {
17
+ return {
18
+ seam_id: seam?.seam_id || null,
19
+ seam_family_id: seam?.seam_family_id || null,
20
+ seam_family_label: seam?.seam_family_label || null,
21
+ output_id: seam?.output_id || null,
22
+ label: seam?.label || null,
23
+ kind: seam?.kind || null,
24
+ ownership_class: seam?.ownership_class || null,
25
+ status: seam?.status || null,
26
+ maintained_modules: seam?.maintained_modules || [],
27
+ emitted_dependencies: seam?.emitted_dependencies || [],
28
+ allowed_change_classes: seam?.allowed_change_classes || [],
29
+ drift_signals: seam?.drift_signals || [],
30
+ proof_stories: (seam?.proof_stories || []).map(proofStoryMapper)
31
+ };
32
+ }
33
+
34
+ export function maintainedOutputRecordFromFiles(files = []) {
35
+ const normalizedFiles = stableSortedStrings(files);
36
+ if (normalizedFiles.some((entry) => String(entry).startsWith("examples/maintained/proof-app/"))) {
37
+ return {
38
+ output_id: "maintained_app",
39
+ label: "Maintained App",
40
+ kind: "maintained_runtime",
41
+ root_paths: ["examples/maintained/proof-app/**"]
42
+ };
43
+ }
44
+
45
+ return {
46
+ output_id: "maintained_app",
47
+ label: "Maintained App",
48
+ kind: "maintained_runtime",
49
+ root_paths: []
50
+ };
51
+ }
52
+
53
+ export function normalizeMaintainedOutput(output, seamMap, {
54
+ summaryField = "status",
55
+ severitySelector = severityRank,
56
+ verificationTargetsFallback = null
57
+ } = {}) {
58
+ const seams = (output?.seams || [])
59
+ .map((seam) => seamMap.get(seam.seam_id || seam.label))
60
+ .filter(Boolean);
61
+ const files = stableSortedStrings(output?.maintained_files_in_scope || seams.flatMap((seam) => seam.maintained_modules || []));
62
+ const humanOwnedSeams = stableSortedStrings(output?.human_owned_seams || seams.map((seam) => seam.label));
63
+ const proofStories = (output?.proof_stories || [])
64
+ .map(normalizeProofStorySummary);
65
+ const sortedSeams = [...seams].sort((a, b) => {
66
+ const severityCompare = severitySelector(b[summaryField]) - severitySelector(a[summaryField]);
67
+ return severityCompare !== 0 ? severityCompare : String(a.seam_id || "").localeCompare(String(b.seam_id || ""));
68
+ });
69
+ const statuses = stableSortedStrings(sortedSeams.map((seam) => seam[summaryField]).filter(Boolean));
70
+ const statusCounts = Object.fromEntries(statuses.map((status) => [status, sortedSeams.filter((seam) => seam[summaryField] === status).length]));
71
+ const seamFamilies = stableSortedStrings(sortedSeams.map((seam) => seam.seam_family_id).filter(Boolean));
72
+
73
+ return {
74
+ output_id: output?.output_id || null,
75
+ label: output?.label || null,
76
+ kind: output?.kind || null,
77
+ root_paths: stableSortedStrings(output?.root_paths || []),
78
+ ownership_boundary: output?.ownership_boundary || null,
79
+ write_scope: output?.write_scope || null,
80
+ verification_targets: output?.verification_targets || verificationTargetsFallback || null,
81
+ maintained_files_in_scope: files,
82
+ human_owned_seams: humanOwnedSeams,
83
+ seam_families: seamFamilies,
84
+ proof_stories: proofStories,
85
+ seams: sortedSeams,
86
+ summary: {
87
+ affected_seam_count: sortedSeams.length,
88
+ affected_seam_family_count: seamFamilies.length,
89
+ maintained_file_count: files.length,
90
+ highest_severity: sortedSeams[0]?.[summaryField] || "aligned",
91
+ status_counts: statusCounts,
92
+ affected_seam_families: seamFamilies
93
+ }
94
+ };
95
+ }
96
+
97
+ export function buildMaintainedOutputGroups(outputs = [], seams = [], {
98
+ summaryField = "status",
99
+ severitySelector = severityRank,
100
+ verificationTargetsFallback = null
101
+ } = {}) {
102
+ const seamMap = new Map(seams.map((seam) => [seam.seam_id || seam.label, seam]));
103
+ const sourceOutputs = outputs.length > 0
104
+ ? outputs
105
+ : [{
106
+ ...maintainedOutputRecordFromFiles(seams.flatMap((seam) => seam.maintained_modules || [])),
107
+ maintained_files_in_scope: stableSortedStrings(seams.flatMap((seam) => seam.maintained_modules || [])),
108
+ human_owned_seams: stableSortedStrings(seams.map((seam) => seam.label)),
109
+ seams: seams.map((seam) => ({ seam_id: seam.seam_id || null }))
110
+ }];
111
+
112
+ return sourceOutputs
113
+ .map((output) => normalizeMaintainedOutput(output, seamMap, {
114
+ summaryField,
115
+ severitySelector,
116
+ verificationTargetsFallback
117
+ }))
118
+ .filter((output) => output.seams.length > 0 || output.maintained_files_in_scope.length > 0)
119
+ .sort((a, b) => String(a.output_id || "").localeCompare(String(b.output_id || "")));
120
+ }
@@ -0,0 +1,547 @@
1
+ import { stableSortedStrings } from "./common.js";
2
+ import { buildPresetGuidanceSummary } from "./change-risk.js";
3
+ import {
4
+ buildAuthHintsQueryPayload,
5
+ buildAuthReviewPacketPayload,
6
+ normalizeReviewGroupSelector
7
+ } from "./auth.js";
8
+ export function laneRolePriority(role) {
9
+ switch (role) {
10
+ case "bundle_reviewer":
11
+ return 0;
12
+ case "auth_reviewer":
13
+ return 1;
14
+ case "mapping_reviewer":
15
+ return 2;
16
+ case "doc_promoter":
17
+ return 3;
18
+ case "adoption_operator":
19
+ return 4;
20
+ case "verification_runner":
21
+ return 5;
22
+ default:
23
+ return 99;
24
+ }
25
+ }
26
+
27
+ export function canonicalTargetsForProposalSurfaces(proposalSurfaces = []) {
28
+ return stableSortedStrings(
29
+ proposalSurfaces
30
+ .filter((surface) => surface.canonical_rel_path)
31
+ .map((surface) => `topogram/${surface.canonical_rel_path}`)
32
+ );
33
+ }
34
+
35
+ export function reviewSelectorsFromImportPlan(importPlan = null) {
36
+ return stableSortedStrings(
37
+ (importPlan?.review_groups || []).map((id) => normalizeReviewGroupSelector(id)).filter(Boolean)
38
+ );
39
+ }
40
+
41
+ export function projectionIdsFromProposalSurfaces(proposalSurfaces = []) {
42
+ return stableSortedStrings(
43
+ proposalSurfaces.flatMap((surface) => (surface.projection_impacts || []).map((impact) => impact.projection_id)).filter(Boolean)
44
+ );
45
+ }
46
+
47
+ export function bundleProposalSurfaces(bundleSlug, importPlan = null) {
48
+ if (!bundleSlug) {
49
+ return [];
50
+ }
51
+ return (importPlan?.proposal_surfaces || []).filter((surface) => String(surface.id || "").includes(`${bundleSlug}:`) || String(surface.id || "").includes(`:${bundleSlug}`) || String(surface.canonical_rel_path || "").includes(`/${bundleSlug}`));
52
+ }
53
+
54
+ export function buildLane({
55
+ lane_id,
56
+ role,
57
+ bundle = null,
58
+ purpose,
59
+ allowed_inputs,
60
+ write_scope,
61
+ owned_targets,
62
+ proof_targets,
63
+ blocking_dependencies = [],
64
+ completion_condition,
65
+ publishes
66
+ }) {
67
+ return {
68
+ lane_id,
69
+ role,
70
+ bundle,
71
+ purpose,
72
+ allowed_inputs,
73
+ write_scope,
74
+ owned_targets,
75
+ proof_targets,
76
+ blocking_dependencies,
77
+ completion_condition,
78
+ publishes
79
+ };
80
+ }
81
+
82
+ export function buildBundleReviewerLanes(report, adoptionStatus, importPlan, singleAgentPlan) {
83
+ return (report?.candidate_model_bundles || []).map((bundle) => {
84
+ const bundlePriority = (adoptionStatus?.bundle_priorities || []).find((entry) => entry.bundle === bundle.slug) || null;
85
+ const proposalSurfaces = bundleProposalSurfaces(bundle.slug, importPlan);
86
+ const reviewSelectors = stableSortedStrings([
87
+ bundlePriority?.recommend_bundle_review_selector || null,
88
+ ...(bundlePriority?.next_review_groups || []).map((group) => normalizeReviewGroupSelector(group.id)).filter(Boolean)
89
+ ]);
90
+ return buildLane({
91
+ lane_id: `bundle_reviewer.${bundle.slug}`,
92
+ role: "bundle_reviewer",
93
+ bundle: bundle.slug,
94
+ purpose: "Review one candidate bundle and its proposal state before canonical adoption.",
95
+ allowed_inputs: [
96
+ "candidates/reconcile/report.json",
97
+ "candidates/reconcile/adoption-status.json",
98
+ "candidates/reconcile/adoption-plan.agent.json"
99
+ ],
100
+ write_scope: singleAgentPlan.write_scope,
101
+ owned_targets: {
102
+ bundles: [bundle.slug],
103
+ review_groups: reviewSelectors,
104
+ canonical_targets: canonicalTargetsForProposalSurfaces(proposalSurfaces),
105
+ projection_ids: projectionIdsFromProposalSurfaces(proposalSurfaces)
106
+ },
107
+ proof_targets: singleAgentPlan.proof_targets,
108
+ completion_condition: "Bundle review is complete when its review-oriented proposal surfaces are accepted, deferred, or clearly blocked with reason.",
109
+ publishes: [`handoff:bundle-review.${bundle.slug}`]
110
+ });
111
+ });
112
+ }
113
+
114
+ export function buildAuthReviewerLanes(report, adoptionStatus, importPlan, singleAgentPlan) {
115
+ const hintsQuery = buildAuthHintsQueryPayload(report, adoptionStatus);
116
+ const bundles = new Set([
117
+ ...hintsQuery.unresolved_hints.map((hint) => hint.bundle),
118
+ ...hintsQuery.deferred_hints.map((hint) => hint.bundle)
119
+ ]);
120
+ return [...bundles].sort().map((bundleSlug) => {
121
+ const bundlePacket = buildAuthReviewPacketPayload(report, adoptionStatus, bundleSlug);
122
+ const proposalSurfaces = bundleProposalSurfaces(bundleSlug, importPlan);
123
+ return buildLane({
124
+ lane_id: `auth_reviewer.${bundleSlug}`,
125
+ role: "auth_reviewer",
126
+ bundle: bundleSlug,
127
+ purpose: "Review auth-sensitive hints and follow-up for one bundle before canonical promotion.",
128
+ allowed_inputs: [
129
+ "candidates/reconcile/report.json",
130
+ "candidates/reconcile/adoption-status.json"
131
+ ],
132
+ write_scope: singleAgentPlan.write_scope,
133
+ owned_targets: {
134
+ bundles: [bundleSlug],
135
+ review_groups: stableSortedStrings([bundlePacket?.next_review_selector, bundlePacket?.bundle_review_selector].filter(Boolean)),
136
+ canonical_targets: stableSortedStrings([
137
+ ...canonicalTargetsForProposalSurfaces(proposalSurfaces),
138
+ ...(bundlePacket?.projection_patch_actions || []).map((entry) => `patch:${entry.action}`)
139
+ ]),
140
+ projection_ids: projectionIdsFromProposalSurfaces(proposalSurfaces)
141
+ },
142
+ proof_targets: singleAgentPlan.proof_targets,
143
+ completion_condition: "Auth review is complete when unresolved and deferred auth hints are resolved into adopted, deferred, or blocked outcomes with explicit reasoning.",
144
+ publishes: [`handoff:auth-review.${bundleSlug}`]
145
+ });
146
+ });
147
+ }
148
+
149
+ export function buildMappingReviewerLane(importPlan, singleAgentPlan) {
150
+ const mappingSurfaces = (importPlan?.proposal_surfaces || []).filter((surface) =>
151
+ surface.recommended_state === "map" || surface.recommended_state === "customize" || surface.current_state === "map" || surface.current_state === "customize"
152
+ );
153
+ if (mappingSurfaces.length === 0) {
154
+ return [];
155
+ }
156
+ return [buildLane({
157
+ lane_id: "mapping_reviewer",
158
+ role: "mapping_reviewer",
159
+ purpose: "Resolve proposal surfaces that still need mapping or customization decisions before canonical adoption.",
160
+ allowed_inputs: [
161
+ "candidates/reconcile/adoption-plan.agent.json",
162
+ "candidates/reconcile/report.json"
163
+ ],
164
+ write_scope: singleAgentPlan.write_scope,
165
+ owned_targets: {
166
+ bundles: [],
167
+ review_groups: reviewSelectorsFromImportPlan(importPlan),
168
+ canonical_targets: canonicalTargetsForProposalSurfaces(mappingSurfaces),
169
+ projection_ids: projectionIdsFromProposalSurfaces(mappingSurfaces)
170
+ },
171
+ proof_targets: singleAgentPlan.proof_targets,
172
+ blocking_dependencies: [],
173
+ completion_condition: "All mapping-sensitive proposal surfaces are mapped, customized, deferred, or explicitly rejected.",
174
+ publishes: ["handoff:mapping-review"]
175
+ })];
176
+ }
177
+
178
+ export function buildDocPromoterLane(importPlan, singleAgentPlan) {
179
+ const docSurfaces = (importPlan?.proposal_surfaces || []).filter((surface) => String(surface.canonical_rel_path || "").startsWith("docs/"));
180
+ if (docSurfaces.length === 0) {
181
+ return [];
182
+ }
183
+ return [buildLane({
184
+ lane_id: "doc_promoter",
185
+ role: "doc_promoter",
186
+ purpose: "Review and prepare documentation-oriented proposal surfaces for later adoption.",
187
+ allowed_inputs: [
188
+ "candidates/reconcile/adoption-plan.agent.json",
189
+ "candidates/reconcile/report.json"
190
+ ],
191
+ write_scope: singleAgentPlan.write_scope,
192
+ owned_targets: {
193
+ bundles: [],
194
+ review_groups: reviewSelectorsFromImportPlan(importPlan),
195
+ canonical_targets: canonicalTargetsForProposalSurfaces(docSurfaces),
196
+ projection_ids: projectionIdsFromProposalSurfaces(docSurfaces)
197
+ },
198
+ proof_targets: singleAgentPlan.proof_targets,
199
+ blocking_dependencies: [],
200
+ completion_condition: "Documentation proposal surfaces are reviewed and ready for explicit adoption or deferral.",
201
+ publishes: ["handoff:doc-promotion-review"]
202
+ })];
203
+ }
204
+
205
+ export function buildAdoptionOperatorLane(importPlan, singleAgentPlan) {
206
+ const canonicalTargets = canonicalTargetsForProposalSurfaces(importPlan?.proposal_surfaces || []);
207
+ return buildLane({
208
+ lane_id: "adoption_operator",
209
+ role: "adoption_operator",
210
+ purpose: "Own the single-writer canonical adoption step after review lanes finish.",
211
+ allowed_inputs: [
212
+ "candidates/reconcile/adoption-plan.agent.json",
213
+ "candidates/reconcile/report.json",
214
+ "candidates/reconcile/adoption-status.json"
215
+ ],
216
+ write_scope: singleAgentPlan.write_scope,
217
+ owned_targets: {
218
+ bundles: [],
219
+ review_groups: reviewSelectorsFromImportPlan(importPlan),
220
+ canonical_targets: canonicalTargets,
221
+ projection_ids: projectionIdsFromProposalSurfaces(importPlan?.proposal_surfaces || [])
222
+ },
223
+ proof_targets: singleAgentPlan.proof_targets,
224
+ blocking_dependencies: [],
225
+ completion_condition: "Canonical adoption runs exactly once against the merged reviewed state.",
226
+ publishes: ["handoff:canonical-adoption"]
227
+ });
228
+ }
229
+
230
+ export function buildVerificationRunnerLane(singleAgentPlan) {
231
+ return buildLane({
232
+ lane_id: "verification_runner",
233
+ role: "verification_runner",
234
+ purpose: "Run the proof set against merged canonical state after adoption completes.",
235
+ allowed_inputs: [
236
+ "candidates/reconcile/adoption-status.json",
237
+ "candidates/reconcile/report.json"
238
+ ],
239
+ write_scope: singleAgentPlan.write_scope,
240
+ owned_targets: {
241
+ bundles: [],
242
+ review_groups: [],
243
+ canonical_targets: [],
244
+ projection_ids: []
245
+ },
246
+ proof_targets: singleAgentPlan.proof_targets,
247
+ blocking_dependencies: ["gate.canonical_adoption"],
248
+ completion_condition: "The smallest attached proof set has been run after canonical adoption completes.",
249
+ publishes: ["handoff:verification"]
250
+ });
251
+ }
252
+
253
+ export function targetsOverlap(a = [], b = []) {
254
+ const set = new Set(a || []);
255
+ return (b || []).some((entry) => set.has(entry));
256
+ }
257
+
258
+ export function laneOverlapSummary(laneA, laneB) {
259
+ const reasons = [];
260
+ if (targetsOverlap(laneA.owned_targets?.canonical_targets, laneB.owned_targets?.canonical_targets)) {
261
+ reasons.push("shared_canonical_targets");
262
+ }
263
+ if (targetsOverlap(laneA.owned_targets?.review_groups, laneB.owned_targets?.review_groups)) {
264
+ reasons.push("shared_review_groups");
265
+ }
266
+ if (targetsOverlap(laneA.owned_targets?.projection_ids, laneB.owned_targets?.projection_ids)) {
267
+ reasons.push("shared_projection_ids");
268
+ }
269
+ if (targetsOverlap(laneA.owned_targets?.bundles, laneB.owned_targets?.bundles) &&
270
+ ((laneA.owned_targets?.canonical_targets || []).length > 0 || (laneB.owned_targets?.canonical_targets || []).length > 0)) {
271
+ reasons.push("shared_bundle_scope");
272
+ }
273
+ return reasons;
274
+ }
275
+
276
+ export function buildOverlapRules(lanes = []) {
277
+ const rules = [];
278
+ for (let index = 0; index < lanes.length; index += 1) {
279
+ for (let inner = index + 1; inner < lanes.length; inner += 1) {
280
+ const left = lanes[index];
281
+ const right = lanes[inner];
282
+ const overlapReasons = laneOverlapSummary(left, right);
283
+ if (overlapReasons.length > 0) {
284
+ rules.push({
285
+ rule_id: `${left.lane_id}__${right.lane_id}`,
286
+ lanes: [left.lane_id, right.lane_id],
287
+ overlap_reasons: overlapReasons,
288
+ policy: "serialize",
289
+ reason: "Parallel work must not compete for the same canonical destination or review ownership."
290
+ });
291
+ }
292
+ }
293
+ }
294
+ return rules;
295
+ }
296
+
297
+ export function buildHandoffPackets(lanes = [], overlapRules = []) {
298
+ const overlapMap = new Map();
299
+ for (const rule of overlapRules) {
300
+ for (const laneId of rule.lanes || []) {
301
+ if (!overlapMap.has(laneId)) {
302
+ overlapMap.set(laneId, []);
303
+ }
304
+ overlapMap.get(laneId).push(rule);
305
+ }
306
+ }
307
+
308
+ return lanes
309
+ .filter((lane) => lane.role !== "adoption_operator" && lane.role !== "verification_runner")
310
+ .map((lane) => ({
311
+ packet_id: lane.publishes?.[0] || `handoff:${lane.lane_id}`,
312
+ from_lane: lane.lane_id,
313
+ to_lane: "adoption_operator",
314
+ status: "awaiting_review",
315
+ scope: {
316
+ bundle: lane.bundle || null,
317
+ review_groups: lane.owned_targets?.review_groups || []
318
+ },
319
+ decision_summary: {
320
+ completion_condition: lane.completion_condition,
321
+ overlap_constraints: (overlapMap.get(lane.lane_id) || []).map((rule) => rule.rule_id)
322
+ },
323
+ canonical_targets: lane.owned_targets?.canonical_targets || [],
324
+ recommended_next_action: {
325
+ action: lane.role === "auth_reviewer" || lane.role === "bundle_reviewer" || lane.role === "mapping_reviewer" || lane.role === "doc_promoter"
326
+ ? "publish_review_state"
327
+ : "wait",
328
+ selector: (lane.owned_targets?.review_groups || [])[0] || null
329
+ },
330
+ blocking_reasons: lane.blocking_dependencies || [],
331
+ proof_expectations: lane.proof_targets || null
332
+ }));
333
+ }
334
+
335
+ export function buildSerializedGates(lanes = []) {
336
+ const reviewLaneIds = lanes
337
+ .filter((lane) => !["adoption_operator", "verification_runner"].includes(lane.role))
338
+ .map((lane) => lane.lane_id);
339
+ return [
340
+ {
341
+ gate_id: "gate.review_resolution",
342
+ owner_lane: null,
343
+ action: "resolve_review_packets",
344
+ blocks_until: reviewLaneIds,
345
+ reason: "Canonical adoption must wait until review-oriented lanes publish their outcomes."
346
+ },
347
+ {
348
+ gate_id: "gate.canonical_adoption",
349
+ owner_lane: "adoption_operator",
350
+ action: "run_from_plan_write",
351
+ blocks_until: ["gate.review_resolution"],
352
+ reason: "Canonical promotion is a single-writer step."
353
+ },
354
+ {
355
+ gate_id: "gate.post_adoption_proof",
356
+ owner_lane: "verification_runner",
357
+ action: "run_proof_targets",
358
+ blocks_until: ["gate.canonical_adoption"],
359
+ reason: "Proof should run against merged canonical state."
360
+ }
361
+ ];
362
+ }
363
+
364
+ export function buildJoinPoints(lanes = []) {
365
+ const reviewLaneIds = lanes
366
+ .filter((lane) => !["adoption_operator", "verification_runner"].includes(lane.role))
367
+ .map((lane) => lane.lane_id);
368
+ return [
369
+ {
370
+ join_id: "join.review_packets_ready",
371
+ requires: reviewLaneIds,
372
+ then_enables: ["gate.review_resolution"]
373
+ },
374
+ {
375
+ join_id: "join.canonical_state_ready",
376
+ requires: ["gate.canonical_adoption"],
377
+ then_enables: ["gate.post_adoption_proof"]
378
+ },
379
+ {
380
+ join_id: "join.proof_complete",
381
+ requires: ["gate.post_adoption_proof"],
382
+ then_enables: []
383
+ }
384
+ ];
385
+ }
386
+
387
+ export function buildParallelWorkstreams(lanes = [], overlapRules = []) {
388
+ const reviewLanes = lanes.filter((lane) => !["adoption_operator", "verification_runner", "mapping_reviewer", "doc_promoter"].includes(lane.role));
389
+ const blockedPairs = new Set(overlapRules.flatMap((rule) => {
390
+ const [a, b] = rule.lanes || [];
391
+ return [`${a}::${b}`, `${b}::${a}`];
392
+ }));
393
+ const workstreams = [];
394
+ for (const lane of reviewLanes.sort((a, b) => laneRolePriority(a.role) - laneRolePriority(b.role) || String(a.bundle || a.lane_id).localeCompare(String(b.bundle || b.lane_id)))) {
395
+ let placed = false;
396
+ for (const stream of workstreams) {
397
+ const conflicts = stream.lane_ids.some((existingLaneId) => blockedPairs.has(`${lane.lane_id}::${existingLaneId}`));
398
+ if (!conflicts) {
399
+ stream.lane_ids.push(lane.lane_id);
400
+ if (lane.bundle && !stream.bundles.includes(lane.bundle)) {
401
+ stream.bundles.push(lane.bundle);
402
+ }
403
+ placed = true;
404
+ break;
405
+ }
406
+ }
407
+ if (!placed) {
408
+ workstreams.push({
409
+ workstream_id: `parallel.${workstreams.length + 1}`,
410
+ lane_ids: [lane.lane_id],
411
+ bundles: lane.bundle ? [lane.bundle] : [],
412
+ reason: "These review-oriented lanes do not overlap on canonical targets or review ownership."
413
+ });
414
+ }
415
+ }
416
+ return workstreams;
417
+ }
418
+
419
+ export function buildMultiAgentSummary(lanes, parallelWorkstreams, overlapRules, handoffPackets) {
420
+ const laneCounts = lanes.reduce((acc, lane) => {
421
+ acc[lane.role] = (acc[lane.role] || 0) + 1;
422
+ return acc;
423
+ }, {});
424
+ return {
425
+ lane_count: lanes.length,
426
+ role_counts: laneCounts,
427
+ parallel_workstream_count: parallelWorkstreams.length,
428
+ serialized_gate_count: 3,
429
+ overlap_rule_count: overlapRules.length,
430
+ handoff_packet_count: handoffPackets.length
431
+ };
432
+ }
433
+
434
+ export function buildMultiAgentRecommendedSequence(singleAgentPlan, parallelWorkstreams, serializedGates, resolvedWorkflowContext = null) {
435
+ const steps = [
436
+ {
437
+ order: 1,
438
+ action: "read_single_agent_plan",
439
+ reason: "Start from the default single-agent plan before splitting work.",
440
+ source: singleAgentPlan.type
441
+ },
442
+ {
443
+ order: 2,
444
+ action: "dispatch_parallel_review_lanes",
445
+ reason: parallelWorkstreams.length > 0
446
+ ? `Dispatch ${parallelWorkstreams.length} non-overlapping review workstream(s).`
447
+ : "No safe parallel review workstreams were identified; keep review serialized."
448
+ },
449
+ {
450
+ order: 3,
451
+ action: "resolve_review_packets",
452
+ reason: "Wait for review-oriented lanes to publish handoff packets before canonical adoption."
453
+ },
454
+ {
455
+ order: 4,
456
+ action: "run_from_plan_write",
457
+ reason: serializedGates.find((gate) => gate.gate_id === "gate.canonical_adoption")?.reason || "Canonical promotion is a single-writer step."
458
+ },
459
+ {
460
+ order: 5,
461
+ action: "run_proof_targets",
462
+ reason: serializedGates.find((gate) => gate.gate_id === "gate.post_adoption_proof")?.reason || "Proof should run after canonical adoption."
463
+ }
464
+ ];
465
+ if ((resolvedWorkflowContext?.effective_review_policy?.escalate_categories || []).length > 0) {
466
+ steps.splice(2, 0, {
467
+ order: 3,
468
+ action: "apply_resolved_workflow_review_policy",
469
+ reason: `Carry resolved review escalation categories through the lane plan: ${resolvedWorkflowContext.effective_review_policy.escalate_categories.join(", ")}.`
470
+ });
471
+ for (let index = 3; index < steps.length; index += 1) {
472
+ steps[index].order = index + 1;
473
+ }
474
+ }
475
+ return steps;
476
+ }
477
+
478
+ export function laneWorkflowContextOverrides(lane, resolvedWorkflowContext) {
479
+ if (!resolvedWorkflowContext) return null;
480
+ const overrides = {};
481
+ if (JSON.stringify(lane.write_scope || null) !== JSON.stringify(resolvedWorkflowContext.effective_write_scope || null)) {
482
+ overrides.effective_write_scope = lane.write_scope || null;
483
+ }
484
+ if (JSON.stringify(lane.proof_targets || null) !== JSON.stringify(resolvedWorkflowContext.effective_verification_policy || null)) {
485
+ overrides.effective_verification_policy = {
486
+ ...resolvedWorkflowContext.effective_verification_policy,
487
+ lane_proof_targets: lane.proof_targets || null
488
+ };
489
+ }
490
+ return Object.keys(overrides).length > 0 ? overrides : null;
491
+ }
492
+
493
+ export function buildMultiAgentPlanPayload({
494
+ workspace,
495
+ singleAgentPlan,
496
+ importPlan,
497
+ report,
498
+ adoptionStatus,
499
+ resolvedWorkflowContext = null
500
+ }) {
501
+ const presetGuidanceSummary = buildPresetGuidanceSummary(importPlan?.workflow_presets || null, resolvedWorkflowContext || singleAgentPlan?.resolved_workflow_context || null);
502
+ const lanes = [
503
+ ...buildBundleReviewerLanes(report, adoptionStatus, importPlan, singleAgentPlan),
504
+ ...buildAuthReviewerLanes(report, adoptionStatus, importPlan, singleAgentPlan),
505
+ ...buildMappingReviewerLane(importPlan, singleAgentPlan),
506
+ ...buildDocPromoterLane(importPlan, singleAgentPlan),
507
+ buildAdoptionOperatorLane(importPlan, singleAgentPlan),
508
+ buildVerificationRunnerLane(singleAgentPlan)
509
+ ].map((lane) => ({
510
+ ...lane,
511
+ workflow_context_overrides: laneWorkflowContextOverrides(lane, resolvedWorkflowContext)
512
+ }));
513
+ const overlapRules = buildOverlapRules(lanes);
514
+ const parallelWorkstreams = buildParallelWorkstreams(lanes, overlapRules);
515
+ const handoffPackets = buildHandoffPackets(lanes, overlapRules);
516
+ const serializedGates = buildSerializedGates(lanes);
517
+ const joinPoints = buildJoinPoints(lanes);
518
+ return {
519
+ type: "multi_agent_plan",
520
+ workspace: workspace || null,
521
+ mode: "import-adopt",
522
+ source_single_agent_plan: singleAgentPlan,
523
+ summary: buildMultiAgentSummary(lanes, parallelWorkstreams, overlapRules, handoffPackets),
524
+ coordination_strategy: {
525
+ model: "artifact_handoff",
526
+ freeform_agent_messaging: "discouraged",
527
+ single_writer_for_canonical: true
528
+ },
529
+ preset_guidance_summary: presetGuidanceSummary,
530
+ active_preset_ids: presetGuidanceSummary.active_preset_ids,
531
+ preset_blockers: presetGuidanceSummary.preset_blockers,
532
+ recommended_preset_action: presetGuidanceSummary.recommended_preset_action,
533
+ resolved_workflow_context: resolvedWorkflowContext || singleAgentPlan?.resolved_workflow_context || null,
534
+ lanes,
535
+ parallel_workstreams: parallelWorkstreams,
536
+ serialized_gates: serializedGates,
537
+ join_points: joinPoints,
538
+ overlap_rules: overlapRules,
539
+ handoff_packets: handoffPackets,
540
+ recommended_sequence: buildMultiAgentRecommendedSequence(
541
+ singleAgentPlan,
542
+ parallelWorkstreams,
543
+ serializedGates,
544
+ resolvedWorkflowContext || singleAgentPlan?.resolved_workflow_context || null
545
+ )
546
+ };
547
+ }