@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
@@ -1,1397 +1,87 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
-
4
- import { parseDocFile } from "../../workspace-docs.js";
5
- import {
6
- defaultOwnershipBoundary,
7
- ownershipBoundaryForMaintainedSurface,
8
- reviewBoundaryForCapability as reviewBoundaryForCapabilityPolicy,
9
- reviewBoundaryForEntity as reviewBoundaryForEntityPolicy,
10
- reviewBoundaryForJourneyDoc as reviewBoundaryForJourneyDocPolicy,
11
- reviewBoundaryForMaintainedClassification,
12
- reviewBoundaryForProjection as reviewBoundaryForProjectionPolicy,
13
- reviewBoundaryForWorkflowDoc as reviewBoundaryForWorkflowDocPolicy
14
- } from "../../policy/review-boundaries.js";
15
-
16
- const bundledRepoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), "..", "..", "..", "..");
17
-
18
- function stableSortedStrings(values) {
19
- return [...new Set((values || []).filter(Boolean))].sort();
20
- }
21
-
22
- function seamIdHint(value) {
23
- return String(value || "")
24
- .trim()
25
- .toLowerCase()
26
- .replace(/[^a-z0-9]+/g, "_")
27
- .replace(/^_+|_+$/g, "") || "maintained_surface";
28
- }
29
-
30
- function titleCaseWords(value) {
31
- return String(value || "")
32
- .split(/[_\-\s]+/)
33
- .filter(Boolean)
34
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
35
- .join(" ");
36
- }
37
-
38
- function refIds(items) {
39
- return stableSortedStrings((items || []).map((item) => item?.id || item?.target?.id));
40
- }
41
-
42
- function docIds(items) {
43
- return stableSortedStrings((items || []).map((item) => item?.id));
44
- }
45
-
46
- function summarizeField(field) {
47
- return {
48
- name: field.name || null,
49
- type: field.fieldType || null,
50
- requiredness: field.requiredness || null,
51
- defaultValue: field.defaultValue ?? null
52
- };
53
- }
54
-
55
- function summarizeProjection(projection) {
56
- return {
57
- id: projection.id,
58
- kind: projection.kind,
59
- name: projection.name || projection.id,
60
- description: projection.description || null,
61
- type: projection.type || null,
62
- realizes: refIds(projection.realizes),
63
- outputs: stableSortedStrings(projection.outputs || []),
64
- uiScreens: stableSortedStrings((projection.uiScreens || []).map((screen) => screen.id)),
65
- widgetBindings: stableSortedStrings((projection.widgetBindings || []).map((entry) => entry.widget?.id).filter(Boolean)),
66
- dbTables: stableSortedStrings((projection.dbTables || []).map((table) => table.table || table.entity?.id)),
67
- httpCapabilities: stableSortedStrings((projection.http || []).map((entry) => entry.capability?.id)),
68
- reviewBoundary: reviewBoundaryForProjectionPolicy(projection),
69
- ownership_boundary: defaultOwnershipBoundary()
70
- };
71
- }
72
-
73
- function summarizeRule(rule) {
74
- return {
75
- id: rule.id,
76
- kind: rule.kind,
77
- name: rule.name || rule.id,
78
- description: rule.description || null,
79
- appliesTo: refIds(rule.appliesTo),
80
- actors: refIds(rule.actors),
81
- roles: refIds(rule.roles),
82
- severity: rule.severity || null,
83
- ownership_boundary: defaultOwnershipBoundary()
84
- };
85
- }
86
-
87
- function summarizeVerification(verification) {
88
- return {
89
- id: verification.id,
90
- kind: verification.kind,
91
- name: verification.name || verification.id,
92
- description: verification.description || null,
93
- method: verification.method || null,
94
- validates: refIds(verification.validates),
95
- scenarios: stableSortedStrings(verification.scenarios || []),
96
- ownership_boundary: defaultOwnershipBoundary()
97
- };
98
- }
99
-
100
- function summarizeJourneyDoc(doc) {
101
- return {
102
- id: doc.id,
103
- kind: doc.kind,
104
- title: doc.title || doc.id,
105
- summary: doc.summary || null,
106
- relatedCapabilities: stableSortedStrings(doc.relatedCapabilities || []),
107
- relatedWorkflows: stableSortedStrings(doc.relatedWorkflows || []),
108
- relatedProjections: stableSortedStrings(doc.relatedProjections || []),
109
- reviewRequired: Boolean(doc.reviewRequired),
110
- reviewBoundary: doc.kind === "workflow" ? reviewBoundaryForWorkflowDocPolicy(doc) : reviewBoundaryForJourneyDocPolicy(doc),
111
- ownership_boundary: defaultOwnershipBoundary()
112
- };
113
- }
114
-
115
- function summarizeComponent(statement) {
116
- return {
117
- id: statement.id,
118
- kind: statement.kind,
119
- name: statement.name || statement.id,
120
- description: statement.description || null,
121
- category: statement.category || null,
122
- version: statement.version || null,
123
- status: statement.status || null,
124
- props: (statement.props || []).map((prop) => ({
125
- name: prop.name ?? null,
126
- type: prop.fieldType ?? null,
127
- requiredness: prop.requiredness ?? null,
128
- defaultValue: prop.defaultValue ?? null
129
- })),
130
- events: (statement.events || []).map((event) => ({
131
- id: event.id ?? null,
132
- shape: event.shape?.id ?? null
133
- })),
134
- slots: (statement.slots || []).map((slot) => ({
135
- id: slot.id ?? null,
136
- description: slot.description ?? null
137
- })),
138
- behavior: stableSortedStrings(statement.behavior || []),
139
- behaviors: (statement.behaviors || []).map((behavior) => ({
140
- kind: behavior.kind || null,
141
- directives: behavior.directives || {},
142
- source: behavior.source || null
143
- })),
144
- patterns: stableSortedStrings(statement.patterns || []),
145
- regions: stableSortedStrings(statement.regions || []),
146
- approvals: stableSortedStrings(statement.approvals || []),
147
- lookups: refIds(statement.lookups),
148
- dependencies: refIds(statement.dependencies),
149
- ownership_boundary: defaultOwnershipBoundary()
150
- };
151
- }
152
-
153
- function summarizeStatement(statement) {
154
- switch (statement.kind) {
155
- case "entity":
156
- return {
157
- id: statement.id,
158
- kind: statement.kind,
159
- name: statement.name || statement.id,
160
- description: statement.description || null,
161
- fields: (statement.fields || []).map(summarizeField),
162
- relations: (statement.relations || []).map((relation) => ({
163
- type: relation.type || null,
164
- sourceField: relation.sourceField || null,
165
- target: relation.target?.id || null
166
- })),
167
- keys: stableSortedStrings((statement.keys || []).map((key) => key.name)),
168
- reviewBoundary: reviewBoundaryForEntityPolicy(statement),
169
- ownership_boundary: defaultOwnershipBoundary()
170
- };
171
- case "widget":
172
- return summarizeComponent(statement);
173
- case "shape":
174
- return {
175
- id: statement.id,
176
- kind: statement.kind,
177
- name: statement.name || statement.id,
178
- description: statement.description || null,
179
- derivedFrom: refIds(statement.derivedFrom),
180
- include: stableSortedStrings(statement.include || []),
181
- exclude: stableSortedStrings(statement.exclude || []),
182
- fields: (statement.projectedFields || statement.fields || []).map(summarizeField),
183
- ownership_boundary: defaultOwnershipBoundary()
184
- };
185
- case "capability":
186
- return {
187
- id: statement.id,
188
- kind: statement.kind,
189
- name: statement.name || statement.id,
190
- description: statement.description || null,
191
- actors: refIds(statement.actors),
192
- roles: refIds(statement.roles),
193
- reads: refIds(statement.reads),
194
- creates: refIds(statement.creates),
195
- updates: refIds(statement.updates),
196
- deletes: refIds(statement.deletes),
197
- input: refIds(statement.input),
198
- output: refIds(statement.output),
199
- reviewBoundary: reviewBoundaryForCapabilityPolicy(statement),
200
- ownership_boundary: defaultOwnershipBoundary()
201
- };
202
- case "rule":
203
- return summarizeRule(statement);
204
- case "projection":
205
- return summarizeProjection(statement);
206
- case "verification":
207
- return summarizeVerification(statement);
208
- case "enum":
209
- return {
210
- id: statement.id,
211
- kind: statement.kind,
212
- name: statement.name || statement.id,
213
- values: stableSortedStrings(statement.values || []),
214
- ownership_boundary: defaultOwnershipBoundary()
215
- };
216
- case "actor":
217
- case "role":
218
- return {
219
- id: statement.id,
220
- kind: statement.kind,
221
- name: statement.name || statement.id,
222
- description: statement.description || null,
223
- ownership_boundary: defaultOwnershipBoundary()
224
- };
225
- default:
226
- return {
227
- id: statement.id,
228
- kind: statement.kind,
229
- name: statement.name || statement.id,
230
- description: statement.description || null,
231
- ownership_boundary: defaultOwnershipBoundary()
232
- };
233
- }
234
- }
235
-
236
- export function reviewBoundaryForCapability(capability) {
237
- return reviewBoundaryForCapabilityPolicy(capability);
238
- }
239
-
240
- export function reviewBoundaryForProjection(projection) {
241
- return reviewBoundaryForProjectionPolicy(projection);
242
- }
243
-
244
- export function reviewBoundaryForEntity(entity) {
245
- return reviewBoundaryForEntityPolicy(entity);
246
- }
247
-
248
- export function graphCounts(graph) {
249
- return Object.fromEntries(
250
- Object.entries(graph.byKind || {})
251
- .sort(([a], [b]) => a.localeCompare(b))
252
- .map(([kind, statements]) => [kind, statements.length])
253
- );
254
- }
255
-
256
- function groupBy(items, keyFn) {
257
- const grouped = {};
258
- for (const item of items) {
259
- const key = keyFn(item);
260
- if (!Object.hasOwn(grouped, key)) {
261
- grouped[key] = [];
262
- }
263
- grouped[key].push(item);
264
- }
265
- return grouped;
266
- }
267
-
268
- export function buildIndexes(graph) {
269
- const statementById = new Map((graph.statements || []).map((statement) => [statement.id, statement]));
270
- const docsById = new Map((graph.docs || []).map((doc) => [doc.id, doc]));
271
- const docsByKind = groupBy(graph.docs || [], (doc) => doc.kind || "unknown");
272
-
273
- return {
274
- statementById,
275
- docsById,
276
- docsByKind
277
- };
278
- }
279
-
280
- function relatedDocs(graph, predicate) {
281
- return (graph.docs || []).filter(predicate);
282
- }
283
-
284
- function verificationsFor(graph, predicate) {
285
- return stableSortedStrings(
286
- (graph.byKind.verification || [])
287
- .filter((verification) => (verification.validates || []).some((target) => predicate(target.id)))
288
- .map((verification) => verification.id)
289
- );
290
- }
291
-
292
- export function relatedJourneysForCapability(graph, capabilityId) {
293
- return relatedDocs(
294
- graph,
295
- (doc) => doc.kind === "journey" && (doc.relatedCapabilities || []).includes(capabilityId)
296
- );
297
- }
298
-
299
- export function relatedWorkflowDocsForCapability(graph, capabilityId) {
300
- return relatedDocs(
301
- graph,
302
- (doc) => doc.kind === "workflow" && (doc.relatedCapabilities || []).includes(capabilityId)
303
- );
304
- }
305
-
306
- export function relatedRulesForTarget(graph, targetId) {
307
- return stableSortedStrings(
308
- (graph.byKind.rule || [])
309
- .filter((rule) => (rule.appliesTo || []).some((target) => target.id === targetId))
310
- .map((rule) => rule.id)
311
- );
312
- }
313
-
314
- export function relatedProjectionsForCapability(graph, capabilityId) {
315
- return stableSortedStrings(
316
- (graph.byKind.projection || [])
317
- .filter((projection) => (projection.realizes || []).some((target) => target.id === capabilityId))
318
- .map((projection) => projection.id)
319
- );
320
- }
321
-
322
- export function relatedCapabilitiesForEntity(graph, entityId) {
323
- return stableSortedStrings(
324
- (graph.byKind.capability || [])
325
- .filter(
326
- (capability) =>
327
- refIds(capability.reads).includes(entityId) ||
328
- refIds(capability.creates).includes(entityId) ||
329
- refIds(capability.updates).includes(entityId) ||
330
- refIds(capability.deletes).includes(entityId)
331
- )
332
- .map((capability) => capability.id)
333
- );
334
- }
335
-
336
- export function relatedShapesForEntity(graph, entityId) {
337
- return stableSortedStrings(
338
- (graph.byKind.shape || [])
339
- .filter((shape) => refIds(shape.derivedFrom).includes(entityId))
340
- .map((shape) => shape.id)
341
- );
342
- }
343
-
344
- export function relatedProjectionsForEntity(graph, entityId) {
345
- return stableSortedStrings(
346
- (graph.byKind.projection || [])
347
- .filter((projection) => {
348
- const dbMatches = (projection.dbTables || []).some((entry) => entry.entity?.id === entityId);
349
- const httpMatches = (projection.http || []).some((entry) => entry.entity?.id === entityId);
350
- return dbMatches || httpMatches;
351
- })
352
- .map((projection) => projection.id)
353
- );
354
- }
355
-
356
- export function relatedCapabilitiesForProjection(projection) {
357
- const ids = [
358
- ...(projection.realizes || []).map((entry) => entry.id),
359
- ...(projection.http || []).map((entry) => entry.capability?.id),
360
- ...(projection.uiActions || []).map((entry) => entry.capability?.id),
361
- ...(projection.uiVisibility || []).map((entry) => entry.capability?.id),
362
- ...(projection.uiLookups || []).map((entry) => entry.capability?.id)
363
- ];
364
- return stableSortedStrings(ids);
365
- }
366
-
367
- export function relatedEntitiesForProjection(projection) {
368
- const ids = [
369
- ...(projection.dbTables || []).map((entry) => entry.entity?.id),
370
- ...(projection.dbColumns || []).map((entry) => entry.entity?.id),
371
- ...(projection.dbRelations || []).map((entry) => entry.source?.id),
372
- ...(projection.dbRelations || []).map((entry) => entry.target?.id)
373
- ];
374
- return stableSortedStrings(ids);
375
- }
376
-
377
- export function relatedShapesForProjection(projection) {
378
- const ids = [
379
- ...(projection.uiScreens || []).map((entry) => entry.viewShape?.id),
380
- ...(projection.uiScreens || []).map((entry) => entry.editShape?.id),
381
- ...(projection.uiCollections || []).map((entry) => entry.itemShape?.id),
382
- ...(projection.httpResponses || []).map((entry) => entry.shape?.id),
383
- ...(projection.http || []).map((entry) => entry.requestShape?.id)
384
- ];
385
- return stableSortedStrings(ids);
386
- }
387
-
388
- export function relatedProjectionsForShape(graph, shapeId) {
389
- const directProjectionIds = stableSortedStrings((graph?.byKind?.projection || [])
390
- .filter((projection) => relatedShapesForProjection(projection).includes(shapeId))
391
- .map((projection) => projection.id));
392
- const viaCapabilities = stableSortedStrings((graph?.byKind?.capability || [])
393
- .filter((capability) => [...(capability.input || []), ...(capability.output || [])].some((item) => item.id === shapeId))
394
- .flatMap((capability) => relatedProjectionsForCapability(graph, capability.id)));
395
-
396
- return stableSortedStrings([...directProjectionIds, ...viaCapabilities]);
397
- }
398
-
399
- export function widgetById(graph, widgetId) {
400
- return (graph?.byKind?.widget || []).find((widget) => widget.id === widgetId) || null;
401
- }
402
-
403
- function projectionById(graph, projectionId) {
404
- return (graph?.byKind?.projection || []).find((projection) => projection.id === projectionId) || null;
405
- }
406
-
407
- function realizedProjectionIds(projection) {
408
- return stableSortedStrings((projection?.realizes || [])
409
- .filter((target) => target.target?.kind === "projection" || String(target.id || "").startsWith("proj_"))
410
- .map((target) => target.id));
411
- }
412
-
413
- function downstreamProjectionIds(graph, projectionIds) {
414
- const visited = new Set(projectionIds);
415
- const queue = [...projectionIds];
416
-
417
- while (queue.length > 0) {
418
- const currentId = queue.shift();
419
- for (const projection of graph?.byKind?.projection || []) {
420
- if (visited.has(projection.id)) {
421
- continue;
422
- }
423
- if (realizedProjectionIds(projection).includes(currentId)) {
424
- visited.add(projection.id);
425
- queue.push(projection.id);
426
- }
427
- }
428
- }
429
-
430
- return stableSortedStrings([...visited]);
431
- }
432
-
433
- export function relatedWidgetsForProjection(graph, projection) {
434
- const directIds = (projection?.widgetBindings || []).map((entry) => entry.widget?.id).filter(Boolean);
435
- const inheritedIds = realizedProjectionIds(projection)
436
- .map((projectionId) => projectionById(graph, projectionId))
437
- .filter(Boolean)
438
- .flatMap((realizedProjection) => relatedWidgetsForProjection(graph, realizedProjection));
439
- return stableSortedStrings([...directIds, ...inheritedIds]);
440
- }
441
-
442
- export function relatedShapesForWidget(widget) {
443
- if (!widget) return [];
444
- const ids = [
445
- ...(widget.events || []).map((event) => event.shape?.id),
446
- ...(widget.lookups || [])
447
- .filter((lookup) => lookup?.target?.kind === "shape" || String(lookup?.id || "").startsWith("shape_"))
448
- .map((lookup) => lookup.id),
449
- ...(widget.dependencies || [])
450
- .filter((dependency) => referenceKind(dependency) === "shape")
451
- .map((dependency) => dependency.id)
452
- ];
453
- return stableSortedStrings(ids);
454
- }
455
-
456
- export function relatedProjectionsForWidget(graph, widgetId) {
457
- const widget = widgetById(graph, widgetId);
458
- if (!widget) return [];
459
- const explicitProjectionIds = stableSortedStrings((graph?.byKind?.projection || [])
460
- .filter((projection) => (projection.widgetBindings || []).some((entry) => entry.widget?.id === widgetId))
461
- .map((projection) => projection.id));
462
- const dependencyProjectionIds = stableSortedStrings((widget.dependencies || []).flatMap((dependency) => {
463
- const kind = referenceKind(dependency);
464
- if (kind === "projection") {
465
- return [dependency.id];
466
- }
467
- if (kind === "capability") {
468
- return relatedProjectionsForCapability(graph, dependency.id);
469
- }
470
- if (kind === "entity") {
471
- return relatedProjectionsForEntity(graph, dependency.id);
472
- }
473
- if (kind === "shape") {
474
- return relatedProjectionsForShape(graph, dependency.id);
475
- }
476
- return [];
477
- }));
478
- const widgetPatterns = new Set(widget.patterns || []);
479
- const widgetRegions = new Set(widget.regions || []);
480
- const viaUiRegions = stableSortedStrings((graph?.byKind?.projection || [])
481
- .filter((projection) => (projection.uiScreenRegions || []).some((region) =>
482
- (region.pattern && widgetPatterns.has(region.pattern)) ||
483
- (region.region && widgetRegions.has(region.region))
484
- ))
485
- .map((projection) => projection.id));
486
-
487
- return downstreamProjectionIds(graph, stableSortedStrings([...explicitProjectionIds, ...dependencyProjectionIds, ...viaUiRegions]));
488
- }
489
-
490
- function referenceKind(reference) {
491
- if (reference?.target?.kind) {
492
- return reference.target.kind;
493
- }
494
- const id = String(reference?.id || "");
495
- const prefix = id.split("_")[0];
496
- if (prefix === "proj") {
497
- return "projection";
498
- }
499
- if (prefix === "cap") {
500
- return "capability";
501
- }
502
- return prefix || null;
503
- }
504
-
505
- export function verificationIdsForTarget(graph, targetIds) {
506
- const set = new Set(targetIds || []);
507
- return verificationsFor(graph, (targetId) => set.has(targetId));
508
- }
509
-
510
- export function summarizeDoc(doc) {
511
- return summarizeJourneyDoc(doc);
512
- }
513
-
514
- export function summarizeById(graph, id) {
515
- const statement = (graph.statements || []).find((item) => item.id === id);
516
- if (statement) {
517
- return summarizeStatement(statement);
518
- }
519
-
520
- const doc = (graph.docs || []).find((item) => item.id === id);
521
- return doc ? summarizeDoc(doc) : null;
522
- }
523
-
524
- export function summarizeStatementsByIds(graph, ids) {
525
- return stableSortedStrings(ids).map((id) => summarizeById(graph, id)).filter(Boolean);
526
- }
527
-
528
- export function summarizeDocsByIds(graph, ids) {
529
- return stableSortedStrings(ids)
530
- .map((id) => (graph.docs || []).find((doc) => doc.id === id))
531
- .filter(Boolean)
532
- .map(summarizeDoc);
533
- }
534
-
535
- export function workspaceInventory(graph) {
536
- return {
537
- capabilities: stableSortedStrings((graph.byKind.capability || []).map((item) => item.id)),
538
- workflows: stableSortedStrings((graph.docs || []).filter((doc) => doc.kind === "workflow").map((doc) => doc.id)),
539
- journeys: stableSortedStrings((graph.docs || []).filter((doc) => doc.kind === "journey").map((doc) => doc.id)),
540
- entities: stableSortedStrings((graph.byKind.entity || []).map((item) => item.id)),
541
- projections: stableSortedStrings((graph.byKind.projection || []).map((item) => item.id)),
542
- widgets: stableSortedStrings((graph.byKind.widget || []).map((item) => item.id)),
543
- verifications: stableSortedStrings((graph.byKind.verification || []).map((item) => item.id)),
544
- domains: stableSortedStrings((graph.byKind.domain || []).map((item) => item.id)),
545
- pitches: stableSortedStrings((graph.byKind.pitch || []).map((item) => item.id)),
546
- requirements: stableSortedStrings((graph.byKind.requirement || []).map((item) => item.id)),
547
- acceptance_criteria: stableSortedStrings((graph.byKind.acceptance_criterion || []).map((item) => item.id)),
548
- tasks: stableSortedStrings((graph.byKind.task || []).map((item) => item.id)),
549
- bugs: stableSortedStrings((graph.byKind.bug || []).map((item) => item.id)),
550
- documents: stableSortedStrings((graph.docs || []).map((doc) => doc.id))
551
- };
552
- }
553
-
554
- export function ensureContextSelection(options = {}) {
555
- const selectors = [
556
- options.capabilityId ? ["capability", options.capabilityId] : null,
557
- options.workflowId ? ["workflow", options.workflowId] : null,
558
- options.projectionId ? ["projection", options.projectionId] : null,
559
- (options.widgetId || options.componentId) ? ["widget", options.widgetId || options.componentId] : null,
560
- options.entityId ? ["entity", options.entityId] : null,
561
- options.journeyId ? ["journey", options.journeyId] : null,
562
- options.surfaceId ? ["surface", options.surfaceId] : null,
563
- options.domainId ? ["domain", options.domainId] : null,
564
- options.pitchId ? ["pitch", options.pitchId] : null,
565
- options.requirementId ? ["requirement", options.requirementId] : null,
566
- options.acceptanceId ? ["acceptance_criterion", options.acceptanceId] : null,
567
- options.taskId ? ["task", options.taskId] : null,
568
- options.bugId ? ["bug", options.bugId] : null,
569
- options.documentId ? ["document", options.documentId] : null
570
- ].filter(Boolean);
571
-
572
- if (selectors.length !== 1) {
573
- throw new Error(
574
- "Context selection requires exactly one of --capability, --workflow, --projection, --widget, --entity, --journey, --surface, --domain, --pitch, --requirement, --acceptance, --task, --bug, or --document"
575
- );
576
- }
577
-
578
- return {
579
- kind: selectors[0][0],
580
- id: selectors[0][1]
581
- };
582
- }
583
-
584
- export function domainById(graph, domainId) {
585
- return (graph?.byKind?.domain || []).find((domain) => domain.id === domainId) || null;
586
- }
587
-
588
- export function summarizeDomain(domain) {
589
- if (!domain) return null;
590
- const members = domain.members || {};
591
- return {
592
- id: domain.id,
593
- kind: "domain",
594
- name: domain.name,
595
- description: domain.description,
596
- status: domain.status,
597
- in_scope: [...(domain.inScope || [])],
598
- out_of_scope: [...(domain.outOfScope || [])],
599
- aliases: [...(domain.aliases || [])],
600
- parent_domain: domain.parentDomain ? domain.parentDomain.id : null,
601
- members_count: Object.values(members).reduce((total, list) => total + list.length, 0)
602
- };
603
- }
604
-
605
- export function domainsByStatement(graph) {
606
- const map = new Map();
607
- for (const statement of (graph?.statements || [])) {
608
- if (statement.resolvedDomain && statement.resolvedDomain.id) {
609
- map.set(statement.id, statement.resolvedDomain.id);
610
- }
611
- }
612
- return map;
613
- }
614
-
615
- export function relatedCapabilitiesForDomain(graph, domainId) {
616
- const domain = domainById(graph, domainId);
617
- if (!domain) return [];
618
- return [...(domain.members?.capabilities || [])].sort();
619
- }
620
-
621
- export function relatedEntitiesForDomain(graph, domainId) {
622
- const domain = domainById(graph, domainId);
623
- if (!domain) return [];
624
- return [...(domain.members?.entities || [])].sort();
625
- }
626
-
627
- export function relatedRulesForDomain(graph, domainId) {
628
- const domain = domainById(graph, domainId);
629
- if (!domain) return [];
630
- return [...(domain.members?.rules || [])].sort();
631
- }
632
-
633
- export function relatedVerificationsForDomain(graph, domainId) {
634
- const domain = domainById(graph, domainId);
635
- if (!domain) return [];
636
- return [...(domain.members?.verifications || [])].sort();
637
- }
638
-
639
- export function relatedProjectionsForDomain(graph, domainId) {
640
- const capabilityIds = new Set(relatedCapabilitiesForDomain(graph, domainId));
641
- if (capabilityIds.size === 0) return [];
642
- const projectionIds = (graph?.byKind?.projection || [])
643
- .filter((projection) => (projection.realizes || []).some((entry) => capabilityIds.has(entry.id)))
644
- .map((projection) => projection.id);
645
- return stableSortedStrings(projectionIds);
646
- }
647
-
648
- // Phase 2 SDLC look-up helpers. Mirror the existing `*ById` pattern.
649
- export function pitchById(graph, id) {
650
- return (graph?.byKind?.pitch || []).find((s) => s.id === id) || null;
651
- }
652
- export function requirementById(graph, id) {
653
- return (graph?.byKind?.requirement || []).find((s) => s.id === id) || null;
654
- }
655
- export function acceptanceCriterionById(graph, id) {
656
- return (graph?.byKind?.acceptance_criterion || []).find((s) => s.id === id) || null;
657
- }
658
- export function taskById(graph, id) {
659
- return (graph?.byKind?.task || []).find((s) => s.id === id) || null;
660
- }
661
- export function bugById(graph, id) {
662
- return (graph?.byKind?.bug || []).find((s) => s.id === id) || null;
663
- }
664
- export function documentById(graph, id) {
665
- return (graph?.docs || []).find((doc) => doc.id === id) || null;
666
- }
667
-
668
- export function summarizePitch(pitch) {
669
- if (!pitch) return null;
670
- return {
671
- id: pitch.id,
672
- name: pitch.name,
673
- status: pitch.status,
674
- priority: pitch.priority,
675
- appetite: pitch.appetite,
676
- domain: pitch.resolvedDomain ? pitch.resolvedDomain.id : null
677
- };
678
- }
679
- export function summarizeRequirement(req) {
680
- if (!req) return null;
681
- return {
682
- id: req.id,
683
- name: req.name,
684
- status: req.status,
685
- priority: req.priority,
686
- pitch: req.pitch?.id || null,
687
- domain: req.resolvedDomain ? req.resolvedDomain.id : null
688
- };
689
- }
690
- export function summarizeAcceptanceCriterion(ac) {
691
- if (!ac) return null;
692
- return {
693
- id: ac.id,
694
- name: ac.name,
695
- status: ac.status,
696
- requirement: ac.requirement?.id || null
697
- };
698
- }
699
- export function summarizeTask(task) {
700
- if (!task) return null;
701
- return {
702
- id: task.id,
703
- name: task.name,
704
- status: task.status,
705
- priority: task.priority,
706
- work_type: task.workType,
707
- claimed_by: (task.claimedBy || []).map((r) => (typeof r === "string" ? r : r?.id)).filter(Boolean),
708
- domain: task.resolvedDomain ? task.resolvedDomain.id : null
709
- };
710
- }
711
- export function summarizeBug(bug) {
712
- if (!bug) return null;
713
- return {
714
- id: bug.id,
715
- name: bug.name,
716
- status: bug.status,
717
- severity: bug.severity,
718
- priority: bug.priority,
719
- domain: bug.resolvedDomain ? bug.resolvedDomain.id : null
720
- };
721
- }
722
- export function summarizeDocument(doc) {
723
- if (!doc) return null;
724
- return {
725
- id: doc.id,
726
- title: doc.title,
727
- kind: doc.kind,
728
- status: doc.status,
729
- domain: doc.domain || null
730
- };
731
- }
732
-
733
- export function getWorkflowDoc(graph, workflowId) {
734
- const doc = (graph.docs || []).find((item) => item.kind === "workflow" && item.id === workflowId);
735
- if (!doc) {
736
- throw new Error(`No workflow doc found with id '${workflowId}'`);
737
- }
738
- return doc;
739
- }
740
-
741
- export function getJourneyDoc(graph, journeyId) {
742
- const doc = (graph.docs || []).find((item) => item.kind === "journey" && item.id === journeyId);
743
- if (!doc) {
744
- throw new Error(`No journey doc found with id '${journeyId}'`);
745
- }
746
- return doc;
747
- }
748
-
749
- export function getStatement(graph, kind, id) {
750
- const statement = (graph.byKind[kind] || []).find((item) => item.id === id);
751
- if (!statement) {
752
- throw new Error(`No ${kind} found with id '${id}'`);
753
- }
754
- return statement;
755
- }
756
-
757
- export function repoRootFromGraph(graph) {
758
- let current = path.resolve(graph.root);
759
- while (true) {
760
- if (fs.existsSync(path.join(current, "examples", "maintained", "proof-app")) && fs.existsSync(path.join(current, "engine"))) {
761
- return current;
762
- }
763
- const parent = path.dirname(current);
764
- if (parent === current) {
765
- if (
766
- fs.existsSync(path.join(bundledRepoRoot, "examples", "maintained", "proof-app")) &&
767
- fs.existsSync(path.join(bundledRepoRoot, "engine"))
768
- ) {
769
- return bundledRepoRoot;
770
- }
771
- return path.resolve(graph.root, "..", "..");
772
- }
773
- current = parent;
774
- }
775
- }
776
-
777
- function workspaceRootFromGraph(graph) {
778
- const root = path.resolve(graph.root);
779
- return path.basename(root) === "topogram" ? path.dirname(root) : root;
780
- }
781
-
782
- function collectMaintainedProofDocPaths(workspaceRoot) {
783
- const candidateDirs = [
784
- path.join(workspaceRoot, "proof"),
785
- path.join(workspaceRoot, "docs", "proof")
786
- ];
787
- const proofFiles = [];
788
-
789
- for (const dirPath of candidateDirs) {
790
- if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {
791
- continue;
792
- }
793
- for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
794
- if (entry.isFile() && /^maintained-.*\.md$/.test(entry.name)) {
795
- proofFiles.push(path.join(dirPath, entry.name));
796
- }
797
- }
798
- }
799
-
800
- return stableSortedStrings(proofFiles);
801
- }
802
-
803
- function readLocalMaintainedProofMetadataFromWorkspace(workspaceRoot) {
804
- return collectMaintainedProofDocPaths(workspaceRoot)
805
- .map((filePath) => parseDocFile(filePath, workspaceRoot))
806
- .filter((doc) => !doc.parseError)
807
- .map((doc) => {
808
- const classification = doc.metadata.classification || null;
809
- const maintainedFiles = stableSortedStrings(doc.metadata.maintained_files || []);
810
- const emittedDependencies = stableSortedStrings(doc.metadata.emitted_dependencies || []);
811
- const humanOwnedSeams = stableSortedStrings(doc.metadata.human_owned_seams || []);
812
-
813
- if (!classification || maintainedFiles.length === 0 || emittedDependencies.length === 0 || humanOwnedSeams.length === 0) {
814
- return null;
815
- }
816
-
817
- return {
818
- classification,
819
- maintainedFiles,
820
- emittedDependencies,
821
- humanOwnedSeams,
822
- seamFamilyId: doc.metadata.seam_family_id || null,
823
- seamFamilyLabel: doc.metadata.seam_family_label || null,
824
- exists: true,
825
- absolutePath: doc.file,
826
- relativePath: doc.relativePath,
827
- reviewBoundary: reviewBoundaryForMaintainedClassification(classification),
828
- ownership_boundary: ownershipBoundaryForMaintainedSurface()
829
- };
830
- })
831
- .filter(Boolean);
832
- }
833
-
834
- export function readLocalMaintainedProofMetadata(graph) {
835
- return readLocalMaintainedProofMetadataFromWorkspace(workspaceRootFromGraph(graph));
836
- }
837
-
838
- export function buildMaintainedBoundaryArtifact({
839
- proofStories = [],
840
- verificationTargets = null,
841
- graph = null
842
- } = {}) {
843
- if (!proofStories.length) {
844
- return null;
845
- }
846
-
847
- const seams = buildMaintainedSeams(proofStories);
848
- const outputs = buildMaintainedOutputs({
849
- seams,
850
- proofStories,
851
- ownershipBoundary: ownershipBoundaryForMaintainedSurface(),
852
- verificationTargets,
853
- graph
854
- });
855
- const maintainedFiles = stableSortedStrings(proofStories.flatMap((item) => item.maintainedFiles || []));
856
- const emittedDependencies = stableSortedStrings(proofStories.flatMap((item) => item.emittedDependencies || []));
857
- const humanOwnedSeams = stableSortedStrings(proofStories.flatMap((item) => item.humanOwnedSeams || []));
858
-
859
- return {
860
- type: "maintained_boundary",
861
- version: 2,
862
- summary: {
863
- focus: "Maintained-app files, emitted constraints, and explicit review boundaries",
864
- maintained_file_count: maintainedFiles.length,
865
- accepted_change_count: proofStories.filter((item) => item.classification === "accepted_change").length,
866
- guarded_change_count: proofStories.filter((item) => item.classification === "guarded_manual_decision").length,
867
- no_go_count: proofStories.filter((item) => item.classification === "no_go").length
868
- },
869
- maintained_files_in_scope: maintainedFiles,
870
- emitted_artifact_dependencies: emittedDependencies,
871
- human_owned_seams: humanOwnedSeams,
872
- outputs,
873
- seams,
874
- proof_stories: proofStories.map((item) => ({
875
- classification: item.classification,
876
- relativePath: item.relativePath,
877
- maintained_files: item.maintainedFiles || [],
878
- emitted_dependencies: item.emittedDependencies || [],
879
- human_owned_seams: item.humanOwnedSeams || [],
880
- seam_family_id: item.seamFamilyId || null,
881
- seam_family_label: item.seamFamilyLabel || null,
882
- review_boundary: item.reviewBoundary,
883
- ownership_boundary: item.ownership_boundary
884
- })),
885
- ownership_boundary: ownershipBoundaryForMaintainedSurface()
886
- };
887
- }
888
-
889
- export function buildLocalMaintainedBoundaryArtifact(workspaceRoot, graph = null) {
890
- const proofStories = readLocalMaintainedProofMetadataFromWorkspace(path.resolve(workspaceRoot));
891
- if (!proofStories.length) {
892
- return null;
893
- }
894
-
895
- const emittedDependencies = stableSortedStrings(proofStories.flatMap((item) => item.emittedDependencies || []));
896
- const verificationTargets = graph
897
- ? recommendedVerificationTargets(graph, emittedDependencies, {
898
- includeMaintainedApp: true,
899
- rationale: "Local maintained proof stories should point agents at emitted dependency checks plus any maintained proof gates."
900
- })
901
- : null;
902
-
903
- return buildMaintainedBoundaryArtifact({
904
- proofStories,
905
- verificationTargets,
906
- graph
907
- });
908
- }
909
-
910
- export function maintainedProofMetadata(graph) {
911
- const repoRoot = repoRootFromGraph(graph);
912
- const staticFiles = [
913
- {
914
- classification: "accepted_change",
915
- path: "examples/maintained/proof-app/proof/issues-ownership-visibility-story.md",
916
- maintainedFiles: ["examples/maintained/proof-app/src/issues.js"],
917
- emittedDependencies: ["proj_web", "proj_api", "journey_issue_resolution_and_closure"],
918
- humanOwnedSeams: ["maintained presenter structure", "detail/list rendering treatment"]
919
- },
920
- {
921
- classification: "accepted_change",
922
- path: "examples/maintained/proof-app/proof/issues-cross-surface-alignment-story.md",
923
- maintainedFiles: ["examples/maintained/proof-app/src/issues.js"],
924
- emittedDependencies: ["proj_web", "proj_api", "journey_issue_creation_and_assignment", "journey_issue_resolution_and_closure"],
925
- humanOwnedSeams: [
926
- "issues detail action state",
927
- "issues list/card summary state",
928
- "issues route and action metadata"
929
- ],
930
- seamFamilyId: "issues_cross_surface_alignment",
931
- seamFamilyLabel: "issues cross-surface ownership alignment"
932
- },
933
- {
934
- classification: "guarded_manual_decision",
935
- path: "examples/maintained/proof-app/proof/content-approval-workflow-decision-story.md",
936
- maintainedFiles: ["examples/maintained/proof-app/src/content-approval-change-guards.js"],
937
- emittedDependencies: ["cap_request_article_revision", "journey_editorial_review_and_revision"],
938
- humanOwnedSeams: ["new workflow affordance treatment", "action placement and copy"]
939
- },
940
- {
941
- classification: "no_go",
942
- path: "examples/maintained/proof-app/proof/issues-ownership-visibility-drift-story.md",
943
- maintainedFiles: ["examples/maintained/proof-app/src/issues.js"],
944
- emittedDependencies: ["proj_web", "journey_issue_resolution_and_closure"],
945
- humanOwnedSeams: ["owner visibility semantics must not drift"]
946
- },
947
- {
948
- classification: "no_go",
949
- path: "examples/maintained/proof-app/proof/content-approval-unsupported-change-story.md",
950
- maintainedFiles: ["examples/maintained/proof-app/src/content-approval.js", "examples/maintained/proof-app/src/content-approval-change-guards.js"],
951
- emittedDependencies: ["proj_web", "proj_api", "proj_db"],
952
- humanOwnedSeams: ["unsupported relation and workflow meaning changes"]
953
- },
954
- {
955
- classification: "no_go",
956
- path: "examples/maintained/proof-app/proof/todo-project-owner-unsupported-change-story.md",
957
- maintainedFiles: ["examples/maintained/proof-app/src/todo-change-guards.js"],
958
- emittedDependencies: ["entity_project", "proj_web", "proj_api"],
959
- humanOwnedSeams: ["ownership retargeting remains manual"]
960
- },
961
- {
962
- classification: "independent_review",
963
- path: "examples/maintained/proof-app/proof/maintained-contract-review.md",
964
- maintainedFiles: [
965
- "examples/maintained/proof-app/src/issues.js",
966
- "examples/maintained/proof-app/src/content-approval-change-guards.js",
967
- "examples/maintained/proof-app/src/todo-change-guards.js"
968
- ],
969
- emittedDependencies: ["maintained-proof-package"],
970
- humanOwnedSeams: ["human audit of emitted contracts vs maintained code"]
971
- },
972
- {
973
- classification: "accepted_change",
974
- path: "examples/generated/content-approval/implementation/proof/web-reference-seam-story.md",
975
- maintainedFiles: ["examples/generated/content-approval/implementation/web/reference.js"],
976
- emittedDependencies: ["proj_web", "journey_editorial_review_and_revision"],
977
- humanOwnedSeams: ["example web reference composition"]
978
- },
979
- {
980
- classification: "accepted_change",
981
- path: "examples/generated/content-approval/implementation/proof/backend-reference-seam-story.md",
982
- maintainedFiles: ["examples/generated/content-approval/implementation/backend/reference.js"],
983
- emittedDependencies: ["proj_api", "proj_db"],
984
- humanOwnedSeams: ["example backend reference integration"]
985
- }
986
- ];
987
-
988
- const staticProofs = staticFiles
989
- .map((item) => {
990
- const absolutePath = path.join(repoRoot, item.path);
991
- return {
992
- ...item,
993
- exists: fs.existsSync(absolutePath),
994
- absolutePath,
995
- relativePath: item.path,
996
- reviewBoundary: reviewBoundaryForMaintainedClassification(item.classification),
997
- ownership_boundary: ownershipBoundaryForMaintainedSurface()
998
- };
999
- })
1000
- .filter((item) => item.exists);
1001
-
1002
- return [
1003
- ...staticProofs,
1004
- ...readLocalMaintainedProofMetadata(graph)
1005
- ];
1006
- }
1007
-
1008
- function maintainedSeamKind(label) {
1009
- const normalized = String(label || "").toLowerCase();
1010
- if (/audit|contract|review/.test(normalized)) {
1011
- return "verification_harness";
1012
- }
1013
- if (/workflow|affordance|action|copy/.test(normalized)) {
1014
- return "workflow_affordance";
1015
- }
1016
- if (/visibility|ownership|relation|policy/.test(normalized)) {
1017
- return "policy_interpretation";
1018
- }
1019
- if (/route|navigation/.test(normalized)) {
1020
- return "route_glue";
1021
- }
1022
- return "ui_presenter";
1023
- }
1024
-
1025
- function maintainedSeamStatus(reviewBoundary) {
1026
- const automationClass = reviewBoundary?.automation_class || "review_required";
1027
- return automationClass === "safe" ? "aligned" : automationClass;
1028
- }
1029
-
1030
- function maintainedSeamOwnershipClass(classification) {
1031
- if (classification === "accepted_change") {
1032
- return "contract_bound";
1033
- }
1034
- if (classification === "guarded_manual_decision" || classification === "independent_review") {
1035
- return "advisory_only";
1036
- }
1037
- if (classification === "no_go") {
1038
- return "out_of_bounds";
1039
- }
1040
- return "contract_bound";
1041
- }
1042
-
1043
- function maintainedSeamAllowedChangeClasses(classification) {
1044
- if (classification === "accepted_change") {
1045
- return ["safe", "review_required"];
1046
- }
1047
- if (classification === "guarded_manual_decision") {
1048
- return ["review_required", "manual_decision"];
1049
- }
1050
- if (classification === "independent_review") {
1051
- return ["review_required"];
1052
- }
1053
- if (classification === "no_go") {
1054
- return ["no_go"];
1055
- }
1056
- return ["review_required"];
1057
- }
1058
-
1059
- function maintainedSeamDriftSignals(emittedDependencies = []) {
1060
- const signals = new Set();
1061
- for (const dependency of emittedDependencies || []) {
1062
- if (dependency === "maintained-proof-package") {
1063
- signals.add("verification_expectation_changed");
1064
- continue;
1065
- }
1066
- if (dependency.startsWith("journey_") || dependency.startsWith("workflow_")) {
1067
- signals.add("workflow_state_changed");
1068
- continue;
1069
- }
1070
- if (dependency.startsWith("proj_ui") || dependency === "proj_web") {
1071
- signals.add("emitted_contract_changed");
1072
- signals.add("route_or_navigation_changed");
1073
- continue;
1074
- }
1075
- if (
1076
- dependency.startsWith("proj_") ||
1077
- dependency.startsWith("cap_") ||
1078
- dependency.startsWith("entity_") ||
1079
- dependency.startsWith("shape_")
1080
- ) {
1081
- signals.add("emitted_contract_changed");
1082
- }
1083
- }
1084
- return stableSortedStrings([...signals]);
1085
- }
1086
-
1087
- function maintainedOutputDescriptor(filePaths = []) {
1088
- const files = stableSortedStrings(filePaths);
1089
- if (files.some((file) => String(file).startsWith("examples/maintained/proof-app/"))) {
1090
- return {
1091
- output_id: "maintained_app",
1092
- label: "Maintained App",
1093
- kind: "maintained_runtime",
1094
- root_paths: ["examples/maintained/proof-app/**"]
1095
- };
1096
- }
1097
- const exampleImplementationMatch = files
1098
- .map((file) => String(file).match(/^examples\/(?:(generated)\/)?([^/]+)\/implementation\/(web|backend|runtime)\//))
1099
- .find(Boolean);
1100
- if (exampleImplementationMatch) {
1101
- const [, category, slug, outputKind] = exampleImplementationMatch;
1102
- const outputId = `output_${seamIdHint(`examples_${slug}_${outputKind}`)}`;
1103
- const rootPrefix = category ? `examples/${category}/${slug}` : `examples/${slug}`;
1104
- const root = `${rootPrefix}/implementation/${outputKind}`;
1105
- return {
1106
- output_id: outputId,
1107
- label: `${titleCaseWords(slug)} ${titleCaseWords(outputKind)} Reference`,
1108
- kind: outputKind === "web" ? "web_app" : outputKind === "backend" ? "backend_adapter" : "maintained_runtime",
1109
- root_paths: [`${root}/**`]
1110
- };
1111
- }
1112
- if (files.some((file) => String(file).startsWith("src/"))) {
1113
- return {
1114
- output_id: "output_src",
1115
- label: "Source",
1116
- kind: "backend_adapter",
1117
- root_paths: ["src/**"]
1118
- };
1119
- }
1120
-
1121
- const firstFile = files[0] || "";
1122
- const segments = String(firstFile).split("/").filter(Boolean);
1123
- const root = segments.length >= 2 ? segments.slice(0, 2).join("/") : (segments[0] || "maintained");
1124
- const outputStem = seamIdHint(root);
1125
- return {
1126
- output_id: `output_${outputStem}`,
1127
- label: titleCaseWords(root.replaceAll("/", "_")),
1128
- kind: "maintained_runtime",
1129
- root_paths: [`${root}/**`]
1130
- };
1131
- }
1132
-
1133
- function mergeMaintainedSeam(existing, next) {
1134
- if (!existing) {
1135
- return {
1136
- ...next,
1137
- seam_family_id: next.seam_family_id || null,
1138
- seam_family_label: next.seam_family_label || null,
1139
- maintained_modules: stableSortedStrings(next.maintained_modules || []),
1140
- emitted_dependencies: stableSortedStrings(next.emitted_dependencies || []),
1141
- human_owned_aspects: stableSortedStrings(next.human_owned_aspects || []),
1142
- allowed_change_classes: stableSortedStrings(next.allowed_change_classes || []),
1143
- drift_signals: stableSortedStrings(next.drift_signals || []),
1144
- proof_stories: [...(next.proof_stories || [])]
1145
- };
1146
- }
1147
-
1148
- const statusPriority = ["aligned", "review_required", "manual_decision", "no_go"];
1149
- const ownershipPriority = ["engine_owned", "contract_bound", "advisory_only", "out_of_bounds"];
1150
-
1151
- const currentStatus = statusPriority.indexOf(existing.status);
1152
- const nextStatus = statusPriority.indexOf(next.status);
1153
- const currentOwnership = ownershipPriority.indexOf(existing.ownership_class);
1154
- const nextOwnership = ownershipPriority.indexOf(next.ownership_class);
1155
-
1156
- return {
1157
- ...existing,
1158
- kind: existing.kind || next.kind,
1159
- seam_family_id: existing.seam_family_id || next.seam_family_id || null,
1160
- seam_family_label: existing.seam_family_label || next.seam_family_label || null,
1161
- status: nextStatus > currentStatus ? next.status : existing.status,
1162
- ownership_class: nextOwnership > currentOwnership ? next.ownership_class : existing.ownership_class,
1163
- maintained_modules: stableSortedStrings([...(existing.maintained_modules || []), ...(next.maintained_modules || [])]),
1164
- emitted_dependencies: stableSortedStrings([...(existing.emitted_dependencies || []), ...(next.emitted_dependencies || [])]),
1165
- human_owned_aspects: stableSortedStrings([...(existing.human_owned_aspects || []), ...(next.human_owned_aspects || [])]),
1166
- allowed_change_classes: stableSortedStrings([...(existing.allowed_change_classes || []), ...(next.allowed_change_classes || [])]),
1167
- drift_signals: stableSortedStrings([...(existing.drift_signals || []), ...(next.drift_signals || [])]),
1168
- proof_stories: stableSortedStrings([
1169
- ...(existing.proof_stories || []).map((item) => JSON.stringify(item)),
1170
- ...(next.proof_stories || []).map((item) => JSON.stringify(item))
1171
- ]).map((item) => JSON.parse(item))
1172
- };
1173
- }
1174
-
1175
- export function buildMaintainedSeams(proofStories = []) {
1176
- const seams = new Map();
1177
-
1178
- for (const story of proofStories || []) {
1179
- for (const seamLabel of story.humanOwnedSeams || story.human_owned_seams || []) {
1180
- const output = maintainedOutputDescriptor(story.maintainedFiles || story.maintained_files || []);
1181
- const seam = {
1182
- seam_id: `seam_${seamIdHint(seamLabel)}`,
1183
- seam_family_id: story.seamFamilyId || story.seam_family_id || null,
1184
- seam_family_label: story.seamFamilyLabel || story.seam_family_label || null,
1185
- output_id: output.output_id,
1186
- label: seamLabel,
1187
- kind: maintainedSeamKind(seamLabel),
1188
- ownership_class: maintainedSeamOwnershipClass(story.classification),
1189
- status: maintainedSeamStatus(story.reviewBoundary || story.review_boundary),
1190
- maintained_modules: story.maintainedFiles || story.maintained_files || [],
1191
- emitted_dependencies: story.emittedDependencies || story.emitted_dependencies || [],
1192
- human_owned_aspects: [seamLabel],
1193
- allowed_change_classes: maintainedSeamAllowedChangeClasses(story.classification),
1194
- drift_signals: maintainedSeamDriftSignals(story.emittedDependencies || story.emitted_dependencies || []),
1195
- proof_stories: [
1196
- {
1197
- classification: story.classification || null,
1198
- relativePath: story.relativePath || story.relative_path || null,
1199
- review_boundary: story.reviewBoundary || story.review_boundary || null
1200
- }
1201
- ]
1202
- };
1203
- seams.set(seam.seam_id, mergeMaintainedSeam(seams.get(seam.seam_id), seam));
1204
- }
1205
- }
1206
-
1207
- return [...seams.values()].sort((a, b) => a.seam_id.localeCompare(b.seam_id));
1208
- }
1209
-
1210
- export function buildMaintainedOutputs({
1211
- seams = [],
1212
- proofStories = [],
1213
- ownershipBoundary = ownershipBoundaryForMaintainedSurface(),
1214
- verificationTargets = null,
1215
- graph = null
1216
- } = {}) {
1217
- const outputs = new Map();
1218
-
1219
- for (const seam of seams || []) {
1220
- const descriptor = maintainedOutputDescriptor(seam.maintained_modules || []);
1221
- const outputId = seam.output_id || descriptor.output_id;
1222
- const existing = outputs.get(outputId) || {
1223
- output_id: outputId,
1224
- label: descriptor.label,
1225
- kind: descriptor.kind,
1226
- root_paths: descriptor.root_paths,
1227
- ownership_boundary: ownershipBoundary,
1228
- verification_targets: verificationTargets || null,
1229
- maintained_files_in_scope: [],
1230
- human_owned_seams: [],
1231
- seams: [],
1232
- proof_stories: []
1233
- };
1234
-
1235
- existing.maintained_files_in_scope = stableSortedStrings([
1236
- ...existing.maintained_files_in_scope,
1237
- ...(seam.maintained_modules || [])
1238
- ]);
1239
- existing.human_owned_seams = stableSortedStrings([
1240
- ...existing.human_owned_seams,
1241
- seam.label
1242
- ]);
1243
- existing.seams.push({
1244
- ...seam,
1245
- output_id: outputId
1246
- });
1247
- outputs.set(outputId, existing);
1248
- }
1249
-
1250
- for (const story of proofStories || []) {
1251
- const descriptor = maintainedOutputDescriptor(story.maintainedFiles || story.maintained_files || []);
1252
- const outputId = descriptor.output_id;
1253
- const existing = outputs.get(outputId) || {
1254
- output_id: outputId,
1255
- label: descriptor.label,
1256
- kind: descriptor.kind,
1257
- root_paths: descriptor.root_paths,
1258
- ownership_boundary: ownershipBoundary,
1259
- verification_targets: verificationTargets || null,
1260
- maintained_files_in_scope: [],
1261
- human_owned_seams: [],
1262
- seams: [],
1263
- proof_stories: []
1264
- };
1265
-
1266
- existing.maintained_files_in_scope = stableSortedStrings([
1267
- ...existing.maintained_files_in_scope,
1268
- ...(story.maintainedFiles || story.maintained_files || [])
1269
- ]);
1270
- existing.human_owned_seams = stableSortedStrings([
1271
- ...existing.human_owned_seams,
1272
- ...(story.humanOwnedSeams || story.human_owned_seams || [])
1273
- ]);
1274
- existing.proof_stories.push(story);
1275
- outputs.set(outputId, existing);
1276
- }
1277
-
1278
- const verificationTargetsForOutput = (output) => {
1279
- if (!graph) {
1280
- return output.verification_targets || verificationTargets || null;
1281
- }
1282
-
1283
- const emittedDependencies = stableSortedStrings([
1284
- ...output.seams.flatMap((seam) => seam.emitted_dependencies || []),
1285
- ...output.proof_stories.flatMap((story) => story.emittedDependencies || story.emitted_dependencies || [])
1286
- ]);
1287
- const verificationIds = stableSortedStrings(
1288
- emittedDependencies.filter((id) => String(id).startsWith("ver_"))
1289
- );
1290
- const generatedDependencyTargets = emittedDependencies.filter((id) => !String(id).startsWith("ver_"));
1291
- const targetIds = stableSortedStrings([...verificationIds, ...generatedDependencyTargets]);
1292
- const includeMaintainedApp = output.kind === "maintained_runtime" || output.kind === "web_app" || output.kind === "mobile_app";
1293
-
1294
- const routedTargets = recommendedVerificationTargets(graph, targetIds, {
1295
- includeMaintainedApp,
1296
- rationale: `${output.label || output.output_id || "Maintained output"} should run the smallest verification set tied to its own emitted dependencies and maintained seams.`
1297
- });
1298
-
1299
- return {
1300
- ...routedTargets,
1301
- verification_ids: stableSortedStrings([
1302
- ...(routedTargets.verification_ids || []),
1303
- ...(verificationTargets?.verification_ids || []).filter((id) => verificationIds.includes(id))
1304
- ]),
1305
- generated_checks: stableSortedStrings([
1306
- ...(routedTargets.generated_checks || []),
1307
- ...(verificationTargets?.generated_checks || []).filter((check) => routedTargets.generated_checks?.includes(check))
1308
- ]),
1309
- maintained_app_checks: includeMaintainedApp
1310
- ? stableSortedStrings([
1311
- ...(routedTargets.maintained_app_checks || []),
1312
- ...(verificationTargets?.maintained_app_checks || [])
1313
- ])
1314
- : []
1315
- };
1316
- };
1317
-
1318
- return [...outputs.values()]
1319
- .map((output) => ({
1320
- ...output,
1321
- verification_targets: verificationTargetsForOutput(output),
1322
- write_scope: graph ? buildMaintainedWriteScope(graph, output.maintained_files_in_scope) : null,
1323
- seams: [...output.seams].sort((a, b) => String(a.seam_id || "").localeCompare(String(b.seam_id || ""))),
1324
- proof_stories: [...output.proof_stories]
1325
- }))
1326
- .sort((a, b) => a.output_id.localeCompare(b.output_id));
1327
- }
1328
-
1329
- export function relativePathFromGraph(graph, targetPath) {
1330
- return path.relative(repoRootFromGraph(graph), targetPath);
1331
- }
1332
-
1333
- export function jsonByteSize(value) {
1334
- return Buffer.byteLength(JSON.stringify(value));
1335
- }
1336
-
1337
- export function jsonLineCount(value) {
1338
- return JSON.stringify(value, null, 2).split("\n").length;
1339
- }
1340
-
1341
- export function percentOf(part, whole) {
1342
- if (!whole) {
1343
- return 0;
1344
- }
1345
- return Number(((part / whole) * 100).toFixed(2));
1346
- }
1347
-
1348
- export function buildDefaultWriteScope() {
1349
- return {
1350
- safe_to_edit: ["topogram/**", "candidates/**"],
1351
- generator_owned: ["artifacts/**", "apps/**"],
1352
- human_owned_review_required: ["examples/maintained/proof-app/**"],
1353
- out_of_bounds: [".git/**", "node_modules/**"]
1354
- };
1355
- }
1356
-
1357
- export function buildMaintainedWriteScope(graph, maintainedFiles = []) {
1358
- return {
1359
- safe_to_edit: stableSortedStrings(maintainedFiles),
1360
- generator_owned: ["artifacts/**", "apps/**"],
1361
- human_owned_review_required: stableSortedStrings([
1362
- ...maintainedFiles,
1363
- "examples/maintained/proof-app/**"
1364
- ]),
1365
- out_of_bounds: ["topogram/**"]
1366
- };
1367
- }
1368
-
1369
- export function recommendedVerificationTargets(graph, targetIds = [], options = {}) {
1370
- const verificationIds = verificationIdsForTarget(graph, targetIds);
1371
- const base = {
1372
- verification_ids: verificationIds,
1373
- generated_checks: verificationIds.length > 0 ? ["compile-check", "runtime-check"] : ["compile-check"],
1374
- maintained_app_checks: [],
1375
- rationale: options.rationale || null
1376
- };
1377
-
1378
- if (options.includeMaintainedApp) {
1379
- base.maintained_app_checks = [
1380
- "examples/maintained/proof-app/scripts/compile-check.mjs",
1381
- "examples/maintained/proof-app/scripts/smoke.mjs",
1382
- "examples/maintained/proof-app/scripts/runtime-check.mjs"
1383
- ];
1384
- }
1385
-
1386
- return base;
1387
- }
1
+ // @ts-check
1388
2
 
1389
3
  export {
1390
4
  docIds,
1391
5
  refIds,
1392
- stableSortedStrings,
6
+ stableSortedStrings
7
+ } from "./shared/primitives.js";
8
+ export {
1393
9
  summarizeProjection,
1394
10
  summarizeRule,
1395
11
  summarizeStatement,
1396
- summarizeVerification
1397
- };
12
+ summarizeVerification,
13
+ reviewBoundaryForCapability,
14
+ reviewBoundaryForProjection,
15
+ reviewBoundaryForEntity,
16
+ graphCounts
17
+ } from "./shared/summaries.js";
18
+ export {
19
+ summarizeDoc,
20
+ summarizeById,
21
+ summarizeStatementsByIds,
22
+ summarizeDocsByIds,
23
+ workspaceInventory
24
+ } from "./shared/relationships.js";
25
+ export {
26
+ relatedJourneysForCapability,
27
+ relatedWorkflowDocsForCapability,
28
+ relatedRulesForTarget,
29
+ relatedProjectionsForCapability,
30
+ relatedCapabilitiesForEntity,
31
+ relatedShapesForEntity,
32
+ relatedProjectionsForEntity,
33
+ relatedCapabilitiesForProjection,
34
+ relatedEntitiesForProjection,
35
+ relatedShapesForProjection,
36
+ relatedProjectionsForShape,
37
+ widgetById,
38
+ relatedWidgetsForProjection,
39
+ relatedShapesForWidget,
40
+ relatedProjectionsForWidget,
41
+ verificationIdsForTarget,
42
+ ensureContextSelection,
43
+ buildIndexes
44
+ } from "./shared/relationships.js";
45
+ export {
46
+ domainById,
47
+ summarizeDomain,
48
+ domainsByStatement,
49
+ relatedCapabilitiesForDomain,
50
+ relatedEntitiesForDomain,
51
+ relatedRulesForDomain,
52
+ relatedVerificationsForDomain,
53
+ relatedProjectionsForDomain,
54
+ pitchById,
55
+ requirementById,
56
+ acceptanceCriterionById,
57
+ taskById,
58
+ bugById,
59
+ documentById,
60
+ summarizePitch,
61
+ summarizeRequirement,
62
+ summarizeAcceptanceCriterion,
63
+ summarizeTask,
64
+ summarizeBug,
65
+ summarizeDocument,
66
+ getWorkflowDoc,
67
+ getJourneyDoc,
68
+ getStatement
69
+ } from "./shared/domain-sdlc.js";
70
+ export {
71
+ repoRootFromGraph,
72
+ readLocalMaintainedProofMetadata,
73
+ buildMaintainedBoundaryArtifact,
74
+ buildLocalMaintainedBoundaryArtifact,
75
+ maintainedProofMetadata,
76
+ buildMaintainedSeams,
77
+ buildMaintainedOutputs,
78
+ relativePathFromGraph
79
+ } from "./shared/maintained-boundary.js";
80
+ export {
81
+ jsonByteSize,
82
+ jsonLineCount,
83
+ percentOf,
84
+ buildDefaultWriteScope,
85
+ buildMaintainedWriteScope,
86
+ recommendedVerificationTargets
87
+ } from "./shared/metrics.js";