@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,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/changeset-verify.ts"],
4
+ "sourcesContent": ["/**\n * Changeset verify \u2014 compare what a change PLANNED (a staged changeset) with what was BUILT.\n *\n * The planned side is always a changeset: its net changes (adds, updates and deletes of elements and\n * relationships, replayed in sequence order). The built side is a `BuiltSide`, which can come from:\n *\n * - another changeset (`builtFromGraph`): an as-built changeset written by hand or, later, by the\n * extraction cascade. The built world is the base model with that changeset applied, and what the\n * build changed is known, so unplanned changes are reported in full.\n * - the base model (`builtFromBase`): e.g. after an as-built changeset was committed on the branch.\n * What the build changed is not known, so unplanned changes are limited to relationships the build\n * gave the elements the plan adds.\n * - a plain snapshot of elements and relationships (`builtFromSnapshot`), the shape an extraction\n * cascade can produce without writing a changeset first.\n *\n * Matching is tolerant of renames. Elements match by id first, then by layer + type + normalized\n * name, then by layer + normalized name with a different type, then (optionally) by layer + type +\n * shared source file. Anything other than an id match is reported as a probable match with its\n * reason, never as exact. Relationships match by (source, predicate, target) after mapping the\n * planned endpoints through the element matches, so a relationship between renamed elements still\n * matches (probably).\n *\n * See docs/CHANGE_MODELING.md (Verify).\n */\n\nimport { Changeset, type StagedChange } from \"./changeset.js\";\nimport { ChangesetGraph, type GraphElement, type GraphRelationship } from \"./changeset-graph.js\";\nimport type { Model } from \"./model.js\";\nimport type { SourceReference } from \"../types/source-reference.js\";\nimport { tryReadBaseIndex } from \"./changeset-base-index.js\";\nimport { stagedSourceReference } from \"./staged-source-reference.js\";\n\n/**\n * Attribute fields whose expected progress is not a divergence: a plan says `status: planned`, the\n * build says `delivered`. Only a move to a later stage of LIFECYCLE_STAGES counts; a move back, or\n * to a value outside them, stays a divergence.\n */\nexport const LIFECYCLE_FIELDS: readonly string[] = [\"attributes.status\"];\n\n/**\n * The stages of the spec's status enums, in order (values in one stage are alternatives from\n * different types: goals, product items, milestones, test cases). Off-ramps such as `deprecated`,\n * `abandoned`, `cancelled` and `at-risk` are not stages, so moving to one is reported as a divergence.\n */\nexport const LIFECYCLE_STAGES: readonly (readonly string[])[] = [\n [\"proposed\", \"draft\"],\n [\"planned\", \"under-review\"],\n [\"approved\", \"in-progress\", \"developing\", \"active\"],\n [\"implemented\", \"delivered\", \"achieved\", \"completed\"],\n [\"automated\"],\n];\n\nfunction lifecycleStage(value: unknown): number {\n if (typeof value !== \"string\") return -1;\n const v = value.trim().toLowerCase();\n return LIFECYCLE_STAGES.findIndex((stage) => stage.includes(v));\n}\n\n/** Whether a field difference is the build moving a lifecycle field to a later stage. */\nexport function isLifecycleMove(difference: FieldDifference): boolean {\n if (!LIFECYCLE_FIELDS.includes(difference.field)) return false;\n const from = lifecycleStage(difference.planned);\n const to = lifecycleStage(difference.built);\n return from >= 0 && to > from;\n}\n\n// ---------------------------------------------------------------------------\n// Shapes\n// ---------------------------------------------------------------------------\n\nexport interface VerifyElement {\n /** Dotted path (`layer.type.name`). */\n id: string;\n layer: string;\n type: string;\n name: string;\n description?: string;\n attributes: Record<string, unknown>;\n sourceReference?: SourceReference;\n}\n\nexport interface VerifyRelationship {\n source: string;\n predicate: string;\n target: string;\n}\n\nexport interface PlannedElementChange extends VerifyElement {\n op: \"add\" | \"update\" | \"delete\";\n /** For updates: the fields the plan changes (`description`, `attributes.priority`, ...). */\n changedFields?: string[];\n /** For updates: each changed field's value before the plan, to tell a build that left it alone from one that changed it differently. */\n baseValues?: Record<string, unknown>;\n}\n\nexport interface PlannedRelationshipChange extends VerifyRelationship {\n op: \"add\" | \"delete\";\n}\n\nexport interface Plan {\n label: string;\n elements: PlannedElementChange[];\n relationships: PlannedRelationshipChange[];\n /** Changes that could not be read, and anything else worth knowing about the plan. */\n notes: string[];\n /**\n * Ids of the elements the base model had when the plan was created (its base index). Such an\n * element cannot be the built form of a planned add. Undefined when no index was recorded.\n */\n baseElementIds?: string[];\n}\n\nexport type BuiltChange = \"added\" | \"updated\" | \"deleted\";\n\nexport interface BuiltElement extends VerifyElement {\n change?: BuiltChange;\n}\n\nexport interface BuiltRelationship extends VerifyRelationship {\n change?: Exclude<BuiltChange, \"updated\">;\n}\n\nexport interface BuiltSide {\n label: string;\n kind: \"changeset\" | \"base\" | \"snapshot\";\n /** Elements that exist in the built world. */\n elements: BuiltElement[];\n /** Relationships that exist in the built world. */\n relationships: BuiltRelationship[];\n /**\n * What the build changed, deletions included. Undefined when it is not known (the base model);\n * unplanned changes are then limited to the neighbourhood of the plan's additions.\n */\n changedElements?: BuiltElement[];\n changedRelationships?: BuiltRelationship[];\n notes: string[];\n}\n\nexport type MatchKind = \"exact\" | \"probable\";\n\nexport interface FieldDifference {\n field: string;\n planned: unknown;\n built: unknown;\n}\n\nexport interface ElementMatch {\n planned: string;\n built: string;\n op: PlannedElementChange[\"op\"];\n match: MatchKind;\n /** Why a probable match was made. */\n reason?: string;\n /** Expected lifecycle progress (see LIFECYCLE_FIELDS): reported, not counted as divergence. */\n lifecycle?: FieldDifference[];\n}\n\nexport interface DivergentElement extends ElementMatch {\n fields: FieldDifference[];\n}\n\nexport interface MissingElement {\n planned: string;\n layer: string;\n type: string;\n name: string;\n op: PlannedElementChange[\"op\"];\n reason: string;\n /** Built elements that could be it, when matching was ambiguous. */\n candidates?: string[];\n /** For a planned update that the build does not carry: the fields it would have changed. */\n fields?: FieldDifference[];\n}\n\nexport interface UnplannedElement {\n id: string;\n layer: string;\n type: string;\n name: string;\n change: BuiltChange | \"present\";\n}\n\nexport interface DeletionNotDone {\n planned: string;\n built: string;\n}\n\nexport interface RelationshipMatch {\n planned: VerifyRelationship;\n built: VerifyRelationship;\n op: PlannedRelationshipChange[\"op\"];\n match: MatchKind;\n reason?: string;\n}\n\nexport interface DivergentRelationship extends RelationshipMatch {\n fields: FieldDifference[];\n}\n\nexport interface MissingRelationship {\n planned: VerifyRelationship;\n reason: string;\n /** Relationships between the same elements that may predate the change (built side without changes). */\n candidates?: VerifyRelationship[];\n}\n\nexport interface UnplannedRelationship extends VerifyRelationship {\n change: Exclude<BuiltChange, \"updated\"> | \"present\";\n}\n\nexport interface RelationshipDeletionNotDone {\n planned: VerifyRelationship;\n built: VerifyRelationship;\n}\n\nexport interface CategoryCounts {\n planned: number;\n /** Planned changes found in the build, divergent ones included (`divergent` is the subset that differs). */\n matched: number;\n probable: number;\n missing: number;\n divergent: number;\n unplanned: number;\n deletionsNotDone: number;\n}\n\nexport type Verdict = \"match\" | \"probable-match\" | \"diverges\" | \"incomplete\" | \"empty-plan\";\n\nexport interface VerifyReport {\n planned: { label: string; elements: number; relationships: number };\n against: { label: string; kind: BuiltSide[\"kind\"] };\n verdict: Verdict;\n verdictText: string;\n summary: { elements: CategoryCounts; relationships: CategoryCounts };\n elements: {\n matched: ElementMatch[];\n divergent: DivergentElement[];\n missing: MissingElement[];\n unplanned: UnplannedElement[];\n deletionsNotDone: DeletionNotDone[];\n };\n relationships: {\n matched: RelationshipMatch[];\n divergent: DivergentRelationship[];\n missing: MissingRelationship[];\n unplanned: UnplannedRelationship[];\n deletionsNotDone: RelationshipDeletionNotDone[];\n };\n notes: string[];\n}\n\nexport interface VerifyOptions {\n /** Match elements of the same layer and type that share a source file (default true). */\n matchBySource?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Planned side\n// ---------------------------------------------------------------------------\n\n/**\n * The net changes of a changeset, replayed in sequence order: an add later updated is an add, an\n * add later deleted is nothing, a relationship added and then removed is nothing.\n *\n * `resolve` maps UUIDs to dotted paths (pass `graph.resolve` from the changeset's own graph);\n * `lookup` supplies an element's record when a delete carries no `before` snapshot.\n */\nexport function planFromChangeset(\n changeset: Changeset,\n context: { resolve?: (id: string) => string; lookup?: (id: string) => GraphElement | undefined } = {}\n): Plan {\n const resolve = context.resolve ?? ((id: string) => id);\n const notes: string[] = [];\n const elements = new Map<string, PlannedElementChange & { before?: VerifyElement }>();\n const relationships = new Map<string, PlannedRelationshipChange>();\n\n const changes = [...(changeset.changes as StagedChange[])]\n .map((change, index) => ({ change, index }))\n .sort((a, b) => (a.change.sequenceNumber ?? a.index) - (b.change.sequenceNumber ?? b.index));\n\n for (const { change, index } of changes) {\n const where = `change #${change.sequenceNumber ?? index} (${change.type} ${change.elementId})`;\n\n if (change.type === \"relationship-add\" || change.type === \"relationship-delete\") {\n const rel = readRelationship(change, resolve);\n if (!rel) {\n notes.push(`Skipped ${where}: it does not name a source, predicate and target.`);\n continue;\n }\n const key = relKey(rel);\n const previous = relationships.get(key);\n if (change.type === \"relationship-add\") {\n // Re-adding a relationship this changeset removed restores it: no net change.\n if (previous?.op === \"delete\") relationships.delete(key);\n else relationships.set(key, { ...rel, op: \"add\" });\n } else {\n if (previous?.op === \"add\") relationships.delete(key);\n else relationships.set(key, { ...rel, op: \"delete\" });\n }\n continue;\n }\n\n const id = resolve(change.elementId);\n const previous = elements.get(id);\n\n if (change.type === \"add\" || change.type === \"update\") {\n if (!change.after) {\n notes.push(`Skipped ${where}: it has no element data.`);\n continue;\n }\n const record = toVerifyElement(change.after, change.layerName, id);\n if (change.type === \"add\" && previous?.op !== \"delete\") {\n elements.set(id, { ...record, op: \"add\" });\n } else if (previous?.op === \"add\") {\n elements.set(id, { ...record, op: \"add\" });\n } else {\n const before =\n previous?.before ??\n (change.before ? toVerifyElement(change.before, change.layerName, id) : undefined);\n const fields = before ? changedFields(before, record) : undefined;\n elements.set(id, {\n ...record,\n op: \"update\",\n ...(before && fields\n ? { before, changedFields: fields, baseValues: Object.fromEntries(fields.map((f) => [f, fieldValue(before, f)])) }\n : {}),\n });\n }\n continue;\n }\n\n if (change.type === \"delete\") {\n if (previous?.op === \"add\") {\n elements.delete(id);\n continue;\n }\n const known = context.lookup?.(id);\n const before =\n previous?.before ??\n (change.before\n ? toVerifyElement(change.before, change.layerName, id)\n : known\n ? fromGraphElement(known)\n : fromId(id, change.layerName));\n elements.set(id, { ...before, id, op: \"delete\", before });\n continue;\n }\n\n notes.push(`Skipped ${where}: unknown change type.`);\n }\n\n return {\n label: changeset.name,\n elements: [...elements.values()].map(({ before: _before, ...rest }) => rest),\n relationships: [...relationships.values()],\n notes,\n };\n}\n\n/** The planned side of a changeset, read through its projected graph over the model. */\nexport async function planFromModel(model: Model, changeset: Changeset): Promise<Plan> {\n const graph = await ChangesetGraph.build(model, changeset);\n return planFromChangeset(changeset, {\n resolve: (id) => graph.resolve(id),\n lookup: (id) => graph.getElement(id),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Built side\n// ---------------------------------------------------------------------------\n\n/** The built world of an as-built changeset: base + its changes, with what it changed. */\nexport function builtFromGraph(graph: ChangesetGraph, label = graph.changeset.name): BuiltSide {\n const elements: BuiltElement[] = [];\n const changedElements: BuiltElement[] = [];\n for (const element of graph.elements.values()) {\n const built: BuiltElement = {\n ...fromGraphElement(element),\n ...(element.status !== \"unchanged\" ? { change: element.status } : {}),\n };\n if (element.status !== \"deleted\") elements.push(built);\n if (element.status !== \"unchanged\") changedElements.push(built);\n }\n\n const relationships: BuiltRelationship[] = [];\n const changedRelationships: BuiltRelationship[] = [];\n for (const rel of graph.relationships()) {\n const built: BuiltRelationship = {\n source: rel.source,\n predicate: rel.predicate,\n target: rel.target,\n ...(rel.status !== \"unchanged\" ? { change: rel.status } : {}),\n };\n if (rel.status !== \"deleted\") relationships.push(built);\n if (rel.status !== \"unchanged\") changedRelationships.push(built);\n }\n\n return { label, kind: \"changeset\", elements, relationships, changedElements, changedRelationships, notes: [] };\n}\n\n/** The base model as the built world. What the build changed is not known. */\nexport async function builtFromBase(model: Model): Promise<BuiltSide> {\n const now = new Date().toISOString();\n const empty = new Changeset({\n id: \"base\",\n name: \"base model\",\n created: now,\n modified: now,\n status: \"committed\",\n baseSnapshot: \"\",\n changes: [],\n });\n const graph = await ChangesetGraph.build(model, empty);\n const side = builtFromGraph(graph, \"base model\");\n return {\n label: \"base model\",\n kind: \"base\",\n elements: side.elements,\n relationships: side.relationships,\n notes: [\n \"Compared with the base model: what the build changed is not recorded, so unplanned changes are limited \" +\n \"to relationships the build gave the elements this plan adds. Compare with an as-built changeset for \" +\n \"a full list.\",\n ],\n };\n}\n\n/**\n * A built side from plain elements and relationships, e.g. the output of an extraction pass.\n * With `allChanged`, every element and relationship given counts as a change of the build (an\n * extraction scoped to the branch's diff); otherwise they are the built world only.\n */\nexport function builtFromSnapshot(snapshot: {\n label: string;\n elements: BuiltElement[];\n relationships: BuiltRelationship[];\n allChanged?: boolean;\n notes?: string[];\n}): BuiltSide {\n const elements = snapshot.elements.filter((e) => e.change !== \"deleted\");\n const relationships = snapshot.relationships.filter((r) => r.change !== \"deleted\");\n return {\n label: snapshot.label,\n kind: \"snapshot\",\n elements,\n relationships,\n ...(snapshot.allChanged\n ? {\n changedElements: snapshot.elements.map((e) => ({ ...e, change: e.change ?? \"added\" })),\n changedRelationships: snapshot.relationships.map((r) => ({ ...r, change: r.change ?? \"added\" })),\n }\n : {}),\n notes: snapshot.notes ?? [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Verify\n// ---------------------------------------------------------------------------\n\nexport function verifyChangeset(plan: Plan, built: BuiltSide, options: VerifyOptions = {}): VerifyReport {\n const matchBySource = options.matchBySource ?? true;\n const notes = [...plan.notes, ...built.notes];\n\n const builtById = new Map(built.elements.map((e) => [e.id, e]));\n const changedIds = new Set((built.changedElements ?? []).map((e) => e.id));\n const claimed = new Set<string>();\n /** planned id \u2192 built id, with how it matched. */\n const elementMap = new Map<string, { built: string; match: MatchKind; reason?: string }>();\n\n const report: VerifyReport = {\n planned: { label: plan.label, elements: plan.elements.length, relationships: plan.relationships.length },\n against: { label: built.label, kind: built.kind },\n verdict: \"match\",\n verdictText: \"\",\n summary: { elements: emptyCounts(), relationships: emptyCounts() },\n elements: { matched: [], divergent: [], missing: [], unplanned: [], deletionsNotDone: [] },\n relationships: { matched: [], divergent: [], missing: [], unplanned: [], deletionsNotDone: [] },\n notes,\n };\n\n // --- Elements: exact ids first, so a fuzzy match never takes an element another plan entry names.\n const live = plan.elements.filter((e) => e.op !== \"delete\");\n const deletions = plan.elements.filter((e) => e.op === \"delete\");\n\n for (const planned of live) {\n if (builtById.has(planned.id)) {\n claimed.add(planned.id);\n elementMap.set(planned.id, { built: planned.id, match: \"exact\" });\n }\n }\n\n const pending = live.filter((e) => !elementMap.has(e.id));\n const ambiguous = new Map<string, string[]>();\n const tiers: Array<{\n test: (planned: VerifyElement, candidate: VerifyElement) => boolean;\n reason: (planned: VerifyElement, candidate: VerifyElement) => string;\n }> = [\n {\n test: (p, c) => p.layer === c.layer && sameType(p, c) && namesMatch(p, c),\n reason: (p, c) =>\n `same layer and type, name matches ignoring case and separators (\"${p.name}\" ~ \"${c.name}\")`,\n },\n {\n test: (p, c) => p.layer === c.layer && !sameType(p, c) && namesMatch(p, c),\n reason: (p, c) => `same layer and name, but built as ${c.type} instead of ${p.type}`,\n },\n ];\n if (matchBySource) {\n tiers.push({\n test: (p, c) => p.layer === c.layer && sameType(p, c) && sharesSourceFile(p, c),\n reason: (p, c) => `same layer and type, shares source file ${sharedSourceFiles(p, c).join(\", \")}`,\n });\n }\n\n // A planned add cannot have been built as an element that existed before the plan. With the plan's\n // base index that is exact; without one, an unchanged element with no relationship to the plan's\n // elements is taken to predate it.\n const baseIds = plan.baseElementIds ? new Set(plan.baseElementIds) : undefined;\n const builtNeighbors = new Map<string, Set<string>>();\n for (const rel of built.relationships) {\n for (const [a, b] of [\n [rel.source, rel.target],\n [rel.target, rel.source],\n ]) {\n const set = builtNeighbors.get(a) ?? new Set<string>();\n set.add(b);\n builtNeighbors.set(a, set);\n }\n }\n const planTouched = new Set<string>([\n ...plan.elements.map((e) => e.id),\n ...plan.relationships.flatMap((r) => [r.source, r.target]),\n ]);\n const neighborCache = new Map<string, Set<string>>();\n const plannedNeighbors = (id: string): Set<string> => {\n const cached = neighborCache.get(id);\n if (cached) return cached;\n const own = plan.relationships\n .filter((r) => r.op === \"add\" && (r.source === id || r.target === id))\n .map((r) => (r.source === id ? r.target : r.source));\n const ids = own.length > 0 ? own : [...planTouched].filter((other) => other !== id);\n const near = new Set(ids.flatMap((other) => [other, elementMap.get(other)?.built ?? other]));\n neighborCache.set(id, near);\n return near;\n };\n let fellBack = false;\n const canBeBuildOf = (planned: PlannedElementChange, candidate: VerifyElement): boolean => {\n if (planned.op !== \"add\") return true;\n if (baseIds) return !baseIds.has(candidate.id);\n if (changedIds.has(candidate.id)) return true;\n fellBack = true;\n const near = plannedNeighbors(planned.id);\n for (const other of builtNeighbors.get(candidate.id) ?? []) if (near.has(other)) return true;\n return false;\n };\n\n for (const tier of tiers) {\n for (const planned of pending) {\n if (elementMap.has(planned.id)) continue;\n const candidates = built.elements.filter(\n (c) => !claimed.has(c.id) && tier.test(planned, c) && canBeBuildOf(planned, c)\n );\n if (candidates.length === 0) continue;\n let chosen = candidates;\n if (chosen.length > 1 && changedIds.size > 0) {\n const changed = chosen.filter((c) => changedIds.has(c.id));\n if (changed.length > 0) chosen = changed;\n }\n if (chosen.length === 1) {\n const candidate = chosen[0];\n claimed.add(candidate.id);\n elementMap.set(planned.id, { built: candidate.id, match: \"probable\", reason: tier.reason(planned, candidate) });\n ambiguous.delete(planned.id);\n } else if (!ambiguous.has(planned.id)) {\n ambiguous.set(\n planned.id,\n chosen.map((c) => c.id)\n );\n }\n }\n }\n\n if (fellBack) {\n notes.push(\n `No base index was recorded for ${plan.label}, so the elements that existed before it are not known exactly: ` +\n \"unchanged elements with no relationship to the plan's elements were not considered as the built form of \" +\n \"a planned add.\"\n );\n }\n\n const lifecycleMoves: string[] = [];\n for (const planned of live) {\n const mapping = elementMap.get(planned.id);\n if (!mapping) {\n const candidates = ambiguous.get(planned.id);\n report.elements.missing.push({\n planned: planned.id,\n layer: planned.layer,\n type: planned.type,\n name: planned.name,\n op: planned.op,\n reason: candidates\n ? `several built elements could be it (${candidates.length}); none matched uniquely`\n : planned.op === \"update\"\n ? \"the element the plan updates does not exist in the build\"\n : \"not found in the build by id, name or source file\",\n ...(candidates ? { candidates } : {}),\n });\n continue;\n }\n\n const builtElement = builtById.get(mapping.built)!;\n const fields = compareElements(planned, builtElement, planned.op === \"update\" ? planned.changedFields : undefined);\n const match: ElementMatch = {\n planned: planned.id,\n built: mapping.built,\n op: planned.op,\n match: mapping.match,\n ...(mapping.reason ? { reason: mapping.reason } : {}),\n };\n\n if (planned.op === \"update\" && planned.changedFields && planned.changedFields.length > 0) {\n const unmet = fields.filter((f) => planned.changedFields!.includes(f.field));\n // Missing only when the build left every field the plan changes alone. A field the build changed\n // to something else is a divergence, reported below with both values.\n const touched = unmet.some((f) => buildTouched(planned, builtElement, f.field));\n if (unmet.length === planned.changedFields.length && !touched) {\n report.elements.missing.push({\n planned: planned.id,\n layer: planned.layer,\n type: planned.type,\n name: planned.name,\n op: planned.op,\n reason: \"the element exists but the build does not carry the planned update\",\n fields: unmet,\n });\n continue;\n }\n }\n\n const lifecycle = fields.filter(isLifecycleMove);\n const differences = fields.filter((f) => !isLifecycleMove(f));\n if (lifecycle.length > 0) {\n match.lifecycle = lifecycle;\n for (const f of lifecycle) {\n lifecycleMoves.push(`${planned.id} ${f.field} ${String(f.planned)} \u2192 ${String(f.built)}`);\n }\n }\n if (differences.length > 0) report.elements.divergent.push({ ...match, fields: differences });\n else report.elements.matched.push(match);\n }\n\n if (lifecycleMoves.length > 0) {\n notes.push(\n `Lifecycle progress, not counted as divergence (${LIFECYCLE_FIELDS.join(\", \")} moving to a later stage of ` +\n `${LIFECYCLE_STAGES.map((stage) => stage.join(\"/\")).join(\" \u2192 \")}): ${lifecycleMoves.join(\"; \")}.`\n );\n }\n\n for (const planned of deletions) {\n const still = builtById.get(planned.id);\n if (still) {\n claimed.add(still.id);\n report.elements.deletionsNotDone.push({ planned: planned.id, built: still.id });\n } else {\n claimed.add(planned.id);\n report.elements.matched.push({ planned: planned.id, built: planned.id, op: \"delete\", match: \"exact\" });\n }\n }\n\n if (built.changedElements) {\n for (const element of built.changedElements) {\n if (claimed.has(element.id)) continue;\n report.elements.unplanned.push({\n id: element.id,\n layer: element.layer,\n type: element.type,\n name: element.name,\n change: element.change ?? \"added\",\n });\n }\n }\n\n // --- Relationships, through the element matches.\n const builtRels = new Map(built.relationships.map((r) => [relKey(r), r]));\n const claimedRels = new Set<string>();\n const mapId = (id: string) => elementMap.get(id)?.built ?? id;\n const probableEndpoints = (rel: VerifyRelationship): string[] =>\n [rel.source, rel.target]\n .filter((id, i, all) => all.indexOf(id) === i)\n .filter((id) => elementMap.get(id)?.match === \"probable\")\n .map((id) => `${id} \u2192 ${elementMap.get(id)!.built}`);\n const missingEndpoints = new Set(report.elements.missing.map((m) => m.planned));\n\n const mapRel = (planned: PlannedRelationshipChange): VerifyRelationship => ({\n source: mapId(planned.source),\n predicate: planned.predicate,\n target: mapId(planned.target),\n });\n const matchInfoFor = (planned: PlannedRelationshipChange) => {\n const probable = probableEndpoints(planned);\n return probable.length > 0\n ? { match: \"probable\" as const, reason: `through probable element match: ${probable.join(\"; \")}` }\n : { match: \"exact\" as const };\n };\n\n // Pass 1: exact (source, predicate, target) hits, so the fallback below can never take a built\n // relationship that another planned relationship matches exactly.\n const unresolved: PlannedRelationshipChange[] = [];\n for (const planned of plan.relationships) {\n const mapped = mapRel(planned);\n const found = builtRels.get(relKey(mapped));\n if (!found || claimedRels.has(relKey(found))) {\n unresolved.push(planned);\n continue;\n }\n claimedRels.add(relKey(found));\n const plannedRef = strip(planned);\n if (planned.op === \"delete\") {\n report.relationships.deletionsNotDone.push({ planned: plannedRef, built: strip(found) });\n } else {\n report.relationships.matched.push({ planned: plannedRef, built: strip(found), op: \"add\", ...matchInfoFor(planned) });\n }\n }\n\n // Elements the plan adds, as built: a relationship touching one cannot predate the change.\n const addedBuiltIds = new Set(\n live.filter((e) => e.op === \"add\" && elementMap.has(e.id)).map((e) => elementMap.get(e.id)!.built)\n );\n\n // Pass 2: everything without an exact hit.\n for (const planned of unresolved) {\n const mapped = mapRel(planned);\n const plannedRef = strip(planned);\n const matchInfo = matchInfoFor(planned);\n\n if (planned.op === \"delete\") {\n claimedRels.add(relKey(mapped));\n report.relationships.matched.push({ planned: plannedRef, built: mapped, op: \"delete\", ...matchInfo });\n continue;\n }\n\n // Same endpoints, another predicate; or the same predicate the other way round.\n // Prefer what the build changed: an untouched base relationship is not the build's version of this one.\n const isAlternative = (r: VerifyRelationship) =>\n !claimedRels.has(relKey(r)) &&\n ((r.source === mapped.source && r.target === mapped.target && r.predicate !== mapped.predicate) ||\n (r.source === mapped.target && r.target === mapped.source && r.predicate === mapped.predicate));\n const alternative = built.changedRelationships\n ? built.changedRelationships.filter((r) => r.change !== \"deleted\").find(isAlternative)\n : // What the build changed is not known: only a relationship touching an element the plan adds\n // can be the build's version; one between pre-existing elements may simply predate the change.\n built.relationships.find(\n (r) => isAlternative(r) && (addedBuiltIds.has(r.source) || addedBuiltIds.has(r.target))\n );\n if (alternative) {\n claimedRels.add(relKey(alternative));\n const fields: FieldDifference[] =\n alternative.predicate !== mapped.predicate\n ? [{ field: \"predicate\", planned: mapped.predicate, built: alternative.predicate }]\n : [\n {\n field: \"direction\",\n planned: `${mapped.source} \u2192 ${mapped.target}`,\n built: `${alternative.source} \u2192 ${alternative.target}`,\n },\n ];\n report.relationships.divergent.push({\n planned: plannedRef,\n built: strip(alternative),\n op: \"add\",\n ...matchInfo,\n fields,\n });\n continue;\n }\n\n // Near matches that may predate the change: listed, not claimed.\n const candidates = built.changedRelationships ? [] : built.relationships.filter(isAlternative).map(strip);\n const absent = [planned.source, planned.target].filter(\n (id) => missingEndpoints.has(id) || (!elementMap.has(id) && !builtById.has(id))\n );\n report.relationships.missing.push({\n planned: plannedRef,\n reason:\n absent.length > 0\n ? `not built; endpoint${absent.length > 1 ? \"s\" : \"\"} ${absent.join(\", \")} not found in the build`\n : candidates.length > 0\n ? \"not found in the build; a relationship between the same elements exists, but it may predate the change\"\n : \"not found in the build\",\n ...(candidates.length > 0 ? { candidates } : {}),\n });\n }\n\n if (built.changedRelationships) {\n for (const rel of built.changedRelationships) {\n if (claimedRels.has(relKey(rel))) continue;\n report.relationships.unplanned.push({ ...strip(rel), change: rel.change ?? \"added\" });\n }\n } else {\n // Base model: relationships the build gave the elements this plan adds, beyond the plan's own.\n for (const rel of built.relationships) {\n if (claimedRels.has(relKey(rel))) continue;\n if (addedBuiltIds.has(rel.source) || addedBuiltIds.has(rel.target)) {\n report.relationships.unplanned.push({ ...strip(rel), change: \"present\" });\n }\n }\n }\n\n report.summary.elements = countCategories(plan.elements.length, report.elements);\n report.summary.relationships = countCategories(plan.relationships.length, report.relationships);\n const { verdict, text } = decideVerdict(report);\n report.verdict = verdict;\n report.verdictText = text;\n return report;\n}\n\n/**\n * Verifies a planned changeset against an as-built changeset or the base model of `model`.\n * Used by the `changeset_verify` MCP tool and `dr changeset verify`.\n */\nexport async function verifyChangesetInModel(\n model: Model,\n options: {\n planned: Changeset;\n against: { changeset: Changeset } | { base: true };\n matchBySource?: boolean;\n }\n): Promise<VerifyReport & { plannedChangeset: { id: string; name: string } }> {\n const { planned } = options;\n const plan = await planFromModel(model, planned);\n const notes: string[] = [];\n const baseIndex = await tryReadBaseIndex(model.rootPath, planned.id);\n if (baseIndex.index) plan.baseElementIds = Object.keys(baseIndex.index.elements);\n if (baseIndex.warning) notes.push(baseIndex.warning);\n\n let built: BuiltSide;\n if (\"changeset\" in options.against) {\n const asBuilt = options.against.changeset;\n if (asBuilt.id === planned.id) {\n notes.push(\"The planned and as-built changesets are the same changeset, so everything matches by definition.\");\n }\n built = builtFromGraph(await ChangesetGraph.build(model, asBuilt), `${asBuilt.name} (${asBuilt.id})`);\n } else {\n built = await builtFromBase(model);\n if (planned.status === \"staged\") {\n notes.push(\n \"The planned changeset is still staged, so its own changes are not in the base model: anything \" +\n \"reported missing is missing from what was committed to the base (e.g. an as-built changeset).\"\n );\n }\n }\n\n const report = verifyChangeset(plan, built, { matchBySource: options.matchBySource });\n report.notes.push(...notes);\n return { plannedChangeset: { id: planned.id, name: planned.name }, ...report };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Lowercase with every non-alphanumeric removed: \"Export Orders\", \"export-orders\", \"exportOrders\" \u2192 \"exportorders\". */\nexport function normalizeName(name: string): string {\n return name.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n}\n\nfunction nameKeys(element: VerifyElement): Set<string> {\n const keys = new Set<string>();\n if (element.name) keys.add(normalizeName(element.name));\n const segment = idNameSegment(element.id);\n if (segment) keys.add(normalizeName(segment));\n keys.delete(\"\");\n return keys;\n}\n\nfunction namesMatch(a: VerifyElement, b: VerifyElement): boolean {\n const keys = nameKeys(b);\n for (const key of nameKeys(a)) if (keys.has(key)) return true;\n return false;\n}\n\nfunction sameType(a: VerifyElement, b: VerifyElement): boolean {\n return normalizeName(a.type) === normalizeName(b.type);\n}\n\nfunction sourceFiles(element: VerifyElement): string[] {\n return (element.sourceReference?.locations ?? [])\n .map((l) => (typeof l?.file === \"string\" ? l.file.replace(/^\\.\\//, \"\").replace(/\\\\/g, \"/\") : \"\"))\n .filter((f) => f.length > 0);\n}\n\nfunction sharedSourceFiles(a: VerifyElement, b: VerifyElement): string[] {\n const files = new Set(sourceFiles(b));\n return sourceFiles(a).filter((f) => files.has(f));\n}\n\nfunction sharesSourceFile(a: VerifyElement, b: VerifyElement): boolean {\n return sharedSourceFiles(a, b).length > 0;\n}\n\n/**\n * Fields where the built element differs from what the plan states. Only what the plan states is\n * compared: a description or attribute the build adds beyond the plan is not a divergence. The\n * exception is `removedBy`: fields a planned update changes that the planned record no longer has\n * (an attribute removed, a description cleared). The build must not have them either.\n */\nfunction compareElements(planned: VerifyElement, built: VerifyElement, removedBy?: string[]): FieldDifference[] {\n const fields: FieldDifference[] = [];\n if (planned.type && !sameType(planned, built)) {\n fields.push({ field: \"type\", planned: planned.type, built: built.type });\n }\n if (planned.name && normalizeName(planned.name) !== normalizeName(built.name)) {\n fields.push({ field: \"name\", planned: planned.name, built: built.name });\n }\n if (planned.description !== undefined && planned.description.trim() !== (built.description ?? \"\").trim()) {\n fields.push({ field: \"description\", planned: planned.description, built: built.description });\n }\n for (const [key, value] of Object.entries(planned.attributes ?? {})) {\n const builtValue = built.attributes?.[key];\n if (!sameValue(value, builtValue)) {\n fields.push({ field: `attributes.${key}`, planned: value, built: builtValue });\n }\n }\n for (const field of removedBy ?? []) {\n if (field === \"description\") {\n if (planned.description === undefined && (built.description ?? \"\").trim() !== \"\") {\n fields.push({ field, planned: undefined, built: built.description });\n }\n } else if (field.startsWith(\"attributes.\")) {\n const key = field.slice(\"attributes.\".length);\n const plannedHas = planned.attributes !== undefined && Object.hasOwn(planned.attributes, key);\n if (!plannedHas && built.attributes?.[key] !== undefined) {\n fields.push({ field, planned: undefined, built: built.attributes[key] });\n }\n }\n }\n return fields;\n}\n\n/** A field's value on an element, by the names `compareElements` and `changedFields` use. */\nfunction fieldValue(element: VerifyElement, field: string): unknown {\n if (field === \"type\") return element.type;\n if (field === \"name\") return element.name;\n if (field === \"description\") return element.description;\n if (field.startsWith(\"attributes.\")) return element.attributes?.[field.slice(\"attributes.\".length)];\n return undefined;\n}\n\nfunction sameFieldValue(field: string, a: unknown, b: unknown): boolean {\n if (field === \"type\" || field === \"name\") {\n return normalizeName(String(a ?? \"\")) === normalizeName(String(b ?? \"\"));\n }\n if (field === \"description\") return String(a ?? \"\").trim() === String(b ?? \"\").trim();\n return sameValue(a, b);\n}\n\n/**\n * Whether the build changed `field` from what it was before the plan. Uses the plan's recorded base\n * value; without one, falls back to whether the build changed the element at all (unknown against\n * the base model, where it counts as untouched).\n */\nfunction buildTouched(planned: PlannedElementChange, built: BuiltElement, field: string): boolean {\n if (planned.baseValues && Object.hasOwn(planned.baseValues, field)) {\n return !sameFieldValue(field, fieldValue(built, field), planned.baseValues[field]);\n }\n return built.change === \"updated\" || built.change === \"added\";\n}\n\nfunction changedFields(before: VerifyElement, after: VerifyElement): string[] {\n const fields: string[] = [];\n if (before.type !== after.type) fields.push(\"type\");\n if (before.name !== after.name) fields.push(\"name\");\n if ((before.description ?? \"\") !== (after.description ?? \"\")) fields.push(\"description\");\n const keys = new Set([...Object.keys(before.attributes ?? {}), ...Object.keys(after.attributes ?? {})]);\n for (const key of keys) {\n if (!sameValue(before.attributes?.[key], after.attributes?.[key])) fields.push(`attributes.${key}`);\n }\n return fields;\n}\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n return stableStringify(a) === stableStringify(b);\n}\n\nfunction stableStringify(value: unknown): string {\n if (value === undefined) return \"undefined\";\n if (value === null || typeof value !== \"object\") return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.keys(value as Record<string, unknown>)\n .sort()\n .map((k) => `${JSON.stringify(k)}:${stableStringify((value as Record<string, unknown>)[k])}`);\n return `{${entries.join(\",\")}}`;\n}\n\nfunction relKey(rel: VerifyRelationship): string {\n return `${rel.source}::${rel.predicate}::${rel.target}`;\n}\n\nfunction strip(rel: VerifyRelationship): VerifyRelationship {\n return { source: rel.source, predicate: rel.predicate, target: rel.target };\n}\n\nfunction readRelationship(change: StagedChange, resolve: (id: string) => string): VerifyRelationship | null {\n const data = (change.type === \"relationship-delete\" ? (change.before ?? change.after) : change.after) as\n | Partial<GraphRelationship>\n | undefined;\n const [source, predicate, target] =\n data?.source && data.predicate && data.target\n ? [data.source, data.predicate, data.target]\n : change.elementId.split(\"::\");\n if (!source || !predicate || !target) return null;\n return { source: resolve(source), predicate, target: resolve(target) };\n}\n\nfunction idNameSegment(id: string): string | undefined {\n const parts = id.split(\".\");\n return parts.length >= 3 ? parts.slice(2).join(\".\") : undefined;\n}\n\nfunction fromId(id: string, layerName: string): VerifyElement {\n const parts = id.split(\".\");\n return {\n id,\n layer: parts.length >= 3 ? parts[0] : layerName,\n type: parts.length >= 3 ? parts[1] : \"\",\n name: idNameSegment(id) ?? id,\n attributes: {},\n };\n}\n\nfunction fromGraphElement(element: GraphElement): VerifyElement {\n return {\n id: element.id,\n layer: element.layer,\n type: element.type,\n name: element.name,\n ...(element.description !== undefined ? { description: element.description } : {}),\n attributes: element.attributes ?? {},\n ...(element.sourceReference ? { sourceReference: element.sourceReference } : {}),\n };\n}\n\nfunction toVerifyElement(record: Record<string, unknown>, layerName: string, id: string): VerifyElement {\n const str = (value: unknown): string | undefined =>\n typeof value === \"string\" && value.length > 0 ? value : undefined;\n const fallback = fromId(id, layerName);\n return {\n id,\n layer: str(record.layer_id) ?? str(record.layer) ?? fallback.layer,\n type: str(record.type) ?? fallback.type,\n name: str(record.name) ?? fallback.name,\n ...(str(record.description) !== undefined ? { description: str(record.description) } : {}),\n attributes:\n record.attributes && typeof record.attributes === \"object\"\n ? (record.attributes as Record<string, unknown>)\n : {},\n ...(stagedSourceReference(record) ? { sourceReference: stagedSourceReference(record) } : {}),\n };\n}\n\nfunction emptyCounts(): CategoryCounts {\n return { planned: 0, matched: 0, probable: 0, missing: 0, divergent: 0, unplanned: 0, deletionsNotDone: 0 };\n}\n\nfunction countCategories(\n planned: number,\n section: {\n matched: Array<{ match: MatchKind }>;\n divergent: Array<{ match: MatchKind }>;\n missing: unknown[];\n unplanned: unknown[];\n deletionsNotDone: unknown[];\n }\n): CategoryCounts {\n return {\n planned,\n matched: section.matched.length + section.divergent.length,\n probable: [...section.matched, ...section.divergent].filter((m) => m.match === \"probable\").length,\n missing: section.missing.length,\n divergent: section.divergent.length,\n unplanned: section.unplanned.length,\n deletionsNotDone: section.deletionsNotDone.length,\n };\n}\n\nfunction decideVerdict(report: VerifyReport): { verdict: Verdict; text: string } {\n const e = report.summary.elements;\n const r = report.summary.relationships;\n if (e.planned === 0 && r.planned === 0) {\n return { verdict: \"empty-plan\", text: \"The plan has no changes to verify.\" };\n }\n\n const parts: string[] = [];\n const say = (n: number, what: string) => {\n if (n > 0) parts.push(`${n} ${what}`);\n };\n say(e.missing, plural(e.missing, \"planned element\", \"planned elements\") + \" missing\");\n say(r.missing, plural(r.missing, \"planned relationship\", \"planned relationships\") + \" missing\");\n say(e.deletionsNotDone + r.deletionsNotDone, \"planned deletion(s) not done\");\n say(e.divergent, plural(e.divergent, \"element differs\", \"elements differ\"));\n say(r.divergent, plural(r.divergent, \"relationship differs\", \"relationships differ\"));\n say(e.unplanned, plural(e.unplanned, \"unplanned element change\", \"unplanned element changes\"));\n say(r.unplanned, plural(r.unplanned, \"unplanned relationship\", \"unplanned relationships\"));\n\n const probable = e.probable + r.probable;\n const probableText = probable > 0 ? ` ${probable} match(es) are probable, not exact; confirm them.` : \"\";\n\n if (e.missing + r.missing + e.deletionsNotDone + r.deletionsNotDone > 0) {\n return { verdict: \"incomplete\", text: `The build is incomplete: ${parts.join(\", \")}.${probableText}` };\n }\n if (e.divergent + r.divergent + e.unplanned + r.unplanned > 0) {\n return { verdict: \"diverges\", text: `The build diverges from the plan: ${parts.join(\", \")}.${probableText}` };\n }\n if (probable > 0) {\n return { verdict: \"probable-match\", text: `Everything planned was found.${probableText}` };\n }\n return { verdict: \"match\", text: \"The build matches the plan.\" };\n}\n\nfunction plural(n: number, one: string, many: string): string {\n return n === 1 ? one : many;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["previous"]
7
+ }
@@ -1,3 +1,37 @@
1
+ function compactChange(change) {
2
+ const { type, elementId, layerName, before, after, timestamp } = change, keepBefore = before != null && type !== "add" && type !== "relationship-add";
3
+ return {
4
+ type,
5
+ elementId,
6
+ layerName,
7
+ ...keepBefore ? { before } : {},
8
+ ...after != null ? { after } : {},
9
+ ...timestamp !== void 0 ? { timestamp } : {}
10
+ };
11
+ }
12
+ function sequenceOf(change, index) {
13
+ const sequence = change.sequenceNumber;
14
+ return typeof sequence == "number" ? sequence : index;
15
+ }
16
+ function nextSequenceNumber(changeset) {
17
+ let max = Math.max(changeset.lastSequence ?? -1, changeset.unstagedPrunedThrough ?? -1);
18
+ changeset.changes.forEach((change, index) => {
19
+ max = Math.max(max, sequenceOf(change, index));
20
+ });
21
+ for (const tombstone of changeset.unstaged ?? [])
22
+ max = Math.max(max, tombstone.sequence, tombstone.removedAt);
23
+ return max + 1;
24
+ }
25
+ function withSequenceNumbers(changes, floor = 0) {
26
+ const used = changes.map((change) => change.sequenceNumber).filter((sequence) => typeof sequence == "number");
27
+ let next = Math.max(floor, used.length > 0 ? Math.max(...used) + 1 : 0);
28
+ return changes.map((change, index) => {
29
+ if (typeof change.sequenceNumber == "number")
30
+ return change;
31
+ const assigned = used.length === 0 ? index : next++;
32
+ return { ...change, sequenceNumber: assigned };
33
+ });
34
+ }
1
35
  class Changeset {
2
36
  name;
3
37
  description;
@@ -8,6 +42,12 @@ class Changeset {
8
42
  // Staging workflow fields (required)
9
43
  id;
10
44
  baseSnapshot;
45
+ /** The highest sequence number used, unstage events included (see `nextSequenceNumber`). */
46
+ lastSequence;
47
+ /** Recently unstaged changes, kept to rebuild earlier revisions; never committed or applied. */
48
+ unstaged = [];
49
+ /** Tombstones with `removedAt` at or below this were dropped to keep the list bounded. */
50
+ unstagedPrunedThrough;
11
51
  /**
12
52
  * Get statistics derived from changes array.
13
53
  * Computed on-demand to ensure always accurate regardless of how changes were added.
@@ -28,7 +68,7 @@ class Changeset {
28
68
  this.name = data.name, this.description = data.description, this.created = data.created, this.modified = data.modified, this.changes = (data.changes || []).map((change) => {
29
69
  const { type, elementId, layerName, before, after, timestamp, sequenceNumber } = change, result = { type, elementId, layerName, before, after, timestamp };
30
70
  return typeof sequenceNumber == "number" && (result.sequenceNumber = sequenceNumber), result;
31
- }), this.status = data.status || "staged", this.id = data.id, this.baseSnapshot = data.baseSnapshot;
71
+ }), this.status = data.status || "staged", this.id = data.id, this.baseSnapshot = data.baseSnapshot, typeof data.lastSequence == "number" && (this.lastSequence = data.lastSequence), Array.isArray(data.unstaged) && (this.unstaged = data.unstaged), typeof data.unstagedPrunedThrough == "number" && (this.unstagedPrunedThrough = data.unstagedPrunedThrough);
32
72
  }
33
73
  /**
34
74
  * Create a new changeset with staged status.
@@ -64,14 +104,16 @@ class Changeset {
64
104
  * @throws No exceptions; updates modified timestamp on success
65
105
  */
66
106
  addChange(type, elementId, layerName, before, after) {
107
+ const sequenceNumber = nextSequenceNumber(this);
67
108
  this.changes.push({
68
109
  type,
69
110
  elementId,
70
111
  layerName,
71
112
  before,
72
113
  after,
73
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
74
- }), this.updateModified();
114
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
115
+ sequenceNumber
116
+ }), this.lastSequence = sequenceNumber, this.updateModified();
75
117
  }
76
118
  /**
77
119
  * Update the modified timestamp to current time.
@@ -135,11 +177,12 @@ class Changeset {
135
177
  modified: this.modified,
136
178
  baseSnapshot: this.baseSnapshot,
137
179
  status: this.status,
138
- changes: this.changes.map((change, idx) => ({
139
- ...change,
140
- sequenceNumber: idx
141
- })),
142
- stats: this.stats
180
+ // Sequence numbers are kept as staged: they may have gaps where changes were unstaged.
181
+ changes: withSequenceNumbers(this.changes, nextSequenceNumber(this)),
182
+ stats: this.stats,
183
+ ...this.lastSequence !== void 0 ? { lastSequence: this.lastSequence } : {},
184
+ ...this.unstaged.length > 0 ? { unstaged: this.unstaged } : {},
185
+ ...this.unstagedPrunedThrough !== void 0 ? { unstagedPrunedThrough: this.unstagedPrunedThrough } : {}
143
186
  };
144
187
  }
145
188
  /**
@@ -154,6 +197,10 @@ class Changeset {
154
197
  }
155
198
  }
156
199
  export {
157
- Changeset
200
+ Changeset,
201
+ compactChange,
202
+ nextSequenceNumber,
203
+ sequenceOf,
204
+ withSequenceNumbers
158
205
  };
159
206
  //# sourceMappingURL=changeset.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/core/changeset.ts"],
4
- "sourcesContent": ["/**\n * Changeset - Represents a collection of model changes.\n *\n * Changesets group related modifications to the architecture model for:\n * - Staging changes before committing to the base model\n * - Audit trail of model changes with before/after snapshots\n * - Collaboration and change tracking across team members\n *\n * Uses the staging workflow (staged/committed/discarded).\n */\n\n/**\n * Represents a single change in a changeset.\n * Records element mutations with before/after snapshots for audit purposes.\n */\nexport interface Change {\n type: \"add\" | \"update\" | \"delete\" | \"relationship-add\" | \"relationship-delete\";\n elementId: string; // For relationships: composite key \"source::predicate::target\"\n layerName: string; // For relationships: source element's layer\n before?: Record<string, unknown>; // Element/relationship state before change\n after?: Record<string, unknown>; // Element/relationship state after change\n timestamp?: string; // ISO timestamp; optional in interface but always set before storage\n}\n\n/**\n * Represents a staged change with sequence number for ordered replay.\n * Used in the staging workflow to ensure changes are applied in order during commit.\n */\nexport interface StagedChange extends Change {\n readonly sequenceNumber: number; // 0-based index for ordering during commit replay\n}\n\n/**\n * Status values for changesets in the staging workflow.\n *\n * - 'staged': Created and active for staging changes (can accept new changes)\n * - 'committed': Changes have been committed to base model\n * - 'discarded': Changes were discarded without committing\n */\nexport type ChangesetStatus = \"staged\" | \"committed\" | \"discarded\";\n\n/**\n * Changeset data with staging semantics and metadata.\n * Includes id and base snapshot for drift detection and auto-computed stats.\n */\nexport interface StagedChangesetData {\n id: string; // Unique changeset ID\n name: string;\n description?: string;\n created: string; // ISO timestamp of creation\n modified: string; // ISO timestamp of last modification\n status: ChangesetStatus;\n baseSnapshot: string; // SHA256 hash of base model at creation (for drift detection)\n changes: Change[] | StagedChange[]; // Changes (with optional sequence numbers)\n stats?: {\n additions: number;\n modifications: number;\n deletions: number;\n };\n}\n\n/**\n * Changeset class for managing model changes.\n * Represents a staging workflow changeset (staged/committed/discarded).\n *\n * Stats are computed on-demand from the changes array, ensuring consistency.\n */\nexport class Changeset {\n name: string;\n description?: string;\n created: string;\n modified: string;\n changes: Change[] = [];\n status: ChangesetStatus = \"staged\";\n\n // Staging workflow fields (required)\n id: string;\n baseSnapshot: string;\n\n /**\n * Get statistics derived from changes array.\n * Computed on-demand to ensure always accurate regardless of how changes were added.\n *\n * @returns Statistics object with count of additions, modifications, deletions\n */\n get stats(): {\n additions: number;\n modifications: number;\n deletions: number;\n } {\n // Compute stats from changes array on-demand (never stale)\n const additions = this.changes.filter((c) => c.type === \"add\" || c.type === \"relationship-add\").length;\n const modifications = this.changes.filter((c) => c.type === \"update\").length;\n const deletions = this.changes.filter((c) => c.type === \"delete\" || c.type === \"relationship-delete\").length;\n\n return { additions, modifications, deletions };\n }\n\n /**\n * Create changeset from data object.\n * Accepts only StagedChangesetData format with id and baseSnapshot.\n *\n * @param data - Changeset data from storage or API\n */\n constructor(data: StagedChangesetData) {\n this.name = data.name;\n this.description = data.description;\n this.created = data.created;\n this.modified = data.modified;\n // Store changes; preserve sequenceNumber if present from storage\n this.changes = (data.changes || []).map((change) => {\n const { type, elementId, layerName, before, after, timestamp, sequenceNumber } = change as any;\n const result: any = { type, elementId, layerName, before, after, timestamp };\n // Preserve sequenceNumber if it exists (from loaded StagedChange)\n if (typeof sequenceNumber === \"number\") {\n result.sequenceNumber = sequenceNumber;\n }\n return result as Change;\n });\n this.status = data.status || \"staged\";\n this.id = data.id;\n this.baseSnapshot = data.baseSnapshot;\n // Note: Stats are computed from changes array via getter; ignore any stored stats\n }\n\n /**\n * Create a new changeset with staged status.\n *\n * @param name - Human-readable name for the changeset\n * @param description - Optional description of changes\n * @param id - Unique changeset ID (required)\n * @param baseSnapshot - SHA256 hash of base model at creation (required)\n * @returns New Changeset instance in staged status\n */\n static create(\n name: string,\n description: string | undefined,\n id: string,\n baseSnapshot: string\n ): Changeset {\n const now = new Date().toISOString();\n return new Changeset({\n name,\n description,\n created: now,\n modified: now,\n changes: [],\n status: \"staged\",\n id,\n baseSnapshot,\n });\n }\n\n /**\n * Add a change to the changeset.\n * Records element mutation with before/after snapshots for audit purposes.\n *\n * @param type - Type of change (add, update, delete)\n * @param elementId - ID of the element being changed\n * @param layerName - Layer containing the element\n * @param before - Element state before change (for update/delete operations)\n * @param after - Element state after change (for add/update operations)\n * @throws No exceptions; updates modified timestamp on success\n */\n addChange(\n type: \"add\" | \"update\" | \"delete\",\n elementId: string,\n layerName: string,\n before?: Record<string, unknown>,\n after?: Record<string, unknown>\n ): void {\n this.changes.push({\n type,\n elementId,\n layerName,\n before,\n after,\n timestamp: new Date().toISOString(),\n });\n this.updateModified();\n }\n\n /**\n * Update the modified timestamp to current time.\n */\n updateModified(): void {\n this.modified = new Date().toISOString();\n }\n\n /**\n * Get the total number of changes in this changeset.\n *\n * @returns Number of changes (add + update + delete)\n */\n getChangeCount(): number {\n return this.changes.length;\n }\n\n /**\n * Get all changes of a specific type.\n *\n * @param type - Type of changes to filter for\n * @returns Array of changes matching the type\n */\n getChangesByType(type: \"add\" | \"update\" | \"delete\"): Change[] {\n return this.changes.filter((c) => c.type === type);\n }\n\n /**\n * Mark changeset as staged.\n * Enables the changeset to accept new changes via staging operations.\n * Updates status and modified timestamp.\n */\n markStaged(): void {\n this.status = \"staged\";\n this.updateModified();\n }\n\n /**\n * Mark changeset as committed.\n * Indicates changes have been committed to the base model.\n * Updates status and modified timestamp.\n */\n markCommitted(): void {\n this.status = \"committed\";\n this.updateModified();\n }\n\n /**\n * Mark changeset as discarded.\n * Indicates changes have been discarded without committing.\n * Updates status and modified timestamp.\n */\n markDiscarded(): void {\n this.status = \"discarded\";\n this.updateModified();\n }\n\n /**\n * Serialize changeset to JSON.\n * Always returns StagedChangesetData format with sequenceNumbers added.\n *\n * @returns Serialized changeset data suitable for storage\n */\n toJSON(): StagedChangesetData {\n return {\n id: this.id,\n name: this.name,\n description: this.description,\n created: this.created,\n modified: this.modified,\n baseSnapshot: this.baseSnapshot,\n status: this.status,\n changes: this.changes.map((change, idx) => ({\n ...change,\n sequenceNumber: idx,\n })),\n stats: this.stats,\n };\n }\n\n /**\n * Deserialize changeset from JSON data.\n * Accepts StagedChangesetData format.\n *\n * @param data - Serialized changeset data\n * @returns New Changeset instance\n */\n static fromJSON(data: StagedChangesetData): Changeset {\n return new Changeset(data);\n }\n}\n"],
5
- "mappings": "AAmEM,MAAO,UAAS;EACpB;EACA;EACA;EACA;EACA,UAAoB,CAAA;EACpB,SAA0B;;EAG1B;EACA;;;;;;;EAQA,IAAI,QAAK;AAMP,UAAM,YAAY,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,SAAS,kBAAkB,EAAE,QAC1F,gBAAgB,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,QAChE,YAAY,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,SAAS,qBAAqB,EAAE;AAEtG,WAAO,EAAE,WAAW,eAAe,UAAS;EAC9C;;;;;;;EAQA,YAAY,MAAyB;AACnC,SAAK,OAAO,KAAK,MACjB,KAAK,cAAc,KAAK,aACxB,KAAK,UAAU,KAAK,SACpB,KAAK,WAAW,KAAK,UAErB,KAAK,WAAW,KAAK,WAAW,CAAA,GAAI,IAAI,CAAC,WAAU;AACjD,YAAM,EAAE,MAAM,WAAW,WAAW,QAAQ,OAAO,WAAW,eAAc,IAAK,QAC3E,SAAc,EAAE,MAAM,WAAW,WAAW,QAAQ,OAAO,UAAS;AAE1E,aAAI,OAAO,kBAAmB,aAC5B,OAAO,iBAAiB,iBAEnB;IACT,CAAC,GACD,KAAK,SAAS,KAAK,UAAU,UAC7B,KAAK,KAAK,KAAK,IACf,KAAK,eAAe,KAAK;EAE3B;;;;;;;;;;EAWA,OAAO,OACL,MACA,aACA,IACA,cAAoB;AAEpB,UAAM,OAAM,oBAAI,KAAI,GAAG,YAAW;AAClC,WAAO,IAAI,UAAU;MACnB;MACA;MACA,SAAS;MACT,UAAU;MACV,SAAS,CAAA;MACT,QAAQ;MACR;MACA;KACD;EACH;;;;;;;;;;;;EAaA,UACE,MACA,WACA,WACA,QACA,OAA+B;AAE/B,SAAK,QAAQ,KAAK;MAChB;MACA;MACA;MACA;MACA;MACA,YAAW,oBAAI,KAAI,GAAG,YAAW;KAClC,GACD,KAAK,eAAc;EACrB;;;;EAKA,iBAAc;AACZ,SAAK,YAAW,oBAAI,KAAI,GAAG,YAAW;EACxC;;;;;;EAOA,iBAAc;AACZ,WAAO,KAAK,QAAQ;EACtB;;;;;;;EAQA,iBAAiB,MAAiC;AAChD,WAAO,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI;EACnD;;;;;;EAOA,aAAU;AACR,SAAK,SAAS,UACd,KAAK,eAAc;EACrB;;;;;;EAOA,gBAAa;AACX,SAAK,SAAS,aACd,KAAK,eAAc;EACrB;;;;;;EAOA,gBAAa;AACX,SAAK,SAAS,aACd,KAAK,eAAc;EACrB;;;;;;;EAQA,SAAM;AACJ,WAAO;MACL,IAAI,KAAK;MACT,MAAM,KAAK;MACX,aAAa,KAAK;MAClB,SAAS,KAAK;MACd,UAAU,KAAK;MACf,cAAc,KAAK;MACnB,QAAQ,KAAK;MACb,SAAS,KAAK,QAAQ,IAAI,CAAC,QAAQ,SAAS;QAC1C,GAAG;QACH,gBAAgB;QAChB;MACF,OAAO,KAAK;;EAEhB;;;;;;;;EASA,OAAO,SAAS,MAAyB;AACvC,WAAO,IAAI,UAAU,IAAI;EAC3B;;",
4
+ "sourcesContent": ["/**\n * Changeset - Represents a collection of model changes.\n *\n * Changesets group related modifications to the architecture model for:\n * - Staging changes before committing to the base model\n * - Audit trail of model changes with before/after snapshots\n * - Collaboration and change tracking across team members\n *\n * Uses the staging workflow (staged/committed/discarded).\n */\n\n/**\n * Represents a single change in a changeset.\n * Records element mutations with before/after snapshots for audit purposes.\n */\nexport interface Change {\n type: \"add\" | \"update\" | \"delete\" | \"relationship-add\" | \"relationship-delete\";\n elementId: string; // For relationships: composite key \"source::predicate::target\"\n layerName: string; // For relationships: source element's layer\n before?: Record<string, unknown>; // Element/relationship state before change\n after?: Record<string, unknown>; // Element/relationship state after change\n timestamp?: string; // ISO timestamp; optional in interface but always set before storage\n}\n\n/**\n * Represents a staged change with sequence number for ordered replay.\n * Used in the staging workflow to ensure changes are applied in order during commit.\n */\nexport interface StagedChange extends Change {\n /**\n * Orders the change for commit replay. Assigned once when the change is staged, as one past the\n * highest number the changeset has used (`nextSequenceNumber`), and never renumbered: unstaging a\n * change leaves a gap, so a revision always names the same prefix of the plan. Changesets staged\n * before this numbered their changes 0..n-1, which reads the same way.\n */\n readonly sequenceNumber: number;\n}\n\n/**\n * A change that was unstaged, kept so the plan as it stood before the unstage can be rebuilt (see\n * `changesAtRevision` in changeset-graph.ts). Stored in the changeset's metadata, never among its\n * changes: commit, apply, preview and the projection do not see it.\n */\nexport interface UnstagedChange {\n /** The sequence number the change had. */\n sequence: number;\n /** The revision the unstage made: the sequence number it took (no change carries it). */\n removedAt: number;\n /** A compact copy of the change (see `compactChange`). */\n change: Change;\n /**\n * Set when the change was rewritten in place (an update folded into the add it updates), not\n * unstaged: the tombstone holds the version the change had before `removedAt`, and the change\n * itself is still staged under the same sequence number.\n */\n rewritten?: true;\n}\n\n/**\n * The change without what the plan's state does not depend on: an add's `before` (nothing was\n * there). Tombstones store this, and revision tokens hash it, so a change hashes the same staged\n * and unstaged.\n */\nexport function compactChange(change: Change): Change {\n const { type, elementId, layerName, before, after, timestamp } = change;\n const keepBefore = before !== undefined && before !== null && type !== \"add\" && type !== \"relationship-add\";\n return {\n type,\n elementId,\n layerName,\n ...(keepBefore ? { before } : {}),\n ...(after !== undefined && after !== null ? { after } : {}),\n ...(timestamp !== undefined ? { timestamp } : {}),\n };\n}\n\n/** A change's sequence number, or its position when it carries none (older in-memory changes). */\nexport function sequenceOf(change: Change, index: number): number {\n const sequence = (change as Partial<StagedChange>).sequenceNumber;\n return typeof sequence === \"number\" ? sequence : index;\n}\n\n/**\n * The sequence number the changeset's next change or unstage takes: one past every number it has\n * used, staged changes, unstaged ones and their unstage events included.\n */\nexport function nextSequenceNumber(changeset: {\n changes: Change[];\n lastSequence?: number;\n unstaged?: UnstagedChange[];\n unstagedPrunedThrough?: number;\n}): number {\n let max = Math.max(changeset.lastSequence ?? -1, changeset.unstagedPrunedThrough ?? -1);\n changeset.changes.forEach((change, index) => {\n max = Math.max(max, sequenceOf(change, index));\n });\n for (const tombstone of changeset.unstaged ?? []) {\n max = Math.max(max, tombstone.sequence, tombstone.removedAt);\n }\n return max + 1;\n}\n\n/**\n * Gives every change its sequence number, assigning numbers to any that carry none.\n *\n * With no numbers at all the position is the number (how older changesets were written). Otherwise\n * an unnumbered change takes the next unused number, never below `floor`: pass\n * `nextSequenceNumber(changeset)` so a number an unstaged change or unstage event used (recorded in\n * `lastSequence` and the tombstones) is not given out again.\n */\nexport function withSequenceNumbers(changes: Change[], floor = 0): StagedChange[] {\n const used = changes\n .map((change) => (change as Partial<StagedChange>).sequenceNumber)\n .filter((sequence): sequence is number => typeof sequence === \"number\");\n let next = Math.max(floor, used.length > 0 ? Math.max(...used) + 1 : 0);\n return changes.map((change, index) => {\n if (typeof (change as Partial<StagedChange>).sequenceNumber === \"number\") return change as StagedChange;\n const assigned = used.length === 0 ? index : next++;\n return { ...change, sequenceNumber: assigned } as StagedChange;\n });\n}\n\n/**\n * Status values for changesets in the staging workflow.\n *\n * - 'staged': Created and active for staging changes (can accept new changes)\n * - 'committed': Changes have been committed to base model\n * - 'discarded': Changes were discarded without committing\n */\nexport type ChangesetStatus = \"staged\" | \"committed\" | \"discarded\";\n\n/**\n * Changeset data with staging semantics and metadata.\n * Includes id and base snapshot for drift detection and auto-computed stats.\n */\nexport interface StagedChangesetData {\n id: string; // Unique changeset ID\n name: string;\n description?: string;\n created: string; // ISO timestamp of creation\n modified: string; // ISO timestamp of last modification\n status: ChangesetStatus;\n baseSnapshot: string; // SHA256 hash of base model at creation (for drift detection)\n changes: Change[] | StagedChange[]; // Changes (with optional sequence numbers)\n /** The highest sequence number used, unstage events included. Absent on older changesets. */\n lastSequence?: number;\n /** Recently unstaged changes, oldest first (bounded; see StagedChangesetStorage). */\n unstaged?: UnstagedChange[];\n /** Tombstones with `removedAt` at or below this were dropped to keep the list bounded. */\n unstagedPrunedThrough?: number;\n stats?: {\n additions: number;\n modifications: number;\n deletions: number;\n };\n}\n\n/**\n * Changeset class for managing model changes.\n * Represents a staging workflow changeset (staged/committed/discarded).\n *\n * Stats are computed on-demand from the changes array, ensuring consistency.\n */\nexport class Changeset {\n name: string;\n description?: string;\n created: string;\n modified: string;\n changes: Change[] = [];\n status: ChangesetStatus = \"staged\";\n\n // Staging workflow fields (required)\n id: string;\n baseSnapshot: string;\n\n /** The highest sequence number used, unstage events included (see `nextSequenceNumber`). */\n lastSequence?: number;\n /** Recently unstaged changes, kept to rebuild earlier revisions; never committed or applied. */\n unstaged: UnstagedChange[] = [];\n /** Tombstones with `removedAt` at or below this were dropped to keep the list bounded. */\n unstagedPrunedThrough?: number;\n\n /**\n * Get statistics derived from changes array.\n * Computed on-demand to ensure always accurate regardless of how changes were added.\n *\n * @returns Statistics object with count of additions, modifications, deletions\n */\n get stats(): {\n additions: number;\n modifications: number;\n deletions: number;\n } {\n // Compute stats from changes array on-demand (never stale)\n const additions = this.changes.filter((c) => c.type === \"add\" || c.type === \"relationship-add\").length;\n const modifications = this.changes.filter((c) => c.type === \"update\").length;\n const deletions = this.changes.filter((c) => c.type === \"delete\" || c.type === \"relationship-delete\").length;\n\n return { additions, modifications, deletions };\n }\n\n /**\n * Create changeset from data object.\n * Accepts only StagedChangesetData format with id and baseSnapshot.\n *\n * @param data - Changeset data from storage or API\n */\n constructor(data: StagedChangesetData) {\n this.name = data.name;\n this.description = data.description;\n this.created = data.created;\n this.modified = data.modified;\n // Store changes; preserve sequenceNumber if present from storage\n this.changes = (data.changes || []).map((change) => {\n const { type, elementId, layerName, before, after, timestamp, sequenceNumber } = change as any;\n const result: any = { type, elementId, layerName, before, after, timestamp };\n // Preserve sequenceNumber if it exists (from loaded StagedChange)\n if (typeof sequenceNumber === \"number\") {\n result.sequenceNumber = sequenceNumber;\n }\n return result as Change;\n });\n this.status = data.status || \"staged\";\n this.id = data.id;\n this.baseSnapshot = data.baseSnapshot;\n if (typeof data.lastSequence === \"number\") this.lastSequence = data.lastSequence;\n if (Array.isArray(data.unstaged)) this.unstaged = data.unstaged;\n if (typeof data.unstagedPrunedThrough === \"number\") this.unstagedPrunedThrough = data.unstagedPrunedThrough;\n // Note: Stats are computed from changes array via getter; ignore any stored stats\n }\n\n /**\n * Create a new changeset with staged status.\n *\n * @param name - Human-readable name for the changeset\n * @param description - Optional description of changes\n * @param id - Unique changeset ID (required)\n * @param baseSnapshot - SHA256 hash of base model at creation (required)\n * @returns New Changeset instance in staged status\n */\n static create(\n name: string,\n description: string | undefined,\n id: string,\n baseSnapshot: string\n ): Changeset {\n const now = new Date().toISOString();\n return new Changeset({\n name,\n description,\n created: now,\n modified: now,\n changes: [],\n status: \"staged\",\n id,\n baseSnapshot,\n });\n }\n\n /**\n * Add a change to the changeset.\n * Records element mutation with before/after snapshots for audit purposes.\n *\n * @param type - Type of change (add, update, delete)\n * @param elementId - ID of the element being changed\n * @param layerName - Layer containing the element\n * @param before - Element state before change (for update/delete operations)\n * @param after - Element state after change (for add/update operations)\n * @throws No exceptions; updates modified timestamp on success\n */\n addChange(\n type: \"add\" | \"update\" | \"delete\",\n elementId: string,\n layerName: string,\n before?: Record<string, unknown>,\n after?: Record<string, unknown>\n ): void {\n // One past every number used, unstaged changes and unstage events included, as\n // StagedChangesetStorage.addChange does\n const sequenceNumber = nextSequenceNumber(this);\n this.changes.push({\n type,\n elementId,\n layerName,\n before,\n after,\n timestamp: new Date().toISOString(),\n sequenceNumber,\n } as StagedChange);\n this.lastSequence = sequenceNumber;\n this.updateModified();\n }\n\n /**\n * Update the modified timestamp to current time.\n */\n updateModified(): void {\n this.modified = new Date().toISOString();\n }\n\n /**\n * Get the total number of changes in this changeset.\n *\n * @returns Number of changes (add + update + delete)\n */\n getChangeCount(): number {\n return this.changes.length;\n }\n\n /**\n * Get all changes of a specific type.\n *\n * @param type - Type of changes to filter for\n * @returns Array of changes matching the type\n */\n getChangesByType(type: \"add\" | \"update\" | \"delete\"): Change[] {\n return this.changes.filter((c) => c.type === type);\n }\n\n /**\n * Mark changeset as staged.\n * Enables the changeset to accept new changes via staging operations.\n * Updates status and modified timestamp.\n */\n markStaged(): void {\n this.status = \"staged\";\n this.updateModified();\n }\n\n /**\n * Mark changeset as committed.\n * Indicates changes have been committed to the base model.\n * Updates status and modified timestamp.\n */\n markCommitted(): void {\n this.status = \"committed\";\n this.updateModified();\n }\n\n /**\n * Mark changeset as discarded.\n * Indicates changes have been discarded without committing.\n * Updates status and modified timestamp.\n */\n markDiscarded(): void {\n this.status = \"discarded\";\n this.updateModified();\n }\n\n /**\n * Serialize changeset to JSON.\n * Always returns StagedChangesetData format with sequenceNumbers added.\n *\n * @returns Serialized changeset data suitable for storage\n */\n toJSON(): StagedChangesetData {\n return {\n id: this.id,\n name: this.name,\n description: this.description,\n created: this.created,\n modified: this.modified,\n baseSnapshot: this.baseSnapshot,\n status: this.status,\n // Sequence numbers are kept as staged: they may have gaps where changes were unstaged.\n changes: withSequenceNumbers(this.changes, nextSequenceNumber(this)),\n stats: this.stats,\n ...(this.lastSequence !== undefined ? { lastSequence: this.lastSequence } : {}),\n ...(this.unstaged.length > 0 ? { unstaged: this.unstaged } : {}),\n ...(this.unstagedPrunedThrough !== undefined ? { unstagedPrunedThrough: this.unstagedPrunedThrough } : {}),\n };\n }\n\n /**\n * Deserialize changeset from JSON data.\n * Accepts StagedChangesetData format.\n *\n * @param data - Serialized changeset data\n * @returns New Changeset instance\n */\n static fromJSON(data: StagedChangesetData): Changeset {\n return new Changeset(data);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,145 @@
1
+ import { readdir, readFile, writeFile } from "fs/promises";
2
+ import { Element } from "./element.js";
3
+ import { CANONICAL_LAYER_NAMES } from "./layers.js";
4
+ import { StagedChangesetStorage } from "./staged-changeset-storage.js";
5
+ import { FileLock } from "../utils/file-lock.js";
6
+ import { getErrorMessage } from "../utils/errors.js";
7
+ import { RELATIONSHIP_REWRITE_TABLE } from "./relationship-migration-table.js";
8
+ function splitType(specNodeId) {
9
+ const dot = specNodeId.indexOf(".");
10
+ return { layer: specNodeId.slice(0, dot), type: specNodeId.slice(dot + 1) };
11
+ }
12
+ function mapId(id, ids, types) {
13
+ const known = ids.get(id);
14
+ if (known)
15
+ return known;
16
+ for (const [from, to] of Object.entries(types)) {
17
+ const rest = id.startsWith(`${from}.`) ? id.slice(from.length + 1) : void 0;
18
+ if (rest && !rest.includes(".") && !rest.includes(":"))
19
+ return `${to}.${rest}`;
20
+ }
21
+ return id;
22
+ }
23
+ function rewriteValue(value, ids, types) {
24
+ if (typeof value == "string") {
25
+ if (value.includes("::")) {
26
+ const parts = value.split("::"), mapped = parts.map((p) => mapId(p, ids, types));
27
+ return mapped.some((p, i) => p !== parts[i]) ? mapped.join("::") : value;
28
+ }
29
+ return mapId(value, ids, types);
30
+ }
31
+ if (Array.isArray(value))
32
+ return value.map((v) => rewriteValue(v, ids, types));
33
+ if (value && typeof value == "object") {
34
+ const obj = value, out = {};
35
+ for (const [k, v] of Object.entries(obj))
36
+ out[k] = rewriteValue(v, ids, types);
37
+ const layerId = typeof obj.layer_id == "string" ? obj.layer_id : void 0, specNodeId = typeof obj.spec_node_id == "string" ? obj.spec_node_id : layerId && typeof obj.type == "string" ? `${layerId}.${obj.type}` : void 0, renamed = specNodeId ? types[specNodeId] : void 0;
38
+ return renamed && ("spec_node_id" in obj && (out.spec_node_id = renamed), "type" in obj && (out.type = splitType(renamed).type)), out;
39
+ }
40
+ return value;
41
+ }
42
+ function renamedPath(path, from, to) {
43
+ return path.startsWith(`${from}.`) ? `${to}.${path.slice(from.length + 1)}` : path;
44
+ }
45
+ async function migrateElementTypes(model, renames = RELATIONSHIP_REWRITE_TABLE.typeRenames ?? {}, options = {}) {
46
+ const result = { filesModified: 0, changes: [], changesetsModified: 0, errors: [] };
47
+ if (Object.keys(renames).length === 0)
48
+ return result;
49
+ for (const layerName of CANONICAL_LAYER_NAMES)
50
+ model.layers.has(layerName) || await model.loadLayer(layerName);
51
+ const ids = /* @__PURE__ */ new Map();
52
+ for (const layer of model.layers.values())
53
+ for (const element of layer.listElements()) {
54
+ const oldSpec = element.spec_node_id || `${layer.name}.${element.type}`, newSpec = renames[oldSpec];
55
+ if (!newSpec)
56
+ continue;
57
+ const target = splitType(newSpec);
58
+ if (target.layer !== layer.name) {
59
+ result.errors.push(`${element.path}: renaming ${oldSpec} to ${newSpec} would move it to another layer, which the upgrade cannot do`);
60
+ continue;
61
+ }
62
+ const json = element.toJSON(), newPath = renamedPath(element.path, oldSpec, newSpec);
63
+ if (options.dryRun) {
64
+ result.changes.push({ from: element.path, to: newPath, fromType: oldSpec, toType: newSpec });
65
+ continue;
66
+ }
67
+ layer.deleteElement(element.id), layer.addElement(new Element({
68
+ ...json,
69
+ // A legacy element whose id is its semantic path keeps the two equal
70
+ id: json.id === element.path ? newPath : json.id,
71
+ path: newPath,
72
+ spec_node_id: newSpec,
73
+ type: target.type,
74
+ layer_id: layer.name
75
+ })), newPath !== element.path && ids.set(element.path, newPath), result.changes.push({ from: element.path, to: newPath, fromType: oldSpec, toType: newSpec });
76
+ }
77
+ if (result.changes.length === 0 || options.dryRun)
78
+ return result;
79
+ if (ids.size > 0)
80
+ for (const layer of model.layers.values())
81
+ for (const element of layer.listElements())
82
+ element.references?.some((r) => ids.has(r.target) || ids.has(r.source)) && (element.references = element.references.map((r) => ({
83
+ ...r,
84
+ source: ids.get(r.source) ?? r.source,
85
+ target: ids.get(r.target) ?? r.target
86
+ })), layer.updateElement(element));
87
+ if (ids.size > 0) {
88
+ const YAML = await import("yaml"), relationshipsPath = `${model.rootPath}/documentation-robotics/model/relationships.yaml`;
89
+ let content;
90
+ try {
91
+ content = await readFile(relationshipsPath, "utf-8");
92
+ } catch (error) {
93
+ if (error.code !== "ENOENT")
94
+ throw error;
95
+ }
96
+ const data = content ? YAML.parse(content) ?? [] : [];
97
+ if (Array.isArray(data)) {
98
+ let changed = !1;
99
+ const rewritten = data.map((rel) => {
100
+ const map = (v) => typeof v == "string" && mapId(v, ids, renames) !== v ? mapId(v, ids, renames) : void 0, source = map(rel.source), target = map(rel.target);
101
+ return !source && !target ? rel : (changed = !0, { ...rel, ...source && { source }, ...target && { target } });
102
+ });
103
+ if (changed) {
104
+ const header = `# Intra-layer relationships
105
+ # Format: source_id -> predicate -> target_id
106
+ # Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}
107
+
108
+ `;
109
+ await writeFile(relationshipsPath, header + YAML.stringify(rewritten), "utf-8"), result.filesModified++;
110
+ }
111
+ }
112
+ }
113
+ const storage = new StagedChangesetStorage(model.rootPath);
114
+ let entries = [];
115
+ try {
116
+ entries = (await readdir(storage.getChangesetsDir(), { withFileTypes: !0 })).filter((entry) => entry.isDirectory() && !entry.name.endsWith(".lock")).map((entry) => entry.name).sort();
117
+ } catch (error) {
118
+ if (error.code !== "ENOENT")
119
+ throw error;
120
+ }
121
+ for (const id of entries)
122
+ try {
123
+ await new FileLock(storage.getChangesetPath(id)).withLock(async () => {
124
+ const changeset = await storage.load(id);
125
+ if (!changeset || changeset.status !== "staged")
126
+ return;
127
+ const before = JSON.stringify(changeset.changes);
128
+ changeset.changes = changeset.changes.map((change) => rewriteValue(change, ids, renames)), JSON.stringify(changeset.changes) !== before && (changeset.updateModified(), await storage.save(changeset), result.changesetsModified++);
129
+ });
130
+ } catch (error) {
131
+ result.errors.push(`Failed to rename element types in staged changeset '${id}': ${getErrorMessage(error)}; fix or delete the changeset and run the upgrade again`);
132
+ }
133
+ return result;
134
+ }
135
+ function summarizeElementTypeChanges(changes) {
136
+ const byType = /* @__PURE__ */ new Map();
137
+ for (const c of changes)
138
+ byType.set(`${c.fromType} -> ${c.toType}`, (byType.get(`${c.fromType} -> ${c.toType}`) ?? 0) + 1);
139
+ return [...byType].map(([k, n]) => `${k} (${n} element${n === 1 ? "" : "s"})`).join("; ");
140
+ }
141
+ export {
142
+ migrateElementTypes,
143
+ summarizeElementTypeChanges
144
+ };
145
+ //# sourceMappingURL=element-type-migration.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/element-type-migration.ts"],
4
+ "sourcesContent": ["/**\n * Element type renames for `dr upgrade`.\n *\n * When the spec renames an element type (\"product.capability\" -> \"product.softwarecapability\",\n * recorded as a rename-type action and generated into RELATIONSHIP_REWRITE_TABLE.typeRenames),\n * every model element of the old type takes the new type, and its semantic path follows\n * (\"product.capability.search\" -> \"product.softwarecapability.search\"). Anything that names the\n * old path is rewritten with it: other elements' references, relationships.yaml, and the\n * element and relationship changes of staged changesets.\n *\n * The rename runs before the relationship rewrite of the same migration, on the loaded model, so\n * the relationship rules (keyed by the new type names) resolve the renamed elements. Renamed\n * layers are left dirty for the upgrade's saveDirtyLayers(); relationships.yaml and changesets\n * are rewritten on disk.\n */\nimport { readdir, readFile, writeFile } from \"fs/promises\";\nimport type { Model } from \"./model.js\";\nimport { Element } from \"./element.js\";\nimport type { Change } from \"./changeset.js\";\nimport { CANONICAL_LAYER_NAMES } from \"./layers.js\";\nimport { StagedChangesetStorage } from \"./staged-changeset-storage.js\";\nimport { FileLock } from \"../utils/file-lock.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { RELATIONSHIP_REWRITE_TABLE } from \"./relationship-migration-table.js\";\n\nexport interface ElementTypeRenameChange {\n /** Element path before and after (the UUID does not change) */\n from: string;\n to: string;\n fromType: string;\n toType: string;\n}\n\nexport interface ElementTypeMigrationResult {\n filesModified: number;\n changes: ElementTypeRenameChange[];\n /** Changesets whose staged changes named a renamed element or type */\n changesetsModified: number;\n errors: string[];\n}\n\nfunction splitType(specNodeId: string): { layer: string; type: string } {\n const dot = specNodeId.indexOf(\".\");\n return { layer: specNodeId.slice(0, dot), type: specNodeId.slice(dot + 1) };\n}\n\n/**\n * The renamed form of an element id: a renamed element's path, or any \"{old type}.{name}\" path,\n * which covers elements that exist only in a staged changeset. Other ids are returned unchanged.\n */\nfunction mapId(id: string, ids: Map<string, string>, types: Record<string, string>): string {\n const known = ids.get(id);\n if (known) return known;\n for (const [from, to] of Object.entries(types)) {\n const rest = id.startsWith(`${from}.`) ? id.slice(from.length + 1) : undefined;\n if (rest && !rest.includes(\".\") && !rest.includes(\":\")) return `${to}.${rest}`;\n }\n return id;\n}\n\n/** Replace every string in a JSON-like value that is a renamed element id (or a composite of them) */\nfunction rewriteValue(value: unknown, ids: Map<string, string>, types: Record<string, string>): unknown {\n if (typeof value === \"string\") {\n // Relationship change ids are \"source::predicate::target\"\n if (value.includes(\"::\")) {\n const parts = value.split(\"::\");\n const mapped = parts.map((p) => mapId(p, ids, types));\n return mapped.some((p, i) => p !== parts[i]) ? mapped.join(\"::\") : value;\n }\n return mapId(value, ids, types);\n }\n if (Array.isArray(value)) return value.map((v) => rewriteValue(v, ids, types));\n if (value && typeof value === \"object\") {\n const obj = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) out[k] = rewriteValue(v, ids, types);\n // An element payload of a renamed type: spec_node_id and type follow the rename\n const layerId = typeof obj.layer_id === \"string\" ? obj.layer_id : undefined;\n const specNodeId =\n typeof obj.spec_node_id === \"string\"\n ? obj.spec_node_id\n : layerId && typeof obj.type === \"string\"\n ? `${layerId}.${obj.type}`\n : undefined;\n const renamed = specNodeId ? types[specNodeId] : undefined;\n if (renamed) {\n if (\"spec_node_id\" in obj) out.spec_node_id = renamed;\n if (\"type\" in obj) out.type = splitType(renamed).type;\n }\n return out;\n }\n return value;\n}\n\n/** The new semantic path for an element of a renamed type, or the path unchanged */\nfunction renamedPath(path: string, from: string, to: string): string {\n return path.startsWith(`${from}.`) ? `${to}.${path.slice(from.length + 1)}` : path;\n}\n\n/**\n * Rename the elements of every renamed type in the loaded model, and rewrite what names them.\n *\n * @param renames - old spec node id -> new spec node id; defaults to the generated table's\n * @param options.dryRun - report the elements that would be renamed, and change nothing\n */\nexport async function migrateElementTypes(\n model: Model,\n renames: Record<string, string> = RELATIONSHIP_REWRITE_TABLE.typeRenames ?? {},\n options: { dryRun?: boolean } = {}\n): Promise<ElementTypeMigrationResult> {\n const result: ElementTypeMigrationResult = { filesModified: 0, changes: [], changesetsModified: 0, errors: [] };\n if (Object.keys(renames).length === 0) return result;\n\n for (const layerName of CANONICAL_LAYER_NAMES) {\n if (!model.layers.has(layerName)) {\n await model.loadLayer(layerName);\n }\n }\n\n // 1. Elements of a renamed type: new type, new path. A move to another layer is not supported.\n const ids = new Map<string, string>();\n for (const layer of model.layers.values()) {\n for (const element of layer.listElements()) {\n const oldSpec = element.spec_node_id || `${layer.name}.${element.type}`;\n const newSpec = renames[oldSpec];\n if (!newSpec) continue;\n const target = splitType(newSpec);\n if (target.layer !== layer.name) {\n result.errors.push(`${element.path}: renaming ${oldSpec} to ${newSpec} would move it to another layer, which the upgrade cannot do`);\n continue;\n }\n const json = element.toJSON();\n const newPath = renamedPath(element.path, oldSpec, newSpec);\n if (options.dryRun) {\n result.changes.push({ from: element.path, to: newPath, fromType: oldSpec, toType: newSpec });\n continue;\n }\n layer.deleteElement(element.id);\n layer.addElement(\n new Element({\n ...json,\n // A legacy element whose id is its semantic path keeps the two equal\n id: json.id === element.path ? newPath : json.id,\n path: newPath,\n spec_node_id: newSpec,\n type: target.type,\n layer_id: layer.name,\n })\n );\n if (newPath !== element.path) ids.set(element.path, newPath);\n result.changes.push({ from: element.path, to: newPath, fromType: oldSpec, toType: newSpec });\n }\n }\n if (result.changes.length === 0 || options.dryRun) return result;\n\n // 2. References from any element to a renamed path\n if (ids.size > 0) {\n for (const layer of model.layers.values()) {\n for (const element of layer.listElements()) {\n if (!element.references?.some((r) => ids.has(r.target) || ids.has(r.source))) continue;\n element.references = element.references.map((r) => ({\n ...r,\n source: ids.get(r.source) ?? r.source,\n target: ids.get(r.target) ?? r.target,\n }));\n layer.updateElement(element);\n }\n }\n }\n\n // 3. relationships.yaml names elements by path (or a legacy id)\n if (ids.size > 0) {\n const YAML = await import(\"yaml\");\n const relationshipsPath = `${model.rootPath}/documentation-robotics/model/relationships.yaml`;\n let content: string | undefined;\n try {\n content = await readFile(relationshipsPath, \"utf-8\");\n } catch (error) {\n if ((error as { code?: string }).code !== \"ENOENT\") throw error;\n }\n const data = content ? (YAML.parse(content) ?? []) : [];\n if (Array.isArray(data)) {\n let changed = false;\n const rewritten = data.map((rel: Record<string, unknown>) => {\n const map = (v: unknown) => (typeof v === \"string\" && mapId(v, ids, renames) !== v ? mapId(v, ids, renames) : undefined);\n const source = map(rel.source);\n const target = map(rel.target);\n if (!source && !target) return rel;\n changed = true;\n return { ...rel, ...(source && { source }), ...(target && { target }) };\n });\n if (changed) {\n const header = `# Intra-layer relationships\n# Format: source_id -> predicate -> target_id\n# Last updated: ${new Date().toISOString()}\n\n`;\n await writeFile(relationshipsPath, header + YAML.stringify(rewritten), \"utf-8\");\n result.filesModified++;\n }\n }\n }\n\n // 4. Staged changesets: element and relationship changes naming a renamed element or type\n const storage = new StagedChangesetStorage(model.rootPath);\n let entries: string[] = [];\n try {\n entries = (await readdir(storage.getChangesetsDir(), { withFileTypes: true }))\n .filter((entry) => entry.isDirectory() && !entry.name.endsWith(\".lock\"))\n .map((entry) => entry.name)\n .sort();\n } catch (error) {\n if ((error as { code?: string }).code !== \"ENOENT\") throw error;\n }\n for (const id of entries) {\n try {\n await new FileLock(storage.getChangesetPath(id)).withLock(async () => {\n const changeset = await storage.load(id);\n if (!changeset || changeset.status !== \"staged\") return;\n const before = JSON.stringify(changeset.changes);\n changeset.changes = changeset.changes.map((change) => rewriteValue(change, ids, renames) as Change) as typeof changeset.changes;\n if (JSON.stringify(changeset.changes) === before) return;\n changeset.updateModified();\n await storage.save(changeset);\n result.changesetsModified++;\n });\n } catch (error) {\n result.errors.push(\n `Failed to rename element types in staged changeset '${id}': ${getErrorMessage(error)}; fix or delete the changeset and run the upgrade again`\n );\n }\n }\n\n // Renamed layers are dirty; the upgrade saves them\n return result;\n}\n\n/** One line per renamed type: \"product.capability -> product.softwarecapability (3 elements)\" */\nexport function summarizeElementTypeChanges(changes: ElementTypeRenameChange[]): string {\n const byType = new Map<string, number>();\n for (const c of changes) byType.set(`${c.fromType} -> ${c.toType}`, (byType.get(`${c.fromType} -> ${c.toType}`) ?? 0) + 1);\n return [...byType].map(([k, n]) => `${k} (${n} element${n === 1 ? \"\" : \"s\"})`).join(\"; \");\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }