@documentation-robotics/cli 0.1.13 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/README.md +74 -2
  2. package/dist/analysis/relationship-classifier.js +2 -9
  3. package/dist/analysis/relationship-classifier.js.map +2 -2
  4. package/dist/analyzers/call-graph-utils.js +2 -2
  5. package/dist/analyzers/call-graph-utils.js.map +2 -2
  6. package/dist/analyzers/cards/build.js +171 -0
  7. package/dist/analyzers/cards/build.js.map +7 -0
  8. package/dist/analyzers/cards/classify.js +42 -0
  9. package/dist/analyzers/cards/classify.js.map +7 -0
  10. package/dist/analyzers/cards/edge-index.js +44 -0
  11. package/dist/analyzers/cards/edge-index.js.map +7 -0
  12. package/dist/analyzers/cards/facts.js +46 -0
  13. package/dist/analyzers/cards/facts.js.map +7 -0
  14. package/dist/analyzers/cards/graph.js +42 -0
  15. package/dist/analyzers/cards/graph.js.map +7 -0
  16. package/dist/analyzers/cards/index.js +8 -0
  17. package/dist/analyzers/cards/index.js.map +7 -0
  18. package/dist/analyzers/cards/tier0.js +18 -0
  19. package/dist/analyzers/cards/tier0.js.map +7 -0
  20. package/dist/analyzers/cards/types.js +1 -0
  21. package/dist/analyzers/cards/types.js.map +7 -0
  22. package/dist/analyzers/cbm-analyzer.js +195 -171
  23. package/dist/analyzers/cbm-analyzer.js.map +2 -2
  24. package/dist/analyzers/cbm-responses.js +336 -0
  25. package/dist/analyzers/cbm-responses.js.map +7 -0
  26. package/dist/analyzers/edge-relationships.js +45 -0
  27. package/dist/analyzers/edge-relationships.js.map +7 -0
  28. package/dist/analyzers/mapping-loader.js +9 -0
  29. package/dist/analyzers/mapping-loader.js.map +2 -2
  30. package/dist/analyzers/tier0-rules.js +153 -0
  31. package/dist/analyzers/tier0-rules.js.map +7 -0
  32. package/dist/analyzers/types.js.map +1 -1
  33. package/dist/analyzers/verify-engine.js +22 -19
  34. package/dist/analyzers/verify-engine.js.map +2 -2
  35. package/dist/audit/relationships/ai/prompts.js +10 -10
  36. package/dist/audit/relationships/ai/prompts.js.map +2 -2
  37. package/dist/audit/relationships/analysis/layer-templates.js +52 -52
  38. package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
  39. package/dist/cli.js +112 -28
  40. package/dist/cli.js.map +3 -3
  41. package/dist/coding-agents/agents/claude-code.js +3 -1
  42. package/dist/coding-agents/agents/claude-code.js.map +2 -2
  43. package/dist/coding-agents/claude-code-client.js +2 -1
  44. package/dist/coding-agents/claude-code-client.js.map +2 -2
  45. package/dist/coding-agents/copilot-client.js +17 -12
  46. package/dist/coding-agents/copilot-client.js.map +2 -2
  47. package/dist/coding-agents/default-permissions.js +152 -0
  48. package/dist/coding-agents/default-permissions.js.map +7 -0
  49. package/dist/coding-agents/drbot-mcp.js +167 -0
  50. package/dist/coding-agents/drbot-mcp.js.map +7 -0
  51. package/dist/commands/add.js +12 -7
  52. package/dist/commands/add.js.map +2 -2
  53. package/dist/commands/analyzer.js +134 -23
  54. package/dist/commands/analyzer.js.map +2 -2
  55. package/dist/commands/audit.js +6 -6
  56. package/dist/commands/audit.js.map +2 -2
  57. package/dist/commands/changeset-verify.js +55 -0
  58. package/dist/commands/changeset-verify.js.map +7 -0
  59. package/dist/commands/changeset.js +261 -147
  60. package/dist/commands/changeset.js.map +2 -2
  61. package/dist/commands/delete.js +52 -19
  62. package/dist/commands/delete.js.map +3 -3
  63. package/dist/commands/element.js +3 -2
  64. package/dist/commands/element.js.map +2 -2
  65. package/dist/commands/farm.js +773 -0
  66. package/dist/commands/farm.js.map +7 -0
  67. package/dist/commands/init.js +11 -1
  68. package/dist/commands/init.js.map +2 -2
  69. package/dist/commands/mcp.js +122 -0
  70. package/dist/commands/mcp.js.map +7 -0
  71. package/dist/commands/relationship.js +251 -47
  72. package/dist/commands/relationship.js.map +2 -2
  73. package/dist/commands/reports.js +1 -1
  74. package/dist/commands/reports.js.map +2 -2
  75. package/dist/commands/update.js +56 -24
  76. package/dist/commands/update.js.map +2 -2
  77. package/dist/commands/upgrade.js +99 -11
  78. package/dist/commands/upgrade.js.map +3 -3
  79. package/dist/commands/validate.js +41 -3
  80. package/dist/commands/validate.js.map +2 -2
  81. package/dist/commands/version.js +2 -2
  82. package/dist/commands/version.js.map +2 -2
  83. package/dist/commands/visualize.js +4 -3
  84. package/dist/commands/visualize.js.map +2 -2
  85. package/dist/config.js +83 -0
  86. package/dist/config.js.map +7 -0
  87. package/dist/core/annotation-store.js +138 -0
  88. package/dist/core/annotation-store.js.map +7 -0
  89. package/dist/core/catalog-migration.js +33 -0
  90. package/dist/core/catalog-migration.js.map +7 -0
  91. package/dist/core/changeset-analysis.js +421 -0
  92. package/dist/core/changeset-analysis.js.map +7 -0
  93. package/dist/core/changeset-base-index.js +153 -0
  94. package/dist/core/changeset-base-index.js.map +7 -0
  95. package/dist/core/changeset-brief-delta.js +102 -0
  96. package/dist/core/changeset-brief-delta.js.map +7 -0
  97. package/dist/core/changeset-graph.js +281 -0
  98. package/dist/core/changeset-graph.js.map +7 -0
  99. package/dist/core/changeset-only-guard.js +32 -0
  100. package/dist/core/changeset-only-guard.js.map +7 -0
  101. package/dist/core/changeset-summary.js +68 -0
  102. package/dist/core/changeset-summary.js.map +7 -0
  103. package/dist/core/changeset-validator.js +66 -20
  104. package/dist/core/changeset-validator.js.map +2 -2
  105. package/dist/core/changeset-verify.js +518 -0
  106. package/dist/core/changeset-verify.js.map +7 -0
  107. package/dist/core/changeset.js +56 -9
  108. package/dist/core/changeset.js.map +2 -2
  109. package/dist/core/element-type-migration.js +145 -0
  110. package/dist/core/element-type-migration.js.map +7 -0
  111. package/dist/core/farm-manifest.js +140 -0
  112. package/dist/core/farm-manifest.js.map +7 -0
  113. package/dist/core/farm-sync-engine.js +346 -0
  114. package/dist/core/farm-sync-engine.js.map +7 -0
  115. package/dist/core/farm-sync-state.js +167 -0
  116. package/dist/core/farm-sync-state.js.map +7 -0
  117. package/dist/core/manifest.js +6 -3
  118. package/dist/core/manifest.js.map +2 -2
  119. package/dist/core/migration-registry.js +84 -1
  120. package/dist/core/migration-registry.js.map +2 -2
  121. package/dist/core/model-upgrade.js +102 -0
  122. package/dist/core/model-upgrade.js.map +7 -0
  123. package/dist/core/model.js +257 -44
  124. package/dist/core/model.js.map +3 -3
  125. package/dist/core/mutation-handler.js +71 -6
  126. package/dist/core/mutation-handler.js.map +2 -2
  127. package/dist/core/relationship-migration-table.js +6639 -0
  128. package/dist/core/relationship-migration-table.js.map +7 -0
  129. package/dist/core/relationship-migration.js +272 -0
  130. package/dist/core/relationship-migration.js.map +7 -0
  131. package/dist/core/relationship-prune.js +49 -0
  132. package/dist/core/relationship-prune.js.map +7 -0
  133. package/dist/core/relationship-staging.js +68 -0
  134. package/dist/core/relationship-staging.js.map +7 -0
  135. package/dist/core/relationships.js +26 -4
  136. package/dist/core/relationships.js.map +2 -2
  137. package/dist/core/report-data-model.js +7 -13
  138. package/dist/core/report-data-model.js.map +2 -2
  139. package/dist/core/staged-changeset-storage.js +64 -17
  140. package/dist/core/staged-changeset-storage.js.map +2 -2
  141. package/dist/core/staged-source-reference.js +28 -0
  142. package/dist/core/staged-source-reference.js.map +7 -0
  143. package/dist/core/staging-area.js +83 -18
  144. package/dist/core/staging-area.js.map +2 -2
  145. package/dist/core/virtual-projection.js +14 -5
  146. package/dist/core/virtual-projection.js.map +2 -2
  147. package/dist/export/archimate-exporter.js +1 -0
  148. package/dist/export/archimate-exporter.js.map +2 -2
  149. package/dist/generated/compiled-validators.js +2 -2
  150. package/dist/generated/compiled-validators.js.map +2 -2
  151. package/dist/generated/layer-registry.js +16 -16
  152. package/dist/generated/layer-registry.js.map +2 -2
  153. package/dist/generated/node-types.js +186 -168
  154. package/dist/generated/node-types.js.map +2 -2
  155. package/dist/generated/relationship-index.js +2209 -7727
  156. package/dist/generated/relationship-index.js.map +2 -2
  157. package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
  158. package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
  159. package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
  160. package/dist/integrations/claude_code/commands/dr-map.md +13 -0
  161. package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
  162. package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
  163. package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
  164. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
  165. package/dist/integrations/claude_code/docs/MCP_SERVER.md +79 -0
  166. package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
  167. package/dist/integrations/claude_code/docs/README.md +52 -1
  168. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
  169. package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
  170. package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
  171. package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
  172. package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
  173. package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
  174. package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
  175. package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
  176. package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
  177. package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
  178. package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
  179. package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
  180. package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
  181. package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
  182. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
  183. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
  184. package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
  185. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
  186. package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
  187. package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
  188. package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
  189. package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
  190. package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
  191. package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
  192. package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
  193. package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
  194. package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
  195. package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
  196. package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
  197. package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
  198. package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
  199. package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
  200. package/dist/integrations/hash-utils.js.map +2 -2
  201. package/dist/mcp/api-key-manager.js +111 -0
  202. package/dist/mcp/api-key-manager.js.map +7 -0
  203. package/dist/mcp/changeset-only.js +34 -0
  204. package/dist/mcp/changeset-only.js.map +7 -0
  205. package/dist/mcp/http-transport.js +176 -0
  206. package/dist/mcp/http-transport.js.map +7 -0
  207. package/dist/mcp/resource-registry.js +97 -0
  208. package/dist/mcp/resource-registry.js.map +7 -0
  209. package/dist/mcp/tool-registry.js +60 -0
  210. package/dist/mcp/tool-registry.js.map +7 -0
  211. package/dist/mcp/tools/annotation-create.js +39 -0
  212. package/dist/mcp/tools/annotation-create.js.map +7 -0
  213. package/dist/mcp/tools/annotation-delete.js +29 -0
  214. package/dist/mcp/tools/annotation-delete.js.map +7 -0
  215. package/dist/mcp/tools/annotation-get.js +29 -0
  216. package/dist/mcp/tools/annotation-get.js.map +7 -0
  217. package/dist/mcp/tools/annotation-list.js +24 -0
  218. package/dist/mcp/tools/annotation-list.js.map +7 -0
  219. package/dist/mcp/tools/annotation-reply.js +34 -0
  220. package/dist/mcp/tools/annotation-reply.js.map +7 -0
  221. package/dist/mcp/tools/annotation-shared.js +10 -0
  222. package/dist/mcp/tools/annotation-shared.js.map +7 -0
  223. package/dist/mcp/tools/annotation-update.js +39 -0
  224. package/dist/mcp/tools/annotation-update.js.map +7 -0
  225. package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
  226. package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
  227. package/dist/mcp/tools/changeset-editing.js +184 -0
  228. package/dist/mcp/tools/changeset-editing.js.map +7 -0
  229. package/dist/mcp/tools/changeset-lifecycle.js +83 -0
  230. package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
  231. package/dist/mcp/tools/changeset-list.js +33 -0
  232. package/dist/mcp/tools/changeset-list.js.map +7 -0
  233. package/dist/mcp/tools/changeset-shared.js +31 -0
  234. package/dist/mcp/tools/changeset-shared.js.map +7 -0
  235. package/dist/mcp/tools/changeset-show.js +55 -0
  236. package/dist/mcp/tools/changeset-show.js.map +7 -0
  237. package/dist/mcp/tools/changeset-verify.js +43 -0
  238. package/dist/mcp/tools/changeset-verify.js.map +7 -0
  239. package/dist/mcp/tools/chat-launch.js +50 -0
  240. package/dist/mcp/tools/chat-launch.js.map +7 -0
  241. package/dist/mcp/tools/chat-status.js +26 -0
  242. package/dist/mcp/tools/chat-status.js.map +7 -0
  243. package/dist/mcp/tools/index.js +65 -0
  244. package/dist/mcp/tools/index.js.map +7 -0
  245. package/dist/mcp/tools/model-add.js +107 -0
  246. package/dist/mcp/tools/model-add.js.map +7 -0
  247. package/dist/mcp/tools/model-delete.js +99 -0
  248. package/dist/mcp/tools/model-delete.js.map +7 -0
  249. package/dist/mcp/tools/model-export.js +72 -0
  250. package/dist/mcp/tools/model-export.js.map +7 -0
  251. package/dist/mcp/tools/model-info.js +51 -0
  252. package/dist/mcp/tools/model-info.js.map +7 -0
  253. package/dist/mcp/tools/model-list.js +53 -0
  254. package/dist/mcp/tools/model-list.js.map +7 -0
  255. package/dist/mcp/tools/model-reload.js +34 -0
  256. package/dist/mcp/tools/model-reload.js.map +7 -0
  257. package/dist/mcp/tools/model-search.js +102 -0
  258. package/dist/mcp/tools/model-search.js.map +7 -0
  259. package/dist/mcp/tools/model-show.js +68 -0
  260. package/dist/mcp/tools/model-show.js.map +7 -0
  261. package/dist/mcp/tools/model-stats.js +22 -0
  262. package/dist/mcp/tools/model-stats.js.map +7 -0
  263. package/dist/mcp/tools/model-trace.js +118 -0
  264. package/dist/mcp/tools/model-trace.js.map +7 -0
  265. package/dist/mcp/tools/model-update.js +121 -0
  266. package/dist/mcp/tools/model-update.js.map +7 -0
  267. package/dist/mcp/tools/model-validate.js +78 -0
  268. package/dist/mcp/tools/model-validate.js.map +7 -0
  269. package/dist/mcp/tools/relationship-tools.js +98 -0
  270. package/dist/mcp/tools/relationship-tools.js.map +7 -0
  271. package/dist/mcp/tools/shared.js +126 -0
  272. package/dist/mcp/tools/shared.js.map +7 -0
  273. package/dist/mcp/tools/spec-type.js +90 -0
  274. package/dist/mcp/tools/spec-type.js.map +7 -0
  275. package/dist/reports/model-layer-report-generator.js +5 -3
  276. package/dist/reports/model-layer-report-generator.js.map +2 -2
  277. package/dist/reports/model-readme-generator.js +116 -0
  278. package/dist/reports/model-readme-generator.js.map +7 -0
  279. package/dist/reports/model-report-data.js +37 -1
  280. package/dist/reports/model-report-data.js.map +2 -2
  281. package/dist/reports/model-report-orchestrator.js +29 -6
  282. package/dist/reports/model-report-orchestrator.js.map +2 -2
  283. package/dist/reports/model-report-utils.js +9 -0
  284. package/dist/reports/model-report-utils.js.map +7 -0
  285. package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
  286. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  287. package/dist/schemas/bundled/api.json +1029 -1658
  288. package/dist/schemas/bundled/apm.json +499 -734
  289. package/dist/schemas/bundled/application.json +745 -370
  290. package/dist/schemas/bundled/base.json +3 -220
  291. package/dist/schemas/bundled/business.json +452 -366
  292. package/dist/schemas/bundled/data-model.json +403 -383
  293. package/dist/schemas/bundled/data-store.json +334 -558
  294. package/dist/schemas/bundled/manifest.json +18 -18
  295. package/dist/schemas/bundled/motivation.json +349 -368
  296. package/dist/schemas/bundled/navigation.json +415 -731
  297. package/dist/schemas/bundled/product.json +325 -159
  298. package/dist/schemas/bundled/security.json +864 -1232
  299. package/dist/schemas/bundled/technology.json +574 -703
  300. package/dist/schemas/bundled/testing.json +718 -1643
  301. package/dist/schemas/bundled/ux.json +739 -1352
  302. package/dist/server/schemas.js +8 -0
  303. package/dist/server/schemas.js.map +2 -2
  304. package/dist/server/server.js +234 -31
  305. package/dist/server/server.js.map +2 -2
  306. package/dist/server-entry.js +2 -1
  307. package/dist/server-entry.js.map +2 -2
  308. package/dist/telemetry/config.js +2 -40
  309. package/dist/telemetry/config.js.map +2 -2
  310. package/dist/telemetry/index.js +1 -1
  311. package/dist/types/index.js.map +2 -2
  312. package/dist/utils/ansi-suppressor.js +23 -0
  313. package/dist/utils/ansi-suppressor.js.map +7 -0
  314. package/dist/utils/attribute-patch.js +41 -0
  315. package/dist/utils/attribute-patch.js.map +7 -0
  316. package/dist/utils/color-utils.js +34 -0
  317. package/dist/utils/color-utils.js.map +7 -0
  318. package/dist/utils/element-reference-shape.js +15 -1
  319. package/dist/utils/element-reference-shape.js.map +3 -3
  320. package/dist/utils/error-hints.js +29 -0
  321. package/dist/utils/error-hints.js.map +7 -0
  322. package/dist/utils/errors.js +75 -10
  323. package/dist/utils/errors.js.map +2 -2
  324. package/dist/utils/globals.js +17 -1
  325. package/dist/utils/globals.js.map +2 -2
  326. package/dist/utils/project-paths.js +50 -3
  327. package/dist/utils/project-paths.js.map +2 -2
  328. package/dist/utils/reference-path-parser.js +49 -0
  329. package/dist/utils/reference-path-parser.js.map +7 -0
  330. package/dist/utils/source-reference.js +17 -10
  331. package/dist/utils/source-reference.js.map +2 -2
  332. package/dist/utils/spec-version.js +10 -2
  333. package/dist/utils/spec-version.js.map +2 -2
  334. package/dist/validators/composed-reference-validator.js +240 -0
  335. package/dist/validators/composed-reference-validator.js.map +7 -0
  336. package/dist/validators/reference-validator.js +57 -13
  337. package/dist/validators/reference-validator.js.map +3 -3
  338. package/dist/validators/relationship-schema-validator.js +33 -5
  339. package/dist/validators/relationship-schema-validator.js.map +3 -3
  340. package/dist/validators/schema-validator.js +27 -9
  341. package/dist/validators/schema-validator.js.map +2 -2
  342. package/dist/validators/types.js.map +1 -1
  343. package/dist/viewer/README.md +6 -6
  344. package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
  345. package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
  346. package/dist/viewer/index.html +2 -2
  347. package/dist/viewer/manifest.json +10 -10
  348. package/dist/viewer/version.json +1 -1
  349. package/package.json +7 -3
  350. package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
  351. package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
@@ -0,0 +1,421 @@
1
+ import { getAllLayerIds } from "../generated/layer-registry.js";
2
+ import { normalizeNodeType } from "../generated/node-types.js";
3
+ import { getValidRelationships, RELATIONSHIPS_BY_DESTINATION, RELATIONSHIPS_BY_SOURCE } from "../generated/relationship-index.js";
4
+ import { changesetRevisionToken } from "./changeset-graph.js";
5
+ const INTENT_LAYERS = /* @__PURE__ */ new Set(["motivation", "business", "product"]), INTENT_TRACE_DEPTH = 4, DEFAULT_HUB_THRESHOLD = 15;
6
+ function computeImpact(graph, options = {}) {
7
+ const depth = options.depth ?? 2, maxElements = options.maxElements ?? 200, hubThreshold = options.hubThreshold ?? DEFAULT_HUB_THRESHOLD, expandHubs = options.expandHubs ?? !1, changed = new Set(graph.changedElements().map((e) => e.id));
8
+ let start, unknownFrom;
9
+ if (options.from) {
10
+ start = /* @__PURE__ */ new Set(), unknownFrom = [];
11
+ for (const id of options.from) {
12
+ const element = graph.getElement(id);
13
+ element ? start.add(element.id) : unknownFrom.push(id);
14
+ }
15
+ } else
16
+ start = changed;
17
+ const reached = /* @__PURE__ */ new Map(), visited = new Set(start), hubs = [], perHop = /* @__PURE__ */ new Map();
18
+ let truncated = !1;
19
+ const reach = (from, neighbor, distance) => {
20
+ if (visited.has(neighbor.other))
21
+ return !1;
22
+ const element = graph.getElement(neighbor.other);
23
+ if (!element || element.status === "deleted")
24
+ return !1;
25
+ if (visited.add(neighbor.other), reached.size >= maxElements)
26
+ return truncated = !0, !1;
27
+ reached.set(element.id, {
28
+ id: element.id,
29
+ layer: element.layer,
30
+ name: element.name,
31
+ type: element.type,
32
+ distance,
33
+ via: describeHop(from, neighbor),
34
+ files: sourceFiles(element),
35
+ ...element.status === "added" || element.status === "updated" ? { status: element.status } : {}
36
+ }), perHop.set(distance, (perHop.get(distance) ?? 0) + 1);
37
+ const degree = graph.neighbors(element.id).length;
38
+ return !(degree > hubThreshold && (hubs.push({ id: element.id, degree, depth: distance }), !expandHubs));
39
+ };
40
+ let frontier = [];
41
+ if (!options.from)
42
+ for (const rel of graph.changedRelationships()) {
43
+ if (changed.has(rel.source) || changed.has(rel.target))
44
+ continue;
45
+ const asNeighbor = (other, direction) => ({ relationship: rel, direction, other });
46
+ reach(rel.source, asNeighbor(rel.target, "out"), 1) && frontier.push(rel.target), reach(rel.target, asNeighbor(rel.source, "in"), 1) && frontier.push(rel.source);
47
+ }
48
+ let current = [...start];
49
+ for (let distance = 1; distance <= depth && current.length + frontier.length > 0 && !truncated; distance++) {
50
+ const next = [];
51
+ for (const id of current) {
52
+ for (const neighbor of graph.neighbors(id, { includeDeleted: changed.has(id) }))
53
+ if (reach(id, neighbor, distance) && next.push(neighbor.other), truncated)
54
+ break;
55
+ if (truncated)
56
+ break;
57
+ }
58
+ current = [...frontier, ...next], frontier = [];
59
+ }
60
+ const byLayer = {};
61
+ for (const layer of orderedLayers([...reached.values()].map((e) => e.layer)))
62
+ byLayer[layer] = [...reached.values()].filter((e) => e.layer === layer).sort((a, b) => a.distance - b.distance || a.id.localeCompare(b.id)).map(({ layer: _layer, ...rest }) => rest);
63
+ return {
64
+ depth,
65
+ changed: changed.size,
66
+ reached: reached.size,
67
+ truncated,
68
+ byLayer,
69
+ perHop: [...perHop.entries()].sort(([a], [b]) => a - b).map(([d, n]) => ({ depth: d, reached: n })),
70
+ hubs: hubs.sort((a, b) => a.depth - b.depth || b.degree - a.degree || a.id.localeCompare(b.id)),
71
+ hubThreshold,
72
+ ...options.from ? { from: [...start], unknownFrom: unknownFrom ?? [] } : {},
73
+ ...options.from && start.size === 0 ? {
74
+ warnings: [
75
+ options.from.length === 0 ? "from is empty, so the walk had no start element and reached nothing; omit from to walk from every changed element." : "None of the from ids is in the projected model, so the walk had no start element and reached nothing; see unknownFrom."
76
+ ]
77
+ } : {}
78
+ };
79
+ }
80
+ function buildBrief({ graph, annotations = [], drift, unwired: unwiredMode = "summary", unwiredLayers, hubThreshold = DEFAULT_HUB_THRESHOLD }) {
81
+ const changeset = graph.changeset, changed = graph.changedElements(), changedIds = new Set(changed.map((e) => e.id)), changedRels = graph.changedRelationships(), inBase = graph.alreadyInBase(), layers = [];
82
+ for (const layer of orderedLayers(changed.map((e) => e.layer)))
83
+ layers.push({
84
+ layer,
85
+ changes: changed.filter((e) => e.layer === layer).map((e) => describeChange(graph, e))
86
+ });
87
+ const relationshipChanges = changedRels.filter((rel) => !changedIds.has(rel.source) && !changedIds.has(rel.target)).map((rel) => ({ status: rel.status, source: rel.source, predicate: rel.predicate, target: rel.target })), touchedLayers = new Set(changed.map((e) => e.layer));
88
+ for (const rel of changedRels)
89
+ for (const end of [rel.source, rel.target]) {
90
+ const element = graph.getElement(end);
91
+ element && touchedLayers.add(element.layer);
92
+ }
93
+ const allLayers = getAllLayerIds(), reach = {
94
+ touched: allLayers.filter((l) => touchedLayers.has(l)),
95
+ untouched: allLayers.filter((l) => !touchedLayers.has(l))
96
+ }, gaps = [];
97
+ for (const element of changed.filter((e) => e.status === "added")) {
98
+ const live = graph.neighbors(element.id);
99
+ live.length === 0 && gaps.push({
100
+ kind: "unconnected",
101
+ element: element.id,
102
+ detail: "Added with no relationships: nothing in the model says what it serves, realizes or uses."
103
+ });
104
+ for (const required of requiredOutgoing(element))
105
+ live.some((n) => {
106
+ if (n.direction !== "out" || n.relationship.predicate !== required.predicate)
107
+ return !1;
108
+ const other = graph.getElement(n.other);
109
+ return !!other && specNodeId(other) === required.destinationSpecNodeId;
110
+ }) || gaps.push({
111
+ kind: "missing-required",
112
+ element: element.id,
113
+ detail: `The catalog requires ${required.sourceSpecNodeId} --${required.predicate}--> ${required.destinationSpecNodeId}.`
114
+ });
115
+ gaps.push(...peerPatternGaps(graph, element));
116
+ }
117
+ const unwired = unwiredMode === "none" ? void 0 : presentUnwired(computeUnwired(graph), unwiredMode, new Set(reach.touched), unwiredLayers), traced = [], untraced = [], unanchored = [], intentWalk = new IntentWalk(graph, hubThreshold);
118
+ for (const element of changed) {
119
+ if (element.status === "deleted" || INTENT_LAYERS.has(element.layer))
120
+ continue;
121
+ const result = intentWalk.trace(element.id);
122
+ if (result.traced)
123
+ traced.push({ element: element.id, ...result.traced });
124
+ else if (untraced.push(element.id), result.unanchored) {
125
+ const { id, path } = result.unanchored;
126
+ unanchored.push({
127
+ element: element.id,
128
+ tracesTo: id,
129
+ path,
130
+ detail: `traces to added intent element ${id}, which traces to nothing`
131
+ });
132
+ }
133
+ }
134
+ const files = /* @__PURE__ */ new Map(), addFiles = (element, role) => {
135
+ for (const file of sourceFiles(element)) {
136
+ const entry = files.get(file) ?? /* @__PURE__ */ new Map();
137
+ entry.get(element.id) !== "changed" && entry.set(element.id, role), files.set(file, entry);
138
+ }
139
+ }, neighborIds = /* @__PURE__ */ new Set();
140
+ for (const element of changed) {
141
+ addFiles(element, "changed");
142
+ for (const n of graph.neighbors(element.id, { includeDeleted: !0 }))
143
+ changedIds.has(n.other) || neighborIds.add(n.other);
144
+ }
145
+ for (const rel of changedRels)
146
+ for (const end of [rel.source, rel.target])
147
+ changedIds.has(end) || neighborIds.add(end);
148
+ for (const id of neighborIds) {
149
+ const element = graph.getElement(id);
150
+ element && element.status !== "deleted" && addFiles(element, "neighbor");
151
+ }
152
+ const codeTouched = [...files.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([file, elements]) => ({
153
+ file,
154
+ elements: [...elements.entries()].map(([id, role]) => ({ id, role }))
155
+ })), watched = /* @__PURE__ */ new Set([...changedIds, ...neighborIds]), feedback = annotations.filter((a) => !a.resolved && watched.has(graph.resolve(a.elementId))).map((a) => ({
156
+ annotationId: a.id,
157
+ elementId: a.elementId,
158
+ author: a.author,
159
+ content: a.content,
160
+ tags: a.tags,
161
+ replies: a.replies.length
162
+ }));
163
+ return {
164
+ revision: graph.revision,
165
+ revisionToken: changesetRevisionToken(graph.changeset, graph.revision),
166
+ changeset: {
167
+ id: changeset.id,
168
+ name: changeset.name,
169
+ ...changeset.description ? { description: changeset.description } : {},
170
+ status: changeset.status,
171
+ created: changeset.created,
172
+ modified: changeset.modified,
173
+ ...drift ? { drift } : {}
174
+ },
175
+ summary: {
176
+ elementsAdded: changed.filter((e) => e.status === "added").length,
177
+ elementsUpdated: changed.filter((e) => e.status === "updated").length,
178
+ elementsDeleted: changed.filter((e) => e.status === "deleted").length,
179
+ relationshipsAdded: changedRels.filter((r) => r.status === "added").length,
180
+ relationshipsDeleted: changedRels.filter((r) => r.status === "deleted").length,
181
+ elementsAlreadyInBase: inBase.elements.length,
182
+ relationshipsAlreadyInBase: inBase.relationships.length
183
+ },
184
+ alreadyInBase: {
185
+ elements: inBase.elements.map((e) => e.id),
186
+ relationships: inBase.relationships.map((r) => ({ source: r.source, predicate: r.predicate, target: r.target }))
187
+ },
188
+ layers,
189
+ relationshipChanges,
190
+ reach,
191
+ gaps,
192
+ ...unwired ? { unwired } : {},
193
+ intent: { traced, untraced, ...unanchored.length > 0 ? { unanchored } : {} },
194
+ codeTouched,
195
+ feedback
196
+ };
197
+ }
198
+ function describeChange(graph, element) {
199
+ const op = element.status, connections = graph.neighbors(element.id, { includeDeleted: !0 }).map((n) => {
200
+ const other = graph.getElement(n.other);
201
+ return {
202
+ direction: n.direction,
203
+ predicate: n.relationship.predicate,
204
+ other: n.other,
205
+ ...other ? { otherName: other.name, otherLayer: other.layer } : {},
206
+ status: n.relationship.status
207
+ };
208
+ }), change = {
209
+ op,
210
+ id: element.id,
211
+ type: element.type,
212
+ name: element.name,
213
+ ...element.description ? { description: element.description } : {},
214
+ connections
215
+ };
216
+ return op !== "deleted" && Object.keys(element.attributes).length > 0 && (change.attributes = element.attributes), op === "updated" && element.before && (change.changedFields = diffFields(element.before, element)), change;
217
+ }
218
+ function diffFields(before, after) {
219
+ const fields = [], compare = (field, a, b) => {
220
+ JSON.stringify(a) !== JSON.stringify(b) && fields.push({ field, before: a, after: b });
221
+ };
222
+ compare("name", before.name, after.name), compare("description", before.description, after.description);
223
+ const beforeAttrs = before.attributes ?? {};
224
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(beforeAttrs), ...Object.keys(after.attributes)]))
225
+ compare(`attributes.${key}`, beforeAttrs[key], after.attributes[key]);
226
+ return fields;
227
+ }
228
+ class IntentWalk {
229
+ graph;
230
+ hubThreshold;
231
+ anchors = /* @__PURE__ */ new Map();
232
+ constructor(graph, hubThreshold) {
233
+ this.graph = graph, this.hubThreshold = hubThreshold;
234
+ }
235
+ trace(start) {
236
+ let unanchored;
237
+ const hit = this.walk(start, (element, path) => element.status !== "added" || this.anchorOf(element.id) ? !0 : (unanchored ??= { id: element.id, path }, !1));
238
+ if (!hit)
239
+ return unanchored ? { unanchored } : {};
240
+ const anchor = this.graph.getElement(hit.id)?.status === "added" ? this.anchorOf(hit.id) : null;
241
+ return {
242
+ traced: {
243
+ tracesTo: hit.id,
244
+ distance: hit.path.length - 1,
245
+ path: hit.path,
246
+ ...anchor ? { anchoredBy: anchor } : {}
247
+ }
248
+ };
249
+ }
250
+ /** The existing intent element an added intent element traces to, or null. */
251
+ anchorOf(id) {
252
+ if (this.anchors.has(id))
253
+ return this.anchors.get(id);
254
+ this.anchors.set(id, null);
255
+ const hit = this.walk(id, (element) => element.status !== "added");
256
+ return this.anchors.set(id, hit), hit;
257
+ }
258
+ /**
259
+ * Walks from `start` and returns the first intent element `accept` takes, with the path to it.
260
+ * Intent elements it refuses are walked through like any other.
261
+ */
262
+ walk(start, accept) {
263
+ const parent = /* @__PURE__ */ new Map(), visited = /* @__PURE__ */ new Set([start]), pathTo = (id) => {
264
+ const path = [id];
265
+ for (let at = parent.get(id); at !== void 0; at = parent.get(at))
266
+ path.unshift(at);
267
+ return path;
268
+ };
269
+ let frontier = [start];
270
+ for (let distance = 1; distance <= INTENT_TRACE_DEPTH && frontier.length > 0; distance++) {
271
+ const next = [];
272
+ for (const id of frontier)
273
+ if (!(id !== start && this.graph.neighbors(id).length > this.hubThreshold))
274
+ for (const n of this.graph.neighbors(id)) {
275
+ if (visited.has(n.other))
276
+ continue;
277
+ visited.add(n.other);
278
+ const element = this.graph.getElement(n.other);
279
+ if (!(!element || element.status === "deleted")) {
280
+ if (parent.set(element.id, id), INTENT_LAYERS.has(element.layer) && accept(element, pathTo(element.id)))
281
+ return { id: element.id, path: pathTo(element.id) };
282
+ next.push(element.id);
283
+ }
284
+ }
285
+ frontier = next;
286
+ }
287
+ return null;
288
+ }
289
+ }
290
+ const PEER_MIN = 2, PEER_SHARE = 0.5;
291
+ function peerPatternGaps(graph, element) {
292
+ const own = graph.neighbors(element.id);
293
+ if (own.length === 0)
294
+ return [];
295
+ const type = specNodeId(element), peers = /* @__PURE__ */ new Set();
296
+ for (const mine of own)
297
+ for (const theirs of graph.neighbors(mine.other)) {
298
+ if (theirs.relationship.predicate !== mine.relationship.predicate || theirs.direction === mine.direction)
299
+ continue;
300
+ const peer = graph.getElement(theirs.other);
301
+ !peer || peer.id === element.id || peer.status === "added" || peer.status === "deleted" || specNodeId(peer) === type && peers.add(peer.id);
302
+ }
303
+ if (peers.size < PEER_MIN)
304
+ return [];
305
+ const shapeOf = (n) => {
306
+ const other = graph.getElement(n.other);
307
+ return other ? `${n.direction}|${n.relationship.predicate}|${specNodeId(other)}` : null;
308
+ }, ownShapes = new Set(own.map(shapeOf)), shapes = /* @__PURE__ */ new Map();
309
+ for (const peerId of peers)
310
+ for (const n of graph.neighbors(peerId)) {
311
+ if (n.other === element.id)
312
+ continue;
313
+ const shape = shapeOf(n);
314
+ if (!shape || ownShapes.has(shape))
315
+ continue;
316
+ const entry = shapes.get(shape) ?? { peers: /* @__PURE__ */ new Set(), examples: [] };
317
+ !entry.peers.has(peerId) && entry.examples.length < 3 && entry.examples.push(describeHop(peerId, n)), entry.peers.add(peerId), shapes.set(shape, entry);
318
+ }
319
+ const threshold = Math.max(PEER_MIN, Math.ceil(peers.size * PEER_SHARE));
320
+ return [...shapes.entries()].filter(([, entry]) => entry.peers.size >= threshold).filter(([shape]) => {
321
+ const [direction, predicate, otherType] = shape.split("|");
322
+ return direction === "out" ? getValidRelationships(type, predicate, otherType).length > 0 : getValidRelationships(otherType, predicate, type).length > 0;
323
+ }).sort(([, a], [, b]) => b.peers.size - a.peers.size).map(([shape, entry]) => {
324
+ const [direction, predicate, otherType] = shape.split("|"), connection = direction === "out" ? `--${predicate}--> ${otherType}` : `${otherType} --${predicate}--> them`;
325
+ return {
326
+ kind: "peer-pattern",
327
+ element: element.id,
328
+ detail: `${entry.peers.size} of ${peers.size} existing ${type} elements wired like this one have ${connection}; this one has none.`,
329
+ examples: entry.examples
330
+ };
331
+ });
332
+ }
333
+ function computeUnwired(graph) {
334
+ const existing = /* @__PURE__ */ new Map(), added = /* @__PURE__ */ new Map();
335
+ for (const element of graph.elements.values()) {
336
+ if (element.status === "deleted")
337
+ continue;
338
+ const counts = element.status === "added" ? added : existing, type = specNodeId(element);
339
+ counts.set(type, (counts.get(type) ?? 0) + 1);
340
+ }
341
+ const result = [];
342
+ for (const element of graph.changedElements()) {
343
+ if (element.status !== "added" && element.status !== "updated")
344
+ continue;
345
+ const type = specNodeId(element), have = /* @__PURE__ */ new Set();
346
+ for (const n of graph.neighbors(element.id)) {
347
+ const other = graph.getElement(n.other);
348
+ other && other.status !== "deleted" && have.add(`${n.direction}|${n.relationship.predicate}|${specNodeId(other)}`);
349
+ }
350
+ const entries = /* @__PURE__ */ new Map(), consider = (direction, predicate, otherType) => {
351
+ const key = `${direction}|${predicate}|${otherType}`;
352
+ if (have.has(key) || entries.has(key))
353
+ return;
354
+ const self = otherType === type ? 1 : 0, existingCount = (existing.get(otherType) ?? 0) - (element.status === "added" ? 0 : self), addedCount = (added.get(otherType) ?? 0) - (element.status === "added" ? self : 0);
355
+ entries.set(key, { direction, predicate, otherType, existingCount, addedCount });
356
+ };
357
+ for (const spec of RELATIONSHIPS_BY_SOURCE.get(type) ?? [])
358
+ consider("out", spec.predicate, spec.destinationSpecNodeId);
359
+ for (const spec of RELATIONSHIPS_BY_DESTINATION.get(type) ?? [])
360
+ consider("in", spec.predicate, spec.sourceSpecNodeId);
361
+ const layerOf = (specId) => specId.split(".")[0], all = [...entries.values()], byLayer = orderedLayers(all.map((e) => layerOf(e.otherType))).map((layer) => ({
362
+ layer,
363
+ entries: all.filter((e) => layerOf(e.otherType) === layer).sort((a, b) => (a.direction === b.direction ? 0 : a.direction === "out" ? -1 : 1) || a.predicate.localeCompare(b.predicate) || a.otherType.localeCompare(b.otherType))
364
+ }));
365
+ result.push({ element: element.id, type, byLayer });
366
+ }
367
+ return result;
368
+ }
369
+ function presentUnwired(computed, mode, reachedLayers, layerFilter) {
370
+ const known = new Set(getAllLayerIds()), filter = layerFilter ? new Set(layerFilter) : void 0, unknownLayers = layerFilter?.filter((l) => !known.has(l)) ?? [], elements = computed.map(({ element, type, byLayer }) => {
371
+ const shown = byLayer.filter((l) => !filter || filter.has(l.layer)).map((l) => {
372
+ const count = l.entries.length;
373
+ if (mode === "full")
374
+ return { layer: l.layer, count, entries: l.entries };
375
+ const inModel = l.entries.filter((e) => e.existingCount + e.addedCount > 0), typesNotInModel = count - inModel.length, withEntries = reachedLayers.has(l.layer) || !!filter?.has(l.layer);
376
+ return {
377
+ layer: l.layer,
378
+ count,
379
+ ...withEntries ? { entries: inModel } : {},
380
+ ...typesNotInModel > 0 ? { typesNotInModel } : {}
381
+ };
382
+ });
383
+ return { element, type, total: shown.reduce((sum, l) => sum + l.count, 0), byLayer: shown };
384
+ });
385
+ return {
386
+ mode,
387
+ ...layerFilter ? { layers: layerFilter } : {},
388
+ ...unknownLayers.length > 0 ? { unknownLayers } : {},
389
+ elements
390
+ };
391
+ }
392
+ function unwiredKey(element, entry) {
393
+ return `${element}|${entry.direction}|${entry.predicate}|${entry.otherType}`;
394
+ }
395
+ function requiredOutgoing(element) {
396
+ return getValidRelationships(specNodeId(element)).filter((r) => r.required);
397
+ }
398
+ function specNodeId(element) {
399
+ return `${element.layer}.${normalizeNodeType(element.layer, element.type)}`;
400
+ }
401
+ function describeHop(from, neighbor) {
402
+ const { predicate, status } = neighbor.relationship, suffix = status === "unchanged" ? "" : ` (${status} by this changeset)`;
403
+ return neighbor.direction === "out" ? `${from} --${predicate}--> ${neighbor.other}${suffix}` : `${neighbor.other} --${predicate}--> ${from}${suffix}`;
404
+ }
405
+ function sourceFiles(element) {
406
+ const locations = element.sourceReference?.locations ?? [];
407
+ return [...new Set(locations.map((l) => l.file).filter((f) => !!f))];
408
+ }
409
+ function orderedLayers(layers) {
410
+ const present = new Set(layers), known = getAllLayerIds().filter((l) => present.has(l)), unknown = [...present].filter((l) => !known.includes(l)).sort();
411
+ return [...known, ...unknown];
412
+ }
413
+ export {
414
+ DEFAULT_HUB_THRESHOLD,
415
+ INTENT_LAYERS,
416
+ buildBrief,
417
+ computeImpact,
418
+ computeUnwired,
419
+ unwiredKey
420
+ };
421
+ //# sourceMappingURL=changeset-analysis.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/changeset-analysis.ts"],
4
+ "sourcesContent": ["/**\n * Changeset analysis \u2014 pressure-testing a planned change against the model.\n *\n * Both functions read a `ChangesetGraph` (the base model with the changeset applied, relationships\n * included):\n *\n * - `computeImpact` walks outward from everything the changeset touches and lists the existing\n * elements it reaches, with the source files they live in.\n * - `buildBrief` renders the changeset as a spec for the build: changes by layer with their\n * connections, which layers the change reaches, gaps, whether each change traces to intent, the\n * code it touches, and open review comments.\n *\n * See docs/CHANGE_MODELING.md.\n */\n\nimport { getAllLayerIds } from \"../generated/layer-registry.js\";\nimport { normalizeNodeType } from \"../generated/node-types.js\";\nimport {\n getValidRelationships,\n RELATIONSHIPS_BY_DESTINATION,\n RELATIONSHIPS_BY_SOURCE,\n} from \"../generated/relationship-index.js\";\nimport type { Annotation } from \"./annotation-store.js\";\nimport type { ChangesetDrift } from \"./changeset-base-index.js\";\nimport { changesetRevisionToken } from \"./changeset-graph.js\";\nimport type { ChangesetGraph, GraphElement, GraphRelationship, Neighbor } from \"./changeset-graph.js\";\n\n/** Layers that state why the system exists; every change should trace to one of them. */\nexport const INTENT_LAYERS = new Set([\"motivation\", \"business\", \"product\"]);\n\nconst INTENT_TRACE_DEPTH = 4;\n\n// ---------------------------------------------------------------------------\n// Impact\n// ---------------------------------------------------------------------------\n\n/** An element with more live relationships than this is a hub: reported, not walked through. */\nexport const DEFAULT_HUB_THRESHOLD = 15;\n\nexport interface ImpactOptions {\n /** How many hops to walk out from the changed elements (default 2). */\n depth?: number;\n /** Stop after this many reached elements (default 200). */\n maxElements?: number;\n /**\n * Walk from these elements (ids or paths) instead of from everything the changeset changes.\n * Ids the projected graph does not have are reported in `unknownFrom`.\n */\n from?: string[];\n /** Degree above which a reached element is a hub (default 15). */\n hubThreshold?: number;\n /** Walk through hubs as through any other element (default false). */\n expandHubs?: boolean;\n}\n\nexport interface ImpactedElement {\n id: string;\n name: string;\n type: string;\n distance: number;\n /** The relationship through which the walk first reached this element. */\n via: string;\n files: string[];\n /** Set when the element is itself changed by the changeset (only a walk `from` chosen elements reaches one). */\n status?: \"added\" | \"updated\";\n}\n\nexport interface ImpactHub {\n id: string;\n /** Live relationships the element has in the projected graph. */\n degree: number;\n /** The hop at which the walk reached it. */\n depth: number;\n}\n\nexport interface ImpactReport {\n depth: number;\n /** Elements the changeset adds, updates or deletes. Without `from`, where the walk starts. */\n changed: number;\n /** Existing elements the walk reached. */\n reached: number;\n truncated: boolean;\n byLayer: Record<string, ImpactedElement[]>;\n /** Elements reached at each hop. */\n perHop: Array<{ depth: number; reached: number }>;\n /** Reached elements with more relationships than `hubThreshold`; the walk did not go through them. */\n hubs: ImpactHub[];\n hubThreshold: number;\n /** The resolved start elements, when the walk started `from` chosen ones. */\n from?: string[];\n /** `from` ids the projected graph does not have. */\n unknownFrom?: string[];\n /** Why the walk is not what it looks like, e.g. a `from` with no start element. */\n warnings?: string[];\n}\n\nexport function computeImpact(graph: ChangesetGraph, options: ImpactOptions = {}): ImpactReport {\n const depth = options.depth ?? 2;\n const maxElements = options.maxElements ?? 200;\n const hubThreshold = options.hubThreshold ?? DEFAULT_HUB_THRESHOLD;\n const expandHubs = options.expandHubs ?? false;\n\n const changed = new Set(graph.changedElements().map((e) => e.id));\n\n let start: Set<string>;\n let unknownFrom: string[] | undefined;\n if (options.from) {\n start = new Set();\n unknownFrom = [];\n for (const id of options.from) {\n const element = graph.getElement(id);\n if (element) start.add(element.id);\n else unknownFrom.push(id);\n }\n } else {\n start = changed;\n }\n\n const reached = new Map<string, ImpactedElement & { layer: string }>();\n const visited = new Set(start);\n const hubs: ImpactHub[] = [];\n const perHop = new Map<number, number>();\n let truncated = false;\n\n /** Records a reached element; returns whether the walk should continue through it. */\n const reach = (from: string, neighbor: Neighbor, distance: number): boolean => {\n if (visited.has(neighbor.other)) return false;\n const element = graph.getElement(neighbor.other);\n if (!element || element.status === \"deleted\") return false;\n visited.add(neighbor.other);\n if (reached.size >= maxElements) {\n truncated = true;\n return false;\n }\n reached.set(element.id, {\n id: element.id,\n layer: element.layer,\n name: element.name,\n type: element.type,\n distance,\n via: describeHop(from, neighbor),\n files: sourceFiles(element),\n ...(element.status === \"added\" || element.status === \"updated\" ? { status: element.status } : {}),\n });\n perHop.set(distance, (perHop.get(distance) ?? 0) + 1);\n const degree = graph.neighbors(element.id).length;\n if (degree > hubThreshold) {\n hubs.push({ id: element.id, degree, depth: distance });\n if (!expandHubs) return false;\n }\n return true;\n };\n\n // A relationship the changeset adds or removes between two existing elements changes both of\n // them, so both ends start one hop out, reached through that relationship. A walk from chosen\n // elements starts only from those.\n let frontier: string[] = [];\n if (!options.from) {\n for (const rel of graph.changedRelationships()) {\n if (changed.has(rel.source) || changed.has(rel.target)) continue;\n const asNeighbor = (other: string, direction: \"out\" | \"in\"): Neighbor => ({ relationship: rel, direction, other });\n if (reach(rel.source, asNeighbor(rel.target, \"out\"), 1)) frontier.push(rel.target);\n if (reach(rel.target, asNeighbor(rel.source, \"in\"), 1)) frontier.push(rel.source);\n }\n }\n\n let current = [...start];\n for (let distance = 1; distance <= depth && current.length + frontier.length > 0 && !truncated; distance++) {\n const next: string[] = [];\n for (const id of current) {\n // From a changed element, follow the relationships the change deletes too: their other\n // ends are exactly what a removal affects. Past that, walk only what will still exist.\n for (const neighbor of graph.neighbors(id, { includeDeleted: changed.has(id) })) {\n if (reach(id, neighbor, distance)) next.push(neighbor.other);\n if (truncated) break;\n }\n if (truncated) break;\n }\n current = [...frontier, ...next];\n frontier = [];\n }\n\n const byLayer: Record<string, ImpactedElement[]> = {};\n for (const layer of orderedLayers([...reached.values()].map((e) => e.layer))) {\n byLayer[layer] = [...reached.values()]\n .filter((e) => e.layer === layer)\n .sort((a, b) => a.distance - b.distance || a.id.localeCompare(b.id))\n .map(({ layer: _layer, ...rest }) => rest);\n }\n\n return {\n depth,\n changed: changed.size,\n reached: reached.size,\n truncated,\n byLayer,\n perHop: [...perHop.entries()].sort(([a], [b]) => a - b).map(([d, n]) => ({ depth: d, reached: n })),\n hubs: hubs.sort((a, b) => a.depth - b.depth || b.degree - a.degree || a.id.localeCompare(b.id)),\n hubThreshold,\n ...(options.from ? { from: [...start], unknownFrom: unknownFrom ?? [] } : {}),\n ...(options.from && start.size === 0\n ? {\n warnings: [\n options.from.length === 0\n ? \"from is empty, so the walk had no start element and reached nothing; omit from to walk from every changed element.\"\n : \"None of the from ids is in the projected model, so the walk had no start element and reached nothing; see unknownFrom.\",\n ],\n }\n : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Brief\n// ---------------------------------------------------------------------------\n\nexport interface BriefConnection {\n direction: \"out\" | \"in\";\n predicate: string;\n other: string;\n otherName?: string;\n otherLayer?: string;\n status: GraphRelationship[\"status\"];\n}\n\nexport interface BriefElementChange {\n op: \"added\" | \"updated\" | \"deleted\";\n id: string;\n type: string;\n name: string;\n description?: string;\n attributes?: Record<string, unknown>;\n changedFields?: Array<{ field: string; before: unknown; after: unknown }>;\n connections: BriefConnection[];\n}\n\nexport interface BriefGap {\n /**\n * - `unconnected`: an added element with no relationships.\n * - `missing-required`: the catalog marks a relationship from the element's type required.\n * - `peer-pattern`: most existing elements of the same type, wired to the same neighbours,\n * have a connection this one lacks. The model's own convention, not a spec rule.\n */\n kind: \"unconnected\" | \"missing-required\" | \"peer-pattern\";\n element: string;\n detail: string;\n /** For `peer-pattern`: up to three existing connections of this shape. */\n examples?: string[];\n}\n\n/** A connection the catalog defines for an element's type that the element does not have. */\nexport interface UnwiredEntry {\n /** \"out\" when the element would be the source, \"in\" when it would be the target. */\n direction: \"out\" | \"in\";\n predicate: string;\n /** Spec node id of the other end (`layer.type`). */\n otherType: string;\n /**\n * Elements of `otherType` that exist outside the changeset's own adds: base elements, updated\n * ones included, that the change does not delete (the element itself not counted).\n */\n existingCount: number;\n /** Elements of `otherType` the changeset adds (the element itself not counted). */\n addedCount: number;\n}\n\n/** One added or updated element's unwired connections, as computed (every layer). */\nexport interface ElementUnwired {\n element: string;\n /** Spec node id of the element's type. */\n type: string;\n /** By the other end's layer, in layer order. */\n byLayer: Array<{ layer: string; entries: UnwiredEntry[] }>;\n}\n\n/**\n * - `summary` (default): per element, a count per layer, with the entries for the layers the change\n * already reaches (and for any layer named in `unwiredLayers`). Only entries whose other type the\n * projected model has (existing or added) are listed; the rest are counted per layer as\n * `typesNotInModel`.\n * - `full`: every entry.\n * - `none`: no unwired section.\n */\nexport type UnwiredMode = \"summary\" | \"full\" | \"none\";\n\nexport interface BriefUnwired {\n mode: Exclude<UnwiredMode, \"none\">;\n /** The layer filter, when one was given. */\n layers?: string[];\n /** Filter layers that are not layers of the model. */\n unknownLayers?: string[];\n elements: Array<{\n element: string;\n type: string;\n /** Unwired connections across the layers shown. */\n total: number;\n byLayer: Array<{\n layer: string;\n /** Unwired connections in this layer, listed or not. */\n count: number;\n entries?: UnwiredEntry[];\n /**\n * Summary mode: unwired connections in this layer whose other type the projected model has no\n * element of (neither existing nor added), not listed in `entries`. `full` lists them.\n */\n typesNotInModel?: number;\n }>;\n }>;\n}\n\nexport interface BriefInput {\n graph: ChangesetGraph;\n annotations?: Annotation[];\n /** Hash-only drift (`{isDrifted, warnings}`) or the structured one from `describeDrift`. */\n drift?: BriefDrift;\n /** How to show unwired connections (default `summary`). */\n unwired?: UnwiredMode;\n /** Only show unwired connections whose other end is in these layers. */\n unwiredLayers?: string[];\n /** The intent trace does not walk through elements with more relationships than this (default 15, as impact). */\n hubThreshold?: number;\n}\n\n/** A changed element that reaches intent, and how. */\nexport interface IntentTrace {\n element: string;\n /** The first intent element the walk reached. */\n tracesTo: string;\n distance: number;\n /** Element ids from the changed element to `tracesTo`, both included. */\n path: string[];\n /**\n * When `tracesTo` is an intent element the changeset adds: the existing intent element it traces\n * to, and the path there. An added intent element counts only when it has one.\n */\n anchoredBy?: { id: string; path: string[] };\n}\n\n/** A changed element whose only intent is an intent element the changeset adds, which traces to nothing. */\nexport interface UnanchoredTrace {\n element: string;\n /** The added intent element it reaches. */\n tracesTo: string;\n path: string[];\n detail: string;\n}\n\n/** `isDrifted` and `warnings` always; the rest when `describeDrift` produced it. */\nexport type BriefDrift = { isDrifted: boolean; warnings: string[] } & Partial<Omit<ChangesetDrift, \"isDrifted\" | \"warnings\">>;\n\nexport interface ChangesetBrief {\n /**\n * The changeset's revision: the last sequence number used, by a stage or an unstage (-1 when\n * nothing was ever staged); pass it back as `since`.\n */\n revision: number;\n /**\n * Names the plan at `revision`, not just its length (see `changesetRevisionToken`). Pass it back\n * as `since` to have the delta warn when changes up to that revision were unstaged or rewritten.\n */\n revisionToken: string;\n changeset: {\n id: string;\n name: string;\n description?: string;\n status: string;\n created: string;\n modified: string;\n drift?: BriefDrift;\n };\n summary: {\n /** Elements the changeset adds that the base does not have yet. */\n elementsAdded: number;\n elementsUpdated: number;\n elementsDeleted: number;\n /** Relationships the changeset adds that the base does not have yet. */\n relationshipsAdded: number;\n relationshipsDeleted: number;\n /** Staged adds whose element the base already has (e.g. an as-built changeset was committed). */\n elementsAlreadyInBase: number;\n /** Staged relationship adds the base already has. */\n relationshipsAlreadyInBase: number;\n };\n /** Staged adds the base already has; commit skips them. Counted apart from the adds above. */\n alreadyInBase: {\n elements: string[];\n relationships: Array<{ source: string; predicate: string; target: string }>;\n };\n layers: Array<{ layer: string; changes: BriefElementChange[] }>;\n /** Changed relationships whose two ends the changeset does not otherwise change. */\n relationshipChanges: Array<{ status: string; source: string; predicate: string; target: string }>;\n reach: { touched: string[]; untouched: string[] };\n gaps: BriefGap[];\n /**\n * Connections the catalog defines for each added or updated element's type, in either direction,\n * that the element has no live relationship for. Absent when `unwired` is `none`.\n */\n unwired?: BriefUnwired;\n /**\n * Whether each changed element outside the intent layers reaches an intent element (motivation,\n * business, product) within a few hops, without walking through hubs. An intent element the\n * changeset adds counts only when it itself traces to an existing one.\n */\n intent: {\n traced: IntentTrace[];\n /** Elements that trace to no intent (including the ones listed under `unanchored`). */\n untraced: string[];\n /** Untraced elements that reach only added intent elements which trace to nothing. */\n unanchored?: UnanchoredTrace[];\n };\n codeTouched: Array<{ file: string; elements: Array<{ id: string; role: \"changed\" | \"neighbor\" }> }>;\n feedback: Array<{\n annotationId: string;\n elementId: string;\n author: string;\n content: string;\n tags: string[];\n replies: number;\n }>;\n}\n\nexport function buildBrief({\n graph,\n annotations = [],\n drift,\n unwired: unwiredMode = \"summary\",\n unwiredLayers,\n hubThreshold = DEFAULT_HUB_THRESHOLD,\n}: BriefInput): ChangesetBrief {\n const changeset = graph.changeset;\n const changed = graph.changedElements();\n const changedIds = new Set(changed.map((e) => e.id));\n const changedRels = graph.changedRelationships();\n const inBase = graph.alreadyInBase();\n\n // Changes by layer\n const layers: ChangesetBrief[\"layers\"] = [];\n for (const layer of orderedLayers(changed.map((e) => e.layer))) {\n layers.push({\n layer,\n changes: changed.filter((e) => e.layer === layer).map((e) => describeChange(graph, e)),\n });\n }\n\n const relationshipChanges = changedRels\n .filter((rel) => !changedIds.has(rel.source) && !changedIds.has(rel.target))\n .map((rel) => ({ status: rel.status, source: rel.source, predicate: rel.predicate, target: rel.target }));\n\n // Reach: a layer is touched when an element in it changes, or a relationship into it does.\n const touchedLayers = new Set(changed.map((e) => e.layer));\n for (const rel of changedRels) {\n for (const end of [rel.source, rel.target]) {\n const element = graph.getElement(end);\n if (element) touchedLayers.add(element.layer);\n }\n }\n const allLayers = getAllLayerIds();\n const reach = {\n touched: allLayers.filter((l) => touchedLayers.has(l)),\n untouched: allLayers.filter((l) => !touchedLayers.has(l)),\n };\n\n // Gaps\n const gaps: BriefGap[] = [];\n for (const element of changed.filter((e) => e.status === \"added\")) {\n const live = graph.neighbors(element.id);\n if (live.length === 0) {\n gaps.push({\n kind: \"unconnected\",\n element: element.id,\n detail: \"Added with no relationships: nothing in the model says what it serves, realizes or uses.\",\n });\n }\n for (const required of requiredOutgoing(element)) {\n const satisfied = live.some((n) => {\n if (n.direction !== \"out\" || n.relationship.predicate !== required.predicate) return false;\n const other = graph.getElement(n.other);\n return !!other && specNodeId(other) === required.destinationSpecNodeId;\n });\n if (!satisfied) {\n gaps.push({\n kind: \"missing-required\",\n element: element.id,\n detail: `The catalog requires ${required.sourceSpecNodeId} --${required.predicate}--> ${required.destinationSpecNodeId}.`,\n });\n }\n }\n gaps.push(...peerPatternGaps(graph, element));\n }\n\n const unwired =\n unwiredMode === \"none\"\n ? undefined\n : presentUnwired(computeUnwired(graph), unwiredMode, new Set(reach.touched), unwiredLayers);\n // Intent: changed elements outside the intent layers should reach one within a few hops.\n const traced: IntentTrace[] = [];\n const untraced: string[] = [];\n const unanchored: UnanchoredTrace[] = [];\n const intentWalk = new IntentWalk(graph, hubThreshold);\n for (const element of changed) {\n if (element.status === \"deleted\" || INTENT_LAYERS.has(element.layer)) continue;\n const result = intentWalk.trace(element.id);\n if (result.traced) {\n traced.push({ element: element.id, ...result.traced });\n } else {\n untraced.push(element.id);\n if (result.unanchored) {\n const { id, path } = result.unanchored;\n unanchored.push({\n element: element.id,\n tracesTo: id,\n path,\n detail: `traces to added intent element ${id}, which traces to nothing`,\n });\n }\n }\n }\n\n // Code touched: files of changed elements and of the existing elements next to them.\n const files = new Map<string, Map<string, \"changed\" | \"neighbor\">>();\n const addFiles = (element: GraphElement, role: \"changed\" | \"neighbor\") => {\n for (const file of sourceFiles(element)) {\n const entry = files.get(file) ?? new Map();\n if (entry.get(element.id) !== \"changed\") entry.set(element.id, role);\n files.set(file, entry);\n }\n };\n const neighborIds = new Set<string>();\n for (const element of changed) {\n addFiles(element, \"changed\");\n for (const n of graph.neighbors(element.id, { includeDeleted: true })) {\n if (!changedIds.has(n.other)) neighborIds.add(n.other);\n }\n }\n for (const rel of changedRels) {\n for (const end of [rel.source, rel.target]) if (!changedIds.has(end)) neighborIds.add(end);\n }\n for (const id of neighborIds) {\n const element = graph.getElement(id);\n if (element && element.status !== \"deleted\") addFiles(element, \"neighbor\");\n }\n const codeTouched = [...files.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([file, elements]) => ({\n file,\n elements: [...elements.entries()].map(([id, role]) => ({ id, role })),\n }));\n\n // Feedback: open annotations on what the change touches.\n const watched = new Set([...changedIds, ...neighborIds]);\n const feedback = annotations\n .filter((a) => !a.resolved && watched.has(graph.resolve(a.elementId)))\n .map((a) => ({\n annotationId: a.id,\n elementId: a.elementId,\n author: a.author,\n content: a.content,\n tags: a.tags,\n replies: a.replies.length,\n }));\n\n return {\n revision: graph.revision,\n revisionToken: changesetRevisionToken(graph.changeset, graph.revision),\n changeset: {\n id: changeset.id,\n name: changeset.name,\n ...(changeset.description ? { description: changeset.description } : {}),\n status: changeset.status,\n created: changeset.created,\n modified: changeset.modified,\n ...(drift ? { drift } : {}),\n },\n summary: {\n elementsAdded: changed.filter((e) => e.status === \"added\").length,\n elementsUpdated: changed.filter((e) => e.status === \"updated\").length,\n elementsDeleted: changed.filter((e) => e.status === \"deleted\").length,\n relationshipsAdded: changedRels.filter((r) => r.status === \"added\").length,\n relationshipsDeleted: changedRels.filter((r) => r.status === \"deleted\").length,\n elementsAlreadyInBase: inBase.elements.length,\n relationshipsAlreadyInBase: inBase.relationships.length,\n },\n alreadyInBase: {\n elements: inBase.elements.map((e) => e.id),\n relationships: inBase.relationships.map((r) => ({ source: r.source, predicate: r.predicate, target: r.target })),\n },\n layers,\n relationshipChanges,\n reach,\n gaps,\n ...(unwired ? { unwired } : {}),\n intent: { traced, untraced, ...(unanchored.length > 0 ? { unanchored } : {}) },\n codeTouched,\n feedback,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction describeChange(graph: ChangesetGraph, element: GraphElement): BriefElementChange {\n const op = element.status as BriefElementChange[\"op\"];\n const connections = graph\n .neighbors(element.id, { includeDeleted: true })\n .map((n): BriefConnection => {\n const other = graph.getElement(n.other);\n return {\n direction: n.direction,\n predicate: n.relationship.predicate,\n other: n.other,\n ...(other ? { otherName: other.name, otherLayer: other.layer } : {}),\n status: n.relationship.status,\n };\n });\n\n const change: BriefElementChange = {\n op,\n id: element.id,\n type: element.type,\n name: element.name,\n ...(element.description ? { description: element.description } : {}),\n connections,\n };\n if (op !== \"deleted\" && Object.keys(element.attributes).length > 0) {\n change.attributes = element.attributes;\n }\n if (op === \"updated\" && element.before) {\n change.changedFields = diffFields(element.before, element);\n }\n return change;\n}\n\nfunction diffFields(\n before: Record<string, unknown>,\n after: GraphElement\n): Array<{ field: string; before: unknown; after: unknown }> {\n const fields: Array<{ field: string; before: unknown; after: unknown }> = [];\n const compare = (field: string, a: unknown, b: unknown) => {\n if (JSON.stringify(a) !== JSON.stringify(b)) fields.push({ field, before: a, after: b });\n };\n compare(\"name\", before.name, after.name);\n compare(\"description\", before.description, after.description);\n const beforeAttrs = (before.attributes as Record<string, unknown> | undefined) ?? {};\n for (const key of new Set([...Object.keys(beforeAttrs), ...Object.keys(after.attributes)])) {\n compare(`attributes.${key}`, beforeAttrs[key], after.attributes[key]);\n }\n return fields;\n}\n\n/**\n * The intent trace: a breadth-first walk from a changed element to the nearest intent element,\n * at most INTENT_TRACE_DEPTH hops, that does not walk through hubs (elements with more than\n * `hubThreshold` live relationships; a hub can still be reached). An existing intent element ends\n * the walk. An intent element the changeset adds ends it only when it is anchored, i.e. it traces\n * the same way to an existing intent element; an unanchored one is remembered and walked through.\n */\nclass IntentWalk {\n private readonly anchors = new Map<string, { id: string; path: string[] } | null>();\n\n constructor(\n private readonly graph: ChangesetGraph,\n private readonly hubThreshold: number\n ) {}\n\n trace(start: string): {\n traced?: Omit<IntentTrace, \"element\">;\n unanchored?: { id: string; path: string[] };\n } {\n let unanchored: { id: string; path: string[] } | undefined;\n const hit = this.walk(start, (element, path) => {\n if (element.status !== \"added\") return true;\n if (this.anchorOf(element.id)) return true;\n unanchored ??= { id: element.id, path };\n return false;\n });\n if (!hit) return unanchored ? { unanchored } : {};\n const anchor = this.graph.getElement(hit.id)?.status === \"added\" ? this.anchorOf(hit.id) : null;\n return {\n traced: {\n tracesTo: hit.id,\n distance: hit.path.length - 1,\n path: hit.path,\n ...(anchor ? { anchoredBy: anchor } : {}),\n },\n };\n }\n\n /** The existing intent element an added intent element traces to, or null. */\n private anchorOf(id: string): { id: string; path: string[] } | null {\n if (this.anchors.has(id)) return this.anchors.get(id)!;\n this.anchors.set(id, null); // a cycle back to it finds nothing\n const hit = this.walk(id, (element) => element.status !== \"added\");\n this.anchors.set(id, hit);\n return hit;\n }\n\n /**\n * Walks from `start` and returns the first intent element `accept` takes, with the path to it.\n * Intent elements it refuses are walked through like any other.\n */\n private walk(\n start: string,\n accept: (element: GraphElement, path: string[]) => boolean\n ): { id: string; path: string[] } | null {\n const parent = new Map<string, string>();\n const visited = new Set([start]);\n const pathTo = (id: string): string[] => {\n const path = [id];\n for (let at = parent.get(id); at !== undefined; at = parent.get(at)) path.unshift(at);\n return path;\n };\n let frontier = [start];\n for (let distance = 1; distance <= INTENT_TRACE_DEPTH && frontier.length > 0; distance++) {\n const next: string[] = [];\n for (const id of frontier) {\n if (id !== start && this.graph.neighbors(id).length > this.hubThreshold) continue;\n for (const n of this.graph.neighbors(id)) {\n if (visited.has(n.other)) continue;\n visited.add(n.other);\n const element = this.graph.getElement(n.other);\n if (!element || element.status === \"deleted\") continue;\n parent.set(element.id, id);\n if (INTENT_LAYERS.has(element.layer) && accept(element, pathTo(element.id))) {\n return { id: element.id, path: pathTo(element.id) };\n }\n next.push(element.id);\n }\n }\n frontier = next;\n }\n return null;\n }\n}\n\n/** A peer pattern counts when at least this many peers, and at least half of them, have it. */\nconst PEER_MIN = 2;\nconst PEER_SHARE = 0.5;\n\n/**\n * Connections an added element's peers usually have and it lacks. Peers are existing elements of\n * the same type that relate to one of the element's neighbours the same way it does (another\n * layout function that also realizes the layout service). Only shapes the catalog still allows are\n * reported, so a convention the spec has since retired is not suggested.\n */\nfunction peerPatternGaps(graph: ChangesetGraph, element: GraphElement): BriefGap[] {\n const own = graph.neighbors(element.id);\n if (own.length === 0) return [];\n const type = specNodeId(element);\n\n const peers = new Set<string>();\n for (const mine of own) {\n for (const theirs of graph.neighbors(mine.other)) {\n // Seen from the shared neighbour, a peer's relationship points the opposite way to ours.\n if (theirs.relationship.predicate !== mine.relationship.predicate || theirs.direction === mine.direction) continue;\n const peer = graph.getElement(theirs.other);\n if (!peer || peer.id === element.id || peer.status === \"added\" || peer.status === \"deleted\") continue;\n if (specNodeId(peer) === type) peers.add(peer.id);\n }\n }\n if (peers.size < PEER_MIN) return [];\n\n const shapeOf = (n: Neighbor): string | null => {\n const other = graph.getElement(n.other);\n return other ? `${n.direction}|${n.relationship.predicate}|${specNodeId(other)}` : null;\n };\n const ownShapes = new Set(own.map(shapeOf));\n\n const shapes = new Map<string, { peers: Set<string>; examples: string[] }>();\n for (const peerId of peers) {\n for (const n of graph.neighbors(peerId)) {\n if (n.other === element.id) continue;\n const shape = shapeOf(n);\n if (!shape || ownShapes.has(shape)) continue;\n const entry = shapes.get(shape) ?? { peers: new Set<string>(), examples: [] };\n if (!entry.peers.has(peerId) && entry.examples.length < 3) entry.examples.push(describeHop(peerId, n));\n entry.peers.add(peerId);\n shapes.set(shape, entry);\n }\n }\n\n const threshold = Math.max(PEER_MIN, Math.ceil(peers.size * PEER_SHARE));\n return [...shapes.entries()]\n .filter(([, entry]) => entry.peers.size >= threshold)\n .filter(([shape]) => {\n const [direction, predicate, otherType] = shape.split(\"|\");\n return direction === \"out\"\n ? getValidRelationships(type, predicate, otherType).length > 0\n : getValidRelationships(otherType, predicate, type).length > 0;\n })\n .sort(([, a], [, b]) => b.peers.size - a.peers.size)\n .map(([shape, entry]) => {\n const [direction, predicate, otherType] = shape.split(\"|\");\n const connection =\n direction === \"out\" ? `--${predicate}--> ${otherType}` : `${otherType} --${predicate}--> them`;\n return {\n kind: \"peer-pattern\" as const,\n element: element.id,\n detail: `${entry.peers.size} of ${peers.size} existing ${type} elements wired like this one have ${connection}; this one has none.`,\n examples: entry.examples,\n };\n });\n}\n\n/**\n * Unwired connections of every added or updated element: each triple the catalog defines with the\n * element's type at one end, for which the element has no live relationship with that predicate\n * to (or from) an element of the other end's type. Only catalog triples are considered.\n */\nexport function computeUnwired(graph: ChangesetGraph): ElementUnwired[] {\n // Existing (base, updated or not) and added elements per type, counted apart.\n const existing = new Map<string, number>();\n const added = new Map<string, number>();\n for (const element of graph.elements.values()) {\n if (element.status === \"deleted\") continue;\n const counts = element.status === \"added\" ? added : existing;\n const type = specNodeId(element);\n counts.set(type, (counts.get(type) ?? 0) + 1);\n }\n\n const result: ElementUnwired[] = [];\n for (const element of graph.changedElements()) {\n if (element.status !== \"added\" && element.status !== \"updated\") continue;\n const type = specNodeId(element);\n\n const have = new Set<string>();\n for (const n of graph.neighbors(element.id)) {\n const other = graph.getElement(n.other);\n if (other && other.status !== \"deleted\") have.add(`${n.direction}|${n.relationship.predicate}|${specNodeId(other)}`);\n }\n\n const entries = new Map<string, UnwiredEntry>();\n const consider = (direction: \"out\" | \"in\", predicate: string, otherType: string) => {\n const key = `${direction}|${predicate}|${otherType}`;\n if (have.has(key) || entries.has(key)) return;\n const self = otherType === type ? 1 : 0;\n const existingCount = (existing.get(otherType) ?? 0) - (element.status === \"added\" ? 0 : self);\n const addedCount = (added.get(otherType) ?? 0) - (element.status === \"added\" ? self : 0);\n entries.set(key, { direction, predicate, otherType, existingCount, addedCount });\n };\n for (const spec of RELATIONSHIPS_BY_SOURCE.get(type) ?? []) consider(\"out\", spec.predicate, spec.destinationSpecNodeId);\n for (const spec of RELATIONSHIPS_BY_DESTINATION.get(type) ?? []) consider(\"in\", spec.predicate, spec.sourceSpecNodeId);\n\n const layerOf = (specId: string) => specId.split(\".\")[0];\n const all = [...entries.values()];\n const byLayer = orderedLayers(all.map((e) => layerOf(e.otherType))).map((layer) => ({\n layer,\n entries: all\n .filter((e) => layerOf(e.otherType) === layer)\n .sort(\n (a, b) =>\n (a.direction === b.direction ? 0 : a.direction === \"out\" ? -1 : 1) ||\n a.predicate.localeCompare(b.predicate) ||\n a.otherType.localeCompare(b.otherType)\n ),\n }));\n result.push({ element: element.id, type, byLayer });\n }\n return result;\n}\n\n/** Shapes computed unwired connections for the brief: see `UnwiredMode`. */\nfunction presentUnwired(\n computed: ElementUnwired[],\n mode: Exclude<UnwiredMode, \"none\">,\n reachedLayers: Set<string>,\n layerFilter?: string[]\n): BriefUnwired {\n const known = new Set(getAllLayerIds());\n const filter = layerFilter ? new Set(layerFilter) : undefined;\n const unknownLayers = layerFilter?.filter((l) => !known.has(l)) ?? [];\n\n const elements = computed.map(({ element, type, byLayer }) => {\n const shown = byLayer\n .filter((l) => !filter || filter.has(l.layer))\n .map((l) => {\n const count = l.entries.length;\n if (mode === \"full\") return { layer: l.layer, count, entries: l.entries };\n // Summary: list only connections to types the projected model has; count the rest.\n const inModel = l.entries.filter((e) => e.existingCount + e.addedCount > 0);\n const typesNotInModel = count - inModel.length;\n const withEntries = reachedLayers.has(l.layer) || !!filter?.has(l.layer);\n return {\n layer: l.layer,\n count,\n ...(withEntries ? { entries: inModel } : {}),\n ...(typesNotInModel > 0 ? { typesNotInModel } : {}),\n };\n });\n return { element, type, total: shown.reduce((sum, l) => sum + l.count, 0), byLayer: shown };\n });\n\n return {\n mode,\n ...(layerFilter ? { layers: layerFilter } : {}),\n ...(unknownLayers.length > 0 ? { unknownLayers } : {}),\n elements,\n };\n}\n\n/** Key of one unwired entry of one element, for comparing two briefs. */\nexport function unwiredKey(element: string, entry: Pick<UnwiredEntry, \"direction\" | \"predicate\" | \"otherType\">): string {\n return `${element}|${entry.direction}|${entry.predicate}|${entry.otherType}`;\n}\n\nfunction requiredOutgoing(element: GraphElement) {\n return getValidRelationships(specNodeId(element)).filter((r) => r.required);\n}\n\nfunction specNodeId(element: GraphElement): string {\n return `${element.layer}.${normalizeNodeType(element.layer, element.type)}`;\n}\n\nfunction describeHop(from: string, neighbor: Neighbor): string {\n const { predicate, status } = neighbor.relationship;\n const suffix = status === \"unchanged\" ? \"\" : ` (${status} by this changeset)`;\n return neighbor.direction === \"out\"\n ? `${from} --${predicate}--> ${neighbor.other}${suffix}`\n : `${neighbor.other} --${predicate}--> ${from}${suffix}`;\n}\n\nfunction sourceFiles(element: GraphElement): string[] {\n const locations = element.sourceReference?.locations ?? [];\n return [...new Set(locations.map((l) => l.file).filter((f): f is string => !!f))];\n}\n\nfunction orderedLayers(layers: Iterable<string>): string[] {\n const present = new Set(layers);\n const known = getAllLayerIds().filter((l) => present.has(l));\n const unknown = [...present].filter((l) => !known.includes(l)).sort();\n return [...known, ...unknown];\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }