@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,149 @@
1
+ export function canonicalWriteCandidatesFromWriteScope(writeScope) {
2
+ return (writeScope?.safe_to_edit || []).filter((entry) => entry === "topogram/**" || String(entry).startsWith("topogram/"));
3
+ }
4
+
5
+ export function summarizeDiffArtifact(diffArtifact) {
6
+ if (!diffArtifact) {
7
+ return null;
8
+ }
9
+ const maintainedOutputs = diffArtifact.affected_maintained_surfaces?.outputs || [];
10
+ const maintainedSeams = diffArtifact.affected_maintained_surfaces?.affected_seams || [];
11
+ const highestMaintainedSeverity = [...maintainedSeams]
12
+ .sort((a, b) => severityRank(b.status) - severityRank(a.status))[0]?.status || null;
13
+ return {
14
+ baseline_root: diffArtifact.baseline_root,
15
+ review_boundary_change_count: (diffArtifact.review_boundary_changes || []).length,
16
+ maintained_file_count: (diffArtifact.affected_maintained_surfaces?.maintained_files_in_scope || []).length,
17
+ affected_verification_count: (diffArtifact.affected_verifications || []).length,
18
+ affected_output_count: maintainedOutputs.length,
19
+ affected_seam_count: maintainedSeams.length,
20
+ highest_maintained_severity: highestMaintainedSeverity
21
+ };
22
+ }
23
+
24
+ export function severityRank(status) {
25
+ if (status === "no_go") return 3;
26
+ if (status === "manual_decision") return 2;
27
+ if (status === "review_required") return 1;
28
+ return 0;
29
+ }
30
+
31
+ export function stableSortedStrings(values) {
32
+ return [...new Set((values || []).filter(Boolean))].sort();
33
+ }
34
+
35
+ export const CANONICAL_TASK_MODES = new Set([
36
+ "modeling",
37
+ "maintained-app-edit",
38
+ "import-adopt",
39
+ "diff-review",
40
+ "verification"
41
+ ]);
42
+
43
+ export const WORKFLOW_REVIEW_BLOCKERS = new Set([
44
+ "review_required",
45
+ "manual_decision",
46
+ "no_go"
47
+ ]);
48
+
49
+ export const PROVIDER_PRESET_MANUAL_DECISION_CATEGORIES = new Set([
50
+ "auth",
51
+ "deployment_assumptions",
52
+ "runtime_assumptions",
53
+ "deploy_runtime_assumptions",
54
+ "environment_expectations",
55
+ "maintained_boundary",
56
+ "ownership_visibility"
57
+ ]);
58
+
59
+ export const WORKFLOW_QUERY_FAMILIES_BY_MODE = {
60
+ modeling: ["change-plan", "write-scope", "verification-targets", "risk-summary"],
61
+ "maintained-app-edit": ["maintained-boundary", "maintained-conformance", "seam-check", "change-plan"],
62
+ "import-adopt": ["import-plan", "risk-summary", "proceed-decision", "review-packet"],
63
+ "diff-review": ["change-plan", "risk-summary", "review-packet"],
64
+ verification: ["verification-targets", "proceed-decision", "risk-summary"]
65
+ };
66
+
67
+ export function stableOrderedUnion(values = []) {
68
+ const seen = new Set();
69
+ const result = [];
70
+ for (const value of values) {
71
+ if (!value || seen.has(value)) continue;
72
+ seen.add(value);
73
+ result.push(value);
74
+ }
75
+ return result;
76
+ }
77
+
78
+ export function targetWidgetId(target = {}) {
79
+ return target.widget_id || target.component_id || null;
80
+ }
81
+
82
+ export function componentBehaviorArtifactPath(target = {}) {
83
+ if (target.target !== "widget-behavior-report") {
84
+ return null;
85
+ }
86
+ const suffix = [target.projection_id, targetWidgetId(target)].filter(Boolean).join(".");
87
+ return suffix ? `${suffix}.widget-behavior-report.json` : "widget-behavior-report.json";
88
+ }
89
+
90
+ export function componentBehaviorQueryCommand(target = {}) {
91
+ if (target.target !== "widget-behavior-report") {
92
+ return null;
93
+ }
94
+ const parts = ["topogram", "query", "widget-behavior", "./topogram"];
95
+ if (target.projection_id) {
96
+ parts.push("--projection", target.projection_id);
97
+ }
98
+ const widgetId = targetWidgetId(target);
99
+ if (widgetId) {
100
+ parts.push("--widget", widgetId);
101
+ }
102
+ parts.push("--json");
103
+ return parts.join(" ");
104
+ }
105
+
106
+ export function recommendedArtifactQueriesFromGeneratorTargets(generatorTargets = []) {
107
+ const queries = [];
108
+ const seen = new Set();
109
+ for (const target of generatorTargets || []) {
110
+ const command = componentBehaviorQueryCommand(target);
111
+ if (!command || seen.has(command)) continue;
112
+ seen.add(command);
113
+ queries.push({
114
+ query: "widget-behavior",
115
+ target: target.target,
116
+ projection_id: target.projection_id || null,
117
+ widget_id: targetWidgetId(target),
118
+ command
119
+ });
120
+ }
121
+ return queries;
122
+ }
123
+
124
+ export function artifactLoadOrderFromGeneratorTargets(generatorTargets = []) {
125
+ return stableOrderedUnion((generatorTargets || [])
126
+ .map((target) => componentBehaviorArtifactPath(target))
127
+ .filter(Boolean));
128
+ }
129
+
130
+ export function flattenVerificationTargets(verificationTargets = null) {
131
+ if (!verificationTargets || typeof verificationTargets !== "object") {
132
+ return [];
133
+ }
134
+ const entries = [];
135
+ for (const [key, value] of Object.entries(verificationTargets)) {
136
+ if (Array.isArray(value)) {
137
+ for (const item of value) {
138
+ if (item) entries.push(item);
139
+ }
140
+ continue;
141
+ }
142
+ if (key === "output_verification_targets" && Array.isArray(value)) {
143
+ for (const output of value) {
144
+ entries.push(...flattenVerificationTargets(output?.verification_targets || null));
145
+ }
146
+ }
147
+ }
148
+ return stableOrderedUnion(entries);
149
+ }
@@ -0,0 +1,539 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import {
5
+ severityRank,
6
+ stableSortedStrings
7
+ } from "./common.js";
8
+ import {
9
+ buildMaintainedOutputGroups,
10
+ normalizeSeamSummary
11
+ } from "./maintained-shared.js";
12
+ export function buildMaintainedImpacts({ diffArtifact, maintainedBoundaryArtifact, sliceArtifact, verificationTargets = null }) {
13
+ const diffMaintained = diffArtifact?.affected_maintained_surfaces || null;
14
+ const boundaryFiles = maintainedBoundaryArtifact?.maintained_files_in_scope || [];
15
+ const boundarySeams = maintainedBoundaryArtifact?.seams || [];
16
+ const boundaryOutputs = maintainedBoundaryArtifact?.outputs || [];
17
+ const proofStories = maintainedBoundaryArtifact?.proof_stories || [];
18
+ const likelyFiles = stableSortedStrings([
19
+ ...(diffMaintained?.maintained_files_in_scope || []),
20
+ ...boundaryFiles
21
+ ]);
22
+ const likelySeams = (diffMaintained?.affected_seams || []).length > 0
23
+ ? diffMaintained.affected_seams
24
+ : boundarySeams;
25
+ const likelyStories = diffMaintained?.proof_stories || proofStories;
26
+ const reviewSensitive = likelyStories.some((story) => {
27
+ const boundary = story.review_boundary || {};
28
+ return boundary.automation_class && boundary.automation_class !== "safe";
29
+ }) || likelySeams.some((seam) => seam.status && seam.status !== "aligned");
30
+ const normalizedSeams = likelySeams.map((seam) => normalizeSeamSummary(seam));
31
+ const affectedOutputs = buildMaintainedOutputGroups(
32
+ diffMaintained?.outputs || boundaryOutputs,
33
+ normalizedSeams,
34
+ {
35
+ verificationTargetsFallback: verificationTargets
36
+ }
37
+ ).map((output) => ({
38
+ output_id: output.output_id,
39
+ label: output.label,
40
+ kind: output.kind,
41
+ root_paths: output.root_paths,
42
+ ownership_boundary: output.ownership_boundary,
43
+ write_scope: output.write_scope,
44
+ verification_targets: output.verification_targets,
45
+ maintained_files_in_scope: output.maintained_files_in_scope,
46
+ human_owned_seams: output.human_owned_seams,
47
+ affected_seams: output.seams,
48
+ proof_stories: output.proof_stories,
49
+ highest_severity: output.summary.highest_severity,
50
+ status_counts: output.summary.status_counts
51
+ }));
52
+
53
+ return {
54
+ maintained_code_likely_impacted: Boolean(
55
+ diffMaintained?.ownership_interpretation?.maintained_code_impact ||
56
+ likelySeams.length > 0 ||
57
+ likelyFiles.length > 0 ||
58
+ (sliceArtifact?.ownership_boundary && boundaryFiles.length > 0)
59
+ ),
60
+ impact_scope: diffMaintained?.ownership_interpretation?.generated_only_impact
61
+ ? "generated_only"
62
+ : reviewSensitive
63
+ ? "review_sensitive"
64
+ : likelyFiles.length > 0
65
+ ? "maintained_code"
66
+ : "generated_only",
67
+ review_sensitive: reviewSensitive,
68
+ maintained_files_in_scope: likelyFiles,
69
+ human_owned_seams: stableSortedStrings([
70
+ ...(maintainedBoundaryArtifact?.human_owned_seams || []),
71
+ ...likelySeams.map((seam) => seam.label),
72
+ ...likelyStories.flatMap((story) => story.human_owned_seams || [])
73
+ ]),
74
+ affected_outputs: affectedOutputs,
75
+ affected_seams: normalizedSeams,
76
+ proof_stories: likelyStories.map((story) => normalizeProofStorySummary(story))
77
+ };
78
+ }
79
+
80
+ export function verificationTargetsForOutput(outputId, maintainedBoundaryArtifact, fallbackVerificationTargets = null) {
81
+ const output = (maintainedBoundaryArtifact?.outputs || []).find((entry) => entry.output_id === outputId);
82
+ return output?.verification_targets || fallbackVerificationTargets || null;
83
+ }
84
+
85
+ export const DEPENDENCY_TOKEN_STOPWORDS = new Set([
86
+ "and",
87
+ "cap",
88
+ "proj",
89
+ "projection",
90
+ "journey",
91
+ "workflow",
92
+ "entity",
93
+ "shape",
94
+ "input",
95
+ "output",
96
+ "rule",
97
+ "ui",
98
+ "web",
99
+ "api",
100
+ "db",
101
+ "shared",
102
+ "runtime",
103
+ "contract",
104
+ "contracts",
105
+ "proof",
106
+ "package",
107
+ "maintained",
108
+ "bundle",
109
+ "screen",
110
+ "detail",
111
+ "list",
112
+ "view",
113
+ "ver"
114
+ ]);
115
+
116
+ export function existingGraphRoot(graph) {
117
+ const root = graph?.root || null;
118
+ return root && fs.existsSync(root) ? root : null;
119
+ }
120
+
121
+ export function readableFilePath(root, relativePath) {
122
+ if (!root || !relativePath) return null;
123
+ let current = root;
124
+ while (true) {
125
+ const candidate = path.join(current, relativePath);
126
+ if (fs.existsSync(candidate)) {
127
+ return candidate;
128
+ }
129
+ const parent = path.dirname(current);
130
+ if (parent === current) {
131
+ return null;
132
+ }
133
+ current = parent;
134
+ }
135
+ }
136
+
137
+ export function loadMaintainedModuleContents(root, maintainedModules = []) {
138
+ return maintainedModules
139
+ .map((relativePath) => readableFilePath(root, relativePath))
140
+ .filter(Boolean)
141
+ .map((absolutePath) => ({
142
+ absolutePath,
143
+ contents: fs.readFileSync(absolutePath, "utf8").toLowerCase()
144
+ }));
145
+ }
146
+
147
+ export function dependencyTokens(emittedDependencies = []) {
148
+ return stableSortedStrings(
149
+ emittedDependencies.flatMap((id) =>
150
+ String(id || "")
151
+ .toLowerCase()
152
+ .split(/[^a-z0-9]+/)
153
+ .filter((token) => token.length > 2 && !DEPENDENCY_TOKEN_STOPWORDS.has(token))
154
+ )
155
+ );
156
+ }
157
+
158
+ export function verificationCoverageForSeamKind(seamKind, verificationTargets) {
159
+ const generatedChecks = verificationTargets?.generated_checks || [];
160
+ const maintainedChecks = verificationTargets?.maintained_app_checks || [];
161
+ if (seamKind === "ui_presenter" || seamKind === "workflow_affordance") {
162
+ return maintainedChecks.length > 0 || generatedChecks.some((check) => check.includes("runtime") || check.includes("compile"));
163
+ }
164
+ if (seamKind === "policy_interpretation") {
165
+ return maintainedChecks.length > 0 || generatedChecks.some((check) => check.includes("runtime"));
166
+ }
167
+ if (seamKind === "verification_harness") {
168
+ return maintainedChecks.length > 0;
169
+ }
170
+ return maintainedChecks.length > 0 || generatedChecks.length > 0 || (verificationTargets?.verification_ids || []).length > 0;
171
+ }
172
+
173
+ export function buildSeamProbeReport(graph, seam, { verificationTargets = null, diffBacked = false, outputRecord = null } = {}) {
174
+ const maintainedModules = seam?.maintained_modules || [];
175
+ const proofStories = seam?.proof_stories || [];
176
+ const emittedDependencies = seam?.emitted_dependencies || [];
177
+ const graphRoot = existingGraphRoot(graph);
178
+ const existingModules = maintainedModules.filter((relativePath) => readableFilePath(graphRoot, relativePath));
179
+ const proofStoryFiles = proofStories.filter((story) => readableFilePath(graphRoot, story?.relativePath));
180
+ const outputMaintainedFiles = new Set([...(outputRecord?.maintained_files_in_scope || []), ...maintainedModules]);
181
+ const proofStoryMaintainedFiles = stableSortedStrings(proofStories.flatMap((story) => story?.maintained_files || []));
182
+ const resolvedDependencies = emittedDependencies.filter((id) => id === "maintained-proof-package" || (graph ? Boolean(summarizeById(graph, id)) : false));
183
+ const moduleContents = loadMaintainedModuleContents(graphRoot, maintainedModules);
184
+ const corroborationTokens = dependencyTokens(emittedDependencies);
185
+ const corroboratedTokens = corroborationTokens.filter((token) => moduleContents.some((entry) => entry.contents.includes(token)));
186
+ const probeList = [
187
+ {
188
+ probe_id: "maintained_modules_present",
189
+ status: maintainedModules.length > 0 ? "pass" : "fail",
190
+ detail: maintainedModules.length > 0
191
+ ? `${maintainedModules.length} maintained module(s) are attached to this seam.`
192
+ : "No maintained modules are attached to this seam."
193
+ },
194
+ {
195
+ probe_id: "proof_story_present",
196
+ status: proofStories.length > 0 ? "pass" : "fail",
197
+ detail: proofStories.length > 0
198
+ ? `${proofStories.length} proof stor${proofStories.length === 1 ? "y is" : "ies are"} attached to this seam.`
199
+ : "No proof story is attached to this seam."
200
+ },
201
+ {
202
+ probe_id: "emitted_dependencies_resolved",
203
+ status: emittedDependencies.length === resolvedDependencies.length ? "pass" : "fail",
204
+ detail: emittedDependencies.length === resolvedDependencies.length
205
+ ? `All ${emittedDependencies.length} emitted dependenc${emittedDependencies.length === 1 ? "y resolves" : "ies resolve"} in the current graph.`
206
+ : `${resolvedDependencies.length} of ${emittedDependencies.length} emitted dependencies resolve in the current graph.`
207
+ },
208
+ {
209
+ probe_id: "verification_targets_present",
210
+ status: ((verificationTargets?.generated_checks || []).length + (verificationTargets?.maintained_app_checks || []).length + (verificationTargets?.verification_ids || []).length) > 0 ? "pass" : "fail",
211
+ detail: verificationTargets
212
+ ? "Verification targets are attached to this seam's output."
213
+ : "No verification targets are attached to this seam's output."
214
+ },
215
+ {
216
+ probe_id: "maintained_modules_exist",
217
+ status: !graphRoot
218
+ ? "skip"
219
+ : maintainedModules.length === 0
220
+ ? "skip"
221
+ : existingModules.length === maintainedModules.length ? "pass" : "fail",
222
+ detail: !graphRoot
223
+ ? "Workspace root is unavailable, so maintained module existence was not verified."
224
+ : maintainedModules.length === 0
225
+ ? "No maintained modules are attached to this seam, so file existence was not verified."
226
+ : existingModules.length === maintainedModules.length
227
+ ? `All ${maintainedModules.length} maintained module file${maintainedModules.length === 1 ? "" : "s"} exist on disk.`
228
+ : `${existingModules.length} of ${maintainedModules.length} maintained module files exist on disk.`
229
+ },
230
+ {
231
+ probe_id: "proof_story_files_exist",
232
+ status: !graphRoot
233
+ ? "skip"
234
+ : proofStories.length === 0
235
+ ? "skip"
236
+ : proofStoryFiles.length === proofStories.length ? "pass" : "fail",
237
+ detail: !graphRoot
238
+ ? "Workspace root is unavailable, so proof story files were not verified."
239
+ : proofStories.length === 0
240
+ ? "No proof stories are attached to this seam, so proof file existence was not verified."
241
+ : proofStoryFiles.length === proofStories.length
242
+ ? `All ${proofStories.length} proof stor${proofStories.length === 1 ? "y file exists" : "y files exist"} on disk.`
243
+ : `${proofStoryFiles.length} of ${proofStories.length} proof story files exist on disk.`
244
+ },
245
+ {
246
+ probe_id: "proof_story_maintained_files_in_scope",
247
+ status: proofStoryMaintainedFiles.length === 0
248
+ ? "skip"
249
+ : proofStoryMaintainedFiles.every((relativePath) => outputMaintainedFiles.has(relativePath)) ? "pass" : "fail",
250
+ detail: proofStoryMaintainedFiles.length === 0
251
+ ? "Proof stories do not declare maintained files for this seam."
252
+ : proofStoryMaintainedFiles.every((relativePath) => outputMaintainedFiles.has(relativePath))
253
+ ? "All maintained files named by proof stories are still in the seam or output scope."
254
+ : "At least one maintained file named by a proof story is no longer in the seam or output scope."
255
+ },
256
+ {
257
+ probe_id: "emitted_dependency_tokens_corroborated",
258
+ status: !graphRoot
259
+ ? "skip"
260
+ : corroborationTokens.length === 0
261
+ ? "skip"
262
+ : corroboratedTokens.length > 0 ? "pass" : "fail",
263
+ detail: !graphRoot
264
+ ? "Workspace root is unavailable, so maintained-module token corroboration was not verified."
265
+ : corroborationTokens.length === 0
266
+ ? "No dependency-specific tokens were available for lightweight maintained-module corroboration."
267
+ : corroboratedTokens.length > 0
268
+ ? `Maintained modules corroborate dependency tokens: ${corroboratedTokens.join(", ")}.`
269
+ : `Maintained modules do not currently corroborate any dependency tokens from: ${corroborationTokens.join(", ")}.`
270
+ },
271
+ {
272
+ probe_id: "verification_targets_cover_seam_kind",
273
+ status: verificationTargets
274
+ ? (verificationCoverageForSeamKind(seam?.kind || null, verificationTargets) ? "pass" : "fail")
275
+ : "skip",
276
+ detail: !verificationTargets
277
+ ? "No verification targets are attached to this seam's output, so seam-kind coverage was not verified."
278
+ : verificationCoverageForSeamKind(seam?.kind || null, verificationTargets)
279
+ ? `Verification targets cover the seam kind '${seam?.kind || "unknown"}'.`
280
+ : `Verification targets do not yet clearly cover the seam kind '${seam?.kind || "unknown"}'.`
281
+ }
282
+ ];
283
+ let checkStatus = "aligned";
284
+ if (seam?.status === "no_go") {
285
+ checkStatus = "no_go";
286
+ } else if (diffBacked && ["manual_decision", "review_required"].includes(seam?.status)) {
287
+ checkStatus = "stale";
288
+ } else if (probeList.some((probe) => probe.status === "fail")) {
289
+ checkStatus = "unverifiable";
290
+ } else if (["manual_decision", "review_required"].includes(seam?.status)) {
291
+ checkStatus = "guarded";
292
+ }
293
+
294
+ return {
295
+ seam_id: seam?.seam_id || null,
296
+ output_id: seam?.output_id || null,
297
+ check_status: checkStatus,
298
+ probes: probeList
299
+ };
300
+ }
301
+
302
+ export function conformanceStateFromSeamCheck(seam, seamCheck) {
303
+ if (seamCheck?.check_status === "no_go") return "no_go";
304
+ if (seamCheck?.check_status === "stale") return "drift_suspected";
305
+ if (seamCheck?.check_status === "unverifiable") return "unverifiable";
306
+ if ((seam?.status || null) === "manual_decision" || (seam?.status || null) === "review_required" || seamCheck?.check_status === "guarded") {
307
+ return "review_required";
308
+ }
309
+ return "aligned";
310
+ }
311
+
312
+ export function importProposalDependencyIds(proposalSurface = {}) {
313
+ return stableSortedStrings([
314
+ ...(proposalSurface.requirements?.related_capabilities || []),
315
+ ...(proposalSurface.requirements?.related_rules || []),
316
+ ...(proposalSurface.requirements?.related_workflows || []),
317
+ ...(proposalSurface.requirements?.related_docs || []),
318
+ ...((proposalSurface.projection_impacts || []).map((impact) => impact.projection_id)),
319
+ ...((proposalSurface.ui_impacts || []).map((impact) => impact.projection_id)),
320
+ ...((proposalSurface.workflow_impacts || []).map((impact) => impact.id))
321
+ ]);
322
+ }
323
+
324
+ export function buildImportProposalMaintainedImpacts(proposalSurface, maintainedBoundaryArtifact) {
325
+ const dependencyIds = importProposalDependencyIds(proposalSurface);
326
+ const seamIdsFromCandidates = new Set((proposalSurface.maintained_seam_candidates || []).map((candidate) => candidate.seam_id).filter(Boolean));
327
+ const matchedSeams = ((maintainedBoundaryArtifact?.seams || []).filter((seam) =>
328
+ seamIdsFromCandidates.size > 0
329
+ ? seamIdsFromCandidates.has(seam.seam_id)
330
+ : (seam.emitted_dependencies || []).some((dependency) => dependencyIds.includes(dependency))
331
+ ))
332
+ .map((seam) => normalizeSeamSummary(seam));
333
+ const matchedOutputs = buildMaintainedOutputGroups(
334
+ (maintainedBoundaryArtifact?.outputs || []).filter((output) =>
335
+ (output.seams || []).some((seam) => matchedSeams.some((matched) => matched.seam_id === seam.seam_id))
336
+ ),
337
+ matchedSeams,
338
+ {
339
+ verificationTargetsFallback: null
340
+ }
341
+ ).map((output) => compactMaintainedOutputSummary({
342
+ ...output,
343
+ affected_seams: output.seams,
344
+ highest_severity: output.summary.highest_severity
345
+ }));
346
+ const candidateSummary = summarizeImportMaintainedSeamCandidates(proposalSurface, matchedSeams);
347
+
348
+ return {
349
+ dependency_ids: dependencyIds,
350
+ maintained_seam_candidates: proposalSurface.maintained_seam_candidates || [],
351
+ maintained_seam_candidate_summary: candidateSummary,
352
+ affected_outputs: matchedOutputs,
353
+ affected_seams: matchedSeams.map((seam) => compactMaintainedSeamSummary(seam)),
354
+ highest_severity: matchedSeams.sort((a, b) => severityRank(b.status) - severityRank(a.status))[0]?.status || "aligned"
355
+ };
356
+ }
357
+
358
+ export function summarizeImportMaintainedSeamCandidates(proposalSurface = {}, matchedSeams = []) {
359
+ const candidates = proposalSurface.maintained_seam_candidates || [];
360
+ const highestConfidence = candidates.reduce((max, candidate) => Math.max(max, Number(candidate.confidence || 0)), 0);
361
+ const topCandidate = [...candidates].sort((a, b) => Number(b.confidence || 0) - Number(a.confidence || 0))[0] || null;
362
+ const matchedOutputs = stableSortedStrings(matchedSeams.map((seam) => seam.output_id).filter(Boolean));
363
+ const status = candidates.length > 0
364
+ ? "clear_candidate"
365
+ : matchedSeams.length > 0
366
+ ? "matched_without_explicit_candidate"
367
+ : "no_candidate";
368
+
369
+ return {
370
+ status,
371
+ candidate_count: candidates.length,
372
+ matched_seam_count: matchedSeams.length,
373
+ matched_output_ids: matchedOutputs,
374
+ highest_confidence: candidates.length > 0 ? highestConfidence : null,
375
+ top_candidate: topCandidate
376
+ ? {
377
+ seam_id: topCandidate.seam_id || null,
378
+ output_id: topCandidate.output_id || null,
379
+ status: topCandidate.status || null,
380
+ confidence: Number(topCandidate.confidence || 0)
381
+ }
382
+ : null,
383
+ review_guidance: candidates.length > 0
384
+ ? "Review the candidate maintained seam mapping before selective adoption."
385
+ : matchedSeams.length > 0
386
+ ? "Proposal dependencies overlap maintained seams, but there is no explicit candidate mapping yet."
387
+ : "No conservative maintained seam candidate was inferred for this proposal surface."
388
+ };
389
+ }
390
+
391
+ export function buildImportMaintainedRisk(proposalSurfaces = [], maintainedBoundaryArtifact = null) {
392
+ const enrichedProposalSurfaces = proposalSurfaces.map((surface) => {
393
+ const maintainedImpacts = buildImportProposalMaintainedImpacts(surface, maintainedBoundaryArtifact);
394
+ return {
395
+ ...surface,
396
+ maintained_impacts: maintainedImpacts
397
+ };
398
+ });
399
+ const allSeams = enrichedProposalSurfaces.flatMap((surface) => surface.maintained_impacts?.affected_seams || []);
400
+ const seamIds = new Set(allSeams.map((seam) => seam.seam_id));
401
+ const normalizedSeams = (maintainedBoundaryArtifact?.seams || [])
402
+ .filter((seam) => seamIds.has(seam.seam_id))
403
+ .map((seam) => normalizeSeamSummary(seam));
404
+ const outputIds = new Set(allSeams.map((seam) => seam.output_id));
405
+ const affectedOutputs = (maintainedBoundaryArtifact?.outputs || [])
406
+ .filter((output) => outputIds.has(output.output_id))
407
+ .map((output) => ({
408
+ ...output,
409
+ affected_seams: (output.seams || []).filter((seam) => seamIds.has(seam.seam_id)),
410
+ highest_severity: [...(output.seams || []).filter((seam) => seamIds.has(seam.seam_id))]
411
+ .sort((a, b) => severityRank(b.status) - severityRank(a.status))[0]?.status || "aligned"
412
+ }));
413
+ const maintainedRisk = buildMaintainedRiskSummary({
414
+ maintainedImpacts: {
415
+ affected_outputs: affectedOutputs,
416
+ affected_seams: normalizedSeams,
417
+ maintained_files_in_scope: stableSortedStrings(normalizedSeams.flatMap((seam) => seam.maintained_modules || []))
418
+ },
419
+ maintainedBoundary: maintainedBoundaryArtifact
420
+ });
421
+ const proposalSurfaceSummaries = enrichedProposalSurfaces.map((surface) => ({
422
+ id: surface.id,
423
+ bundle: surface.bundle || null,
424
+ kind: surface.kind || null,
425
+ ...surface.maintained_impacts?.maintained_seam_candidate_summary
426
+ }));
427
+ const surfacesWithCandidates = proposalSurfaceSummaries.filter((surface) => surface.status === "clear_candidate");
428
+ const surfacesWithoutCandidates = proposalSurfaceSummaries.filter((surface) => surface.status === "no_candidate");
429
+ const maintainedSeamReviewSummary = {
430
+ status: surfacesWithCandidates.length > 0
431
+ ? (surfacesWithoutCandidates.length > 0 ? "mixed" : "clear_candidate")
432
+ : "no_candidate",
433
+ proposal_surface_count: proposalSurfaceSummaries.length,
434
+ surfaces_with_candidates_count: surfacesWithCandidates.length,
435
+ surfaces_without_candidates_count: surfacesWithoutCandidates.length,
436
+ candidate_count: proposalSurfaceSummaries.reduce((sum, surface) => sum + (surface.candidate_count || 0), 0),
437
+ top_candidate: surfacesWithCandidates
438
+ .sort((a, b) => (b.highest_confidence || 0) - (a.highest_confidence || 0))[0]?.top_candidate || null,
439
+ proposal_surfaces: proposalSurfaceSummaries
440
+ };
441
+
442
+ return {
443
+ proposal_surfaces: enrichedProposalSurfaces,
444
+ maintained_risk: {
445
+ ...maintainedRisk,
446
+ maintained_seam_review_summary: maintainedSeamReviewSummary
447
+ },
448
+ maintained_seam_review_summary: maintainedSeamReviewSummary
449
+ };
450
+ }
451
+
452
+ export function compactMaintainedSeamSummary(seam) {
453
+ return {
454
+ seam_id: seam?.seam_id || null,
455
+ seam_family_id: seam?.seam_family_id || null,
456
+ seam_family_label: seam?.seam_family_label || null,
457
+ output_id: seam?.output_id || null,
458
+ kind: seam?.kind || null,
459
+ status: seam?.status || null,
460
+ ownership_class: seam?.ownership_class || null
461
+ };
462
+ }
463
+
464
+ export function compactMaintainedOutputSummary(output) {
465
+ const verificationTargets = output?.verification_targets || null;
466
+ return {
467
+ output_id: output?.output_id || null,
468
+ kind: output?.kind || null,
469
+ highest_severity: output?.highest_severity || output?.summary?.highest_severity || "aligned",
470
+ affected_seam_count: output?.affected_seams?.length || output?.summary?.affected_seam_count || 0,
471
+ affected_seam_family_count: output?.summary?.affected_seam_family_count || 0,
472
+ maintained_file_count: output?.maintained_files_in_scope?.length || output?.summary?.maintained_file_count || 0,
473
+ verification_targets: verificationTargets
474
+ };
475
+ }
476
+
477
+ export function buildMaintainedRiskSummary({ maintainedImpacts = null, maintainedBoundary = null, diffSummary = null } = {}) {
478
+ const affectedOutputs = maintainedImpacts?.affected_outputs || (maintainedBoundary?.outputs || []).map((output) => ({
479
+ output_id: output.output_id,
480
+ kind: output.kind,
481
+ highest_severity: output?.summary?.highest_severity || "aligned",
482
+ affected_seams: output.seams || [],
483
+ maintained_files_in_scope: output.maintained_files_in_scope || [],
484
+ verification_targets: output.verification_targets || null
485
+ }));
486
+ const affectedSeams = maintainedImpacts?.affected_seams || (maintainedBoundary?.seams || []).map((seam) => normalizeSeamSummary(seam));
487
+ const maintainedFilesInScope = stableSortedStrings(
488
+ maintainedImpacts?.maintained_files_in_scope ||
489
+ maintainedBoundary?.maintained_files_in_scope ||
490
+ affectedOutputs.flatMap((output) => output.maintained_files_in_scope || []) ||
491
+ affectedSeams.flatMap((seam) => seam.maintained_modules || [])
492
+ );
493
+ const compactOutputs = affectedOutputs
494
+ .map((output) => compactMaintainedOutputSummary(output))
495
+ .filter((output) => output.output_id || output.affected_seam_count > 0 || output.maintained_file_count > 0)
496
+ .sort((a, b) => String(a.output_id || "").localeCompare(String(b.output_id || "")));
497
+ const compactSeams = affectedSeams
498
+ .map((seam) => compactMaintainedSeamSummary(seam))
499
+ .filter((seam) => seam.seam_id || seam.output_id || seam.kind)
500
+ .sort((a, b) => {
501
+ const severityCompare = severityRank(b.status) - severityRank(a.status);
502
+ return severityCompare !== 0 ? severityCompare : String(a.seam_id || "").localeCompare(String(b.seam_id || ""));
503
+ });
504
+ const derivedStatusCounts = {
505
+ aligned: compactSeams.filter((seam) => seam.status === "aligned").length,
506
+ review_required: compactSeams.filter((seam) => seam.status === "review_required").length,
507
+ manual_decision: compactSeams.filter((seam) => seam.status === "manual_decision").length,
508
+ no_go: compactSeams.filter((seam) => seam.status === "no_go").length
509
+ };
510
+ const statusCounts = {
511
+ aligned: derivedStatusCounts.aligned || maintainedBoundary?.summary?.aligned_count || 0,
512
+ review_required: derivedStatusCounts.review_required || maintainedBoundary?.summary?.review_required_count || 0,
513
+ manual_decision: derivedStatusCounts.manual_decision || maintainedBoundary?.summary?.manual_decision_count || 0,
514
+ no_go: derivedStatusCounts.no_go || maintainedBoundary?.summary?.no_go_count || 0
515
+ };
516
+ const highestSeverity = compactSeams[0]?.status
517
+ || [...compactOutputs].sort((a, b) => severityRank(b.highest_severity) - severityRank(a.highest_severity))[0]?.highest_severity
518
+ || (statusCounts.no_go > 0 ? "no_go" : statusCounts.manual_decision > 0 ? "manual_decision" : statusCounts.review_required > 0 ? "review_required" : null)
519
+ || diffSummary?.highest_maintained_severity
520
+ || "aligned";
521
+ const outputVerificationTargets = compactOutputs.map((output) => ({
522
+ output_id: output.output_id,
523
+ verification_targets: output.verification_targets || null
524
+ }));
525
+ const affectedSeamFamilies = stableSortedStrings(compactSeams.map((seam) => seam.seam_family_id).filter(Boolean));
526
+
527
+ return {
528
+ affected_output_count: compactOutputs.length || diffSummary?.affected_output_count || 0,
529
+ affected_seam_count: compactSeams.length || diffSummary?.affected_seam_count || 0,
530
+ affected_seam_family_count: affectedSeamFamilies.length,
531
+ highest_severity: highestSeverity,
532
+ status_counts: statusCounts,
533
+ affected_seam_families: affectedSeamFamilies,
534
+ affected_outputs: compactOutputs,
535
+ affected_seams: compactSeams,
536
+ maintained_files_in_scope: maintainedFilesInScope,
537
+ output_verification_targets: outputVerificationTargets
538
+ };
539
+ }