@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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/reports/model-layer-report-generator.ts"],
4
- "sourcesContent": ["/**\n * Model Layer Report Generator\n *\n * Generates markdown documentation for a single architecture layer with:\n * - Mermaid intra-layer and inter-layer diagrams\n * - Relationship tables\n * - Element reference documentation\n * - Navigation and metadata\n *\n * Mirrors LayerReportGenerator from spec script but renders element instances\n * instead of type schemas.\n */\n\nimport type { ModelLayerReportData } from './model-report-data.js';\nimport type { Relationship } from '../core/relationships.js';\nimport { formatLayerName } from '../utils/layer-name-formatter.js';\nimport { formatMarkdownTable } from '../utils/markdown-table.js';\nimport { sanitizeMermaidId, escapeMermaidLabel } from '../utils/mermaid-utils.js';\nimport { createAnchor } from '../utils/markdown-anchor.js';\nimport { escapeMarkdown, getLayerDescription, valueToMarkdown } from '../export/markdown-utils.js';\nimport { getLayerOrder, CANONICAL_LAYER_NAMES } from '../core/layers.js';\nimport { getValidRelationships } from '../generated/relationship-index.js';\n\nexport class ModelLayerReportGenerator {\n constructor(private modelVersion: string, private generatedAt: string) {}\n\n /**\n * Generate complete layer report markdown\n */\n generate(data: ModelLayerReportData): string {\n const lines: string[] = [];\n\n // Document header\n lines.push(this.generateHeader(data));\n\n // Report Index (table of contents)\n lines.push(this.generateTableOfContents(data));\n\n // Layer Introduction + Statistics\n lines.push(this.generateLayerIntroduction(data));\n\n // Intra-Layer Relationships diagram\n lines.push(this.generateIntraLayerDiagram(data));\n\n // Inter-Layer Dependencies diagram\n lines.push(this.generateInterLayerDiagram(data));\n\n // Inter-Layer Relationships Table\n lines.push(this.generateInterLayerTable(data));\n\n // Element Reference section\n lines.push(this.generateElementReference(data));\n\n // Footer\n lines.push(this.generateFooter());\n\n return lines.join('');\n }\n\n private generateHeader(data: ModelLayerReportData): string {\n const lines: string[] = [];\n lines.push(`# ${formatLayerName(data.layerName)}\\n`);\n lines.push('\\n');\n lines.push(`${getLayerDescription(data.layerName)}\\n`);\n lines.push('\\n');\n return lines.join('');\n }\n\n private generateTableOfContents(data: ModelLayerReportData): string {\n const lines: string[] = [];\n lines.push('## Report Index\\n');\n lines.push('\\n');\n\n lines.push('- [Layer Introduction](#layer-introduction)\\n');\n lines.push('- [Intra-Layer Relationships](#intra-layer-relationships)\\n');\n lines.push('- [Inter-Layer Dependencies](#inter-layer-dependencies)\\n');\n\n if (data.interRelationships.length > 0) {\n lines.push('- [Inter-Layer Relationships Table](#inter-layer-relationships-table)\\n');\n }\n\n if (data.elements.length > 0) {\n lines.push('- [Element Reference](#element-reference)\\n');\n }\n\n lines.push('\\n');\n return lines.join('');\n }\n\n private generateLayerIntroduction(data: ModelLayerReportData): string {\n const lines: string[] = [];\n lines.push('## Layer Introduction\\n');\n lines.push('\\n');\n\n // Statistics table\n const headers = ['Metric', 'Count'];\n const rows = [\n ['Elements', String(data.statistics.elementCount)],\n ['Intra-Layer Relationships', String(data.statistics.intraRelationshipCount)],\n ['Inter-Layer Relationships', String(data.statistics.interRelationshipCount)],\n ['Inbound Relationships', String(data.statistics.inboundRelationshipCount)],\n ['Outbound Relationships', String(data.statistics.outboundRelationshipCount)],\n ];\n\n const table = formatMarkdownTable(headers, rows);\n lines.push(table);\n lines.push('\\n');\n\n // Upstream and downstream layers\n if (data.upstreamLayers.length > 0 || data.downstreamLayers.length > 0) {\n lines.push('**Cross-Layer References**:\\n');\n lines.push('\\n');\n\n if (data.upstreamLayers.length > 0) {\n const upstreamLinks = data.upstreamLayers\n .map((layerName) => this.createLayerLink(layerName))\n .join(', ');\n lines.push(`- **Upstream layers**: ${upstreamLinks}\\n`);\n }\n\n if (data.downstreamLayers.length > 0) {\n const downstreamLinks = data.downstreamLayers\n .map((layerName) => this.createLayerLink(layerName))\n .join(', ');\n lines.push(`- **Downstream layers**: ${downstreamLinks}\\n`);\n }\n\n lines.push('\\n');\n }\n\n return lines.join('');\n }\n\n private generateIntraLayerDiagram(data: ModelLayerReportData): string {\n const lines: string[] = [];\n lines.push('## Intra-Layer Relationships\\n');\n lines.push('\\n');\n\n // Empty layer handling\n if (data.elements.length === 0) {\n lines.push('No elements in this layer.\\n');\n lines.push('\\n');\n return lines.join('');\n }\n\n // Large layer fallback: For >30 elements, use summary table instead of Mermaid\n // to prevent rendering failures\n if (data.elements.length > 30) {\n lines.push('*This layer has >30 elements. Summary table shown instead of diagram.*\\n');\n lines.push('\\n');\n\n const headers = ['Element', 'Type', 'Relationships'];\n const rows: string[][] = [];\n\n for (const element of data.elements) {\n const relCount = data.intraRelationships.filter(\n (rel) => rel.source === element.path || rel.target === element.path\n ).length;\n\n rows.push([\n `\\`${escapeMarkdown(element.path)}\\``,\n `\\`${escapeMarkdown(element.type)}\\``,\n String(relCount),\n ]);\n }\n\n const table = formatMarkdownTable(headers, rows);\n lines.push(table);\n lines.push('\\n');\n\n return lines.join('');\n }\n\n // Generate mermaid diagram for layers with \u226430 elements\n lines.push('```mermaid\\n');\n lines.push('flowchart LR\\n');\n lines.push(` subgraph ${sanitizeMermaidId(data.layerName)}\\n`);\n\n // Add all elements as nodes (use escapeMermaidLabel for correct Mermaid escaping)\n for (const element of data.elements) {\n const nodeId = sanitizeMermaidId(element.path);\n const label = escapeMermaidLabel(element.name);\n lines.push(` ${nodeId}[\"${label}\"]\\n`);\n }\n\n // Add intra-layer relationships (sorted for deterministic output)\n const sortedRelationships = [...data.intraRelationships].sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n for (const rel of sortedRelationships) {\n const sourceId = sanitizeMermaidId(rel.source);\n const targetId = sanitizeMermaidId(rel.target);\n const predicate = escapeMermaidLabel(rel.predicate);\n lines.push(` ${sourceId} -->|${predicate}| ${targetId}\\n`);\n }\n\n lines.push(' end\\n');\n lines.push('```\\n');\n lines.push('\\n');\n\n return lines.join('');\n }\n\n private generateInterLayerDiagram(data: ModelLayerReportData): string {\n const lines: string[] = [];\n lines.push('## Inter-Layer Dependencies\\n');\n lines.push('\\n');\n\n // Empty layer handling\n if (data.elements.length === 0) {\n lines.push('No elements in this layer.\\n');\n lines.push('\\n');\n return lines.join('');\n }\n\n lines.push('```mermaid\\n');\n lines.push('flowchart TB\\n');\n lines.push(' classDef current fill:#f9f,stroke:#333,stroke-width:2px\\n');\n\n // Add all layers as nodes\n for (const layerName of CANONICAL_LAYER_NAMES) {\n const layerId = sanitizeMermaidId(layerName);\n const label = formatLayerName(layerName);\n lines.push(` ${layerId}[\"${label}\"]\\n`);\n }\n\n // Add layer-to-layer relationships for inter-layer dependencies\n const addedRelationships = new Set<string>();\n const layerRelationships: Array<{ sourceId: string; destId: string }> = [];\n\n for (const rel of data.interRelationships) {\n // Only include inter-layer relationships\n if (rel.layer !== rel.targetLayer && rel.targetLayer) {\n const key = `${rel.layer}-${rel.targetLayer}`;\n if (!addedRelationships.has(key)) {\n const sourceId = sanitizeMermaidId(rel.layer);\n const destId = sanitizeMermaidId(rel.targetLayer);\n layerRelationships.push({ sourceId, destId });\n addedRelationships.add(key);\n }\n }\n }\n\n // Sort and output relationships for deterministic ordering\n const sortedLayerRels = layerRelationships.sort((a, b) => {\n const aKey = `${a.sourceId}-${a.destId}`;\n const bKey = `${b.sourceId}-${b.destId}`;\n return aKey.localeCompare(bKey);\n });\n\n for (const { sourceId, destId } of sortedLayerRels) {\n lines.push(` ${sourceId} --> ${destId}\\n`);\n }\n\n // Highlight current layer\n const currentLayerId = sanitizeMermaidId(data.layerName);\n lines.push(` class ${currentLayerId} current\\n`);\n\n lines.push('```\\n');\n lines.push('\\n');\n\n return lines.join('');\n }\n\n private generateInterLayerTable(data: ModelLayerReportData): string {\n const lines: string[] = [];\n\n if (data.interRelationships.length === 0) {\n return '';\n }\n\n lines.push('## Inter-Layer Relationships Table\\n');\n lines.push('\\n');\n\n // Sort relationships for deterministic output\n const sortedRels = [...data.interRelationships].sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n const headers = ['Relationship ID', 'Source Node', 'Dest Node', 'Dest Layer', 'Predicate', 'Cardinality', 'Strength'];\n const rows: string[][] = [];\n\n for (const rel of sortedRels) {\n // Extract element types from paths (e.g., \"motivation.goal.customer-satisfaction\" -> \"motivation.goal\")\n const sourceType = this.extractElementType(rel.source);\n const targetType = this.extractElementType(rel.target);\n\n // Look up spec-level relationship properties\n const specs = getValidRelationships(sourceType, rel.predicate, targetType);\n const cardinality = specs.length > 0 ? specs[0].cardinality : 'unknown';\n const strength = specs.length > 0 ? specs[0].strength : 'unknown';\n\n // Use spec relationship ID when available, fall back to concatenated form\n const relationshipId = specs.length > 0 ? specs[0].id : `${rel.source}-${rel.predicate}-${rel.target}`;\n\n rows.push([\n `\\`${escapeMarkdown(relationshipId)}\\``,\n `\\`${escapeMarkdown(rel.source)}\\``,\n `\\`${escapeMarkdown(rel.target)}\\``,\n rel.targetLayer ? `\\`${escapeMarkdown(rel.targetLayer)}\\`` : '\u2014',\n `\\`${escapeMarkdown(rel.predicate)}\\``,\n cardinality,\n strength,\n ]);\n }\n\n const table = formatMarkdownTable(headers, rows);\n lines.push(table);\n lines.push('\\n');\n\n return lines.join('');\n }\n\n private generateElementReference(data: ModelLayerReportData): string {\n const lines: string[] = [];\n\n if (data.elements.length === 0) {\n return '';\n }\n\n lines.push('## Element Reference\\n');\n lines.push('\\n');\n\n for (const element of data.elements) {\n const anchor = createAnchor(element.name);\n lines.push(`### ${escapeMarkdown(element.name)} {#${anchor}}\\n`);\n lines.push('\\n');\n\n // ID and type\n lines.push(`**ID**: \\`${escapeMarkdown(element.path)}\\`\\n\\n`);\n lines.push(`**Type**: \\`${escapeMarkdown(element.type)}\\`\\n\\n`);\n\n // Description if available\n if (element.description) {\n lines.push(`${escapeMarkdown(element.description)}\\n\\n`);\n }\n\n // Attributes if available\n if (Object.keys(element.attributes).length > 0) {\n lines.push('#### Attributes\\n\\n');\n const attrHeaders = ['Name', 'Value'];\n const attrRows: string[][] = [];\n\n // Sort attributes for deterministic output\n const sortedAttrs = Object.entries(element.attributes).sort((a, b) =>\n a[0].localeCompare(b[0])\n );\n\n for (const [name, value] of sortedAttrs) {\n const valueStr = valueToMarkdown(value);\n attrRows.push([name, valueStr]);\n }\n\n const attrTable = formatMarkdownTable(attrHeaders, attrRows);\n lines.push(attrTable);\n lines.push('\\n');\n }\n\n // Relationships involving this element\n const elementRels = this.getElementRelationships(data, element.path);\n if (elementRels.length > 0) {\n lines.push('#### Relationships\\n\\n');\n const relHeaders = ['Type', 'Related Element', 'Predicate', 'Direction'];\n const relRows: string[][] = [];\n\n // Sort for deterministic output\n const sortedElementRels = [...elementRels].sort((a, b) => {\n const aKey = `${a.relType}-${a.rel.source}-${a.rel.predicate}-${a.rel.target}`;\n const bKey = `${b.relType}-${b.rel.source}-${b.rel.predicate}-${b.rel.target}`;\n return aKey.localeCompare(bKey);\n });\n\n for (const { rel, relType } of sortedElementRels) {\n const relatedElement = rel.source === element.path ? rel.target : rel.source;\n const direction = rel.source === element.path ? 'outbound' : 'inbound';\n\n relRows.push([\n relType,\n `\\`${escapeMarkdown(relatedElement)}\\``,\n `\\`${escapeMarkdown(rel.predicate)}\\``,\n direction,\n ]);\n }\n\n const relTable = formatMarkdownTable(relHeaders, relRows);\n lines.push(relTable);\n lines.push('\\n');\n }\n }\n\n return lines.join('');\n }\n\n private generateFooter(): string {\n const lines: string[] = [];\n lines.push('---\\n');\n lines.push('\\n');\n lines.push(`Generated: ${this.generatedAt} | Model Version: ${this.modelVersion}\\n`);\n return lines.join('');\n }\n\n /**\n * Create a markdown link to a layer report\n */\n private createLayerLink(layerName: string): string {\n const layerNumber = getLayerOrder(layerName);\n if (layerNumber === -1) {\n return layerName;\n }\n const filename = `./${String(layerNumber).padStart(2, '0')}-${layerName}-layer-report.md`;\n return `[${formatLayerName(layerName)}](${filename})`;\n }\n\n /**\n * Get relationships involving a specific element\n */\n private getElementRelationships(\n data: ModelLayerReportData,\n elementPath: string\n ): Array<{ rel: Relationship; relType: 'intra-layer' | 'inter-layer' }> {\n const rels: Array<{ rel: Relationship; relType: 'intra-layer' | 'inter-layer' }> = [];\n\n // Intra-layer relationships\n for (const rel of data.intraRelationships) {\n if (rel.source === elementPath || rel.target === elementPath) {\n rels.push({ rel, relType: 'intra-layer' });\n }\n }\n\n // Inter-layer relationships\n for (const rel of data.interRelationships) {\n if (rel.source === elementPath || rel.target === elementPath) {\n rels.push({ rel, relType: 'inter-layer' });\n }\n }\n\n return rels;\n }\n\n /**\n * Extract element type from element path in layer.type format\n * Example: \"motivation.goal.customer-satisfaction\" -> \"motivation.goal\"\n * This format matches the keys in RELATIONSHIPS_BY_SOURCE map.\n */\n private extractElementType(elementPath: string): string {\n const parts = elementPath.split('.');\n if (parts.length >= 2) {\n return parts.slice(0, 2).join('.');\n }\n return elementPath;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Model Layer Report Generator\n *\n * Generates markdown documentation for a single architecture layer with:\n * - Mermaid intra-layer and inter-layer diagrams\n * - Relationship tables\n * - Element reference documentation\n * - Navigation and metadata\n *\n * Mirrors LayerReportGenerator from spec script but renders element instances\n * instead of type schemas.\n */\n\nimport type { ModelLayerReportData } from './model-report-data.js';\nimport type { Relationship } from '../core/relationships.js';\nimport { formatLayerName } from '../utils/layer-name-formatter.js';\nimport { formatMarkdownTable } from '../utils/markdown-table.js';\nimport { sanitizeMermaidId, escapeMermaidLabel } from '../utils/mermaid-utils.js';\nimport { createAnchor } from '../utils/markdown-anchor.js';\nimport { escapeMarkdown, getLayerDescription, valueToMarkdown } from '../export/markdown-utils.js';\nimport type { CanonicalLayerName } from '../core/layers.js';\nimport { getLayerOrder, CANONICAL_LAYER_NAMES } from '../core/layers.js';\nimport { getValidRelationships } from '../generated/relationship-index.js';\nimport { getLayerReportFileName } from './model-report-utils.js';\n\nexport class ModelLayerReportGenerator {\n constructor(private modelVersion: string, private generatedAt: string) {}\n\n /**\n * Generate complete layer report markdown\n */\n generate(data: ModelLayerReportData): string {\n const lines: string[] = [];\n\n // Document header\n lines.push(this.generateHeader(data));\n\n // Report Index (table of contents)\n lines.push(this.generateTableOfContents(data));\n\n // Layer Introduction + Statistics\n lines.push(this.generateLayerIntroduction(data));\n\n // Intra-Layer Relationships diagram\n lines.push(this.generateIntraLayerDiagram(data));\n\n // Inter-Layer Dependencies diagram\n lines.push(this.generateInterLayerDiagram(data));\n\n // Inter-Layer Relationships Table\n lines.push(this.generateInterLayerTable(data));\n\n // Element Reference section\n lines.push(this.generateElementReference(data));\n\n // Footer\n lines.push(this.generateFooter());\n\n return lines.join('');\n }\n\n private generateHeader(data: ModelLayerReportData): string {\n const lines: string[] = [];\n lines.push(`# ${formatLayerName(data.layerName)}\\n`);\n lines.push('\\n');\n lines.push('[\u2190 Back to README](../README.md)\\n');\n lines.push('\\n');\n lines.push(`${getLayerDescription(data.layerName)}\\n`);\n lines.push('\\n');\n return lines.join('');\n }\n\n private generateTableOfContents(data: ModelLayerReportData): string {\n const lines: string[] = [];\n lines.push('## Report Index\\n');\n lines.push('\\n');\n\n lines.push('- [Layer Introduction](#layer-introduction)\\n');\n lines.push('- [Intra-Layer Relationships](#intra-layer-relationships)\\n');\n lines.push('- [Inter-Layer Dependencies](#inter-layer-dependencies)\\n');\n\n if (data.interRelationships.length > 0) {\n lines.push('- [Inter-Layer Relationships Table](#inter-layer-relationships-table)\\n');\n }\n\n if (data.elements.length > 0) {\n lines.push('- [Element Reference](#element-reference)\\n');\n }\n\n lines.push('\\n');\n return lines.join('');\n }\n\n private generateLayerIntroduction(data: ModelLayerReportData): string {\n const lines: string[] = [];\n lines.push('## Layer Introduction\\n');\n lines.push('\\n');\n\n // Statistics table\n const headers = ['Metric', 'Count'];\n const rows = [\n ['Elements', String(data.statistics.elementCount)],\n ['Intra-Layer Relationships', String(data.statistics.intraRelationshipCount)],\n ['Inter-Layer Relationships', String(data.statistics.interRelationshipCount)],\n ['Inbound Relationships', String(data.statistics.inboundRelationshipCount)],\n ['Outbound Relationships', String(data.statistics.outboundRelationshipCount)],\n ];\n\n const table = formatMarkdownTable(headers, rows);\n lines.push(table);\n lines.push('\\n');\n\n // Upstream and downstream layers\n if (data.upstreamLayers.length > 0 || data.downstreamLayers.length > 0) {\n lines.push('**Cross-Layer References**:\\n');\n lines.push('\\n');\n\n if (data.upstreamLayers.length > 0) {\n const upstreamLinks = data.upstreamLayers\n .map((layerName) => this.createLayerLink(layerName))\n .join(', ');\n lines.push(`- **Upstream layers**: ${upstreamLinks}\\n`);\n }\n\n if (data.downstreamLayers.length > 0) {\n const downstreamLinks = data.downstreamLayers\n .map((layerName) => this.createLayerLink(layerName))\n .join(', ');\n lines.push(`- **Downstream layers**: ${downstreamLinks}\\n`);\n }\n\n lines.push('\\n');\n }\n\n return lines.join('');\n }\n\n private generateIntraLayerDiagram(data: ModelLayerReportData): string {\n const lines: string[] = [];\n lines.push('## Intra-Layer Relationships\\n');\n lines.push('\\n');\n\n // Empty layer handling\n if (data.elements.length === 0) {\n lines.push('No elements in this layer.\\n');\n lines.push('\\n');\n return lines.join('');\n }\n\n // Large layer fallback: For >30 elements, use summary table instead of Mermaid\n // to prevent rendering failures\n if (data.elements.length > 30) {\n lines.push('*This layer has >30 elements. Summary table shown instead of diagram.*\\n');\n lines.push('\\n');\n\n const headers = ['Element', 'Type', 'Relationships'];\n const rows: string[][] = [];\n\n for (const element of data.elements) {\n const relCount = data.intraRelationships.filter(\n (rel) => rel.source === element.path || rel.target === element.path\n ).length;\n\n rows.push([\n `\\`${escapeMarkdown(element.path)}\\``,\n `\\`${escapeMarkdown(element.type)}\\``,\n String(relCount),\n ]);\n }\n\n const table = formatMarkdownTable(headers, rows);\n lines.push(table);\n lines.push('\\n');\n\n return lines.join('');\n }\n\n // Generate mermaid diagram for layers with \u226430 elements\n lines.push('```mermaid\\n');\n lines.push('flowchart LR\\n');\n lines.push(` subgraph ${sanitizeMermaidId(data.layerName)}\\n`);\n\n // Add all elements as nodes (use escapeMermaidLabel for correct Mermaid escaping)\n for (const element of data.elements) {\n const nodeId = sanitizeMermaidId(element.path);\n const label = escapeMermaidLabel(element.name);\n lines.push(` ${nodeId}[\"${label}\"]\\n`);\n }\n\n // Add intra-layer relationships (sorted for deterministic output)\n const sortedRelationships = [...data.intraRelationships].sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n for (const rel of sortedRelationships) {\n const sourceId = sanitizeMermaidId(rel.source);\n const targetId = sanitizeMermaidId(rel.target);\n const predicate = escapeMermaidLabel(rel.predicate);\n lines.push(` ${sourceId} -->|${predicate}| ${targetId}\\n`);\n }\n\n lines.push(' end\\n');\n lines.push('```\\n');\n lines.push('\\n');\n\n return lines.join('');\n }\n\n private generateInterLayerDiagram(data: ModelLayerReportData): string {\n const lines: string[] = [];\n lines.push('## Inter-Layer Dependencies\\n');\n lines.push('\\n');\n\n // Empty layer handling\n if (data.elements.length === 0) {\n lines.push('No elements in this layer.\\n');\n lines.push('\\n');\n return lines.join('');\n }\n\n lines.push('```mermaid\\n');\n lines.push('flowchart TB\\n');\n lines.push(' classDef current fill:#f9f,stroke:#333,stroke-width:2px\\n');\n\n // Add all layers as nodes\n for (const layerName of CANONICAL_LAYER_NAMES) {\n const layerId = sanitizeMermaidId(layerName);\n const label = formatLayerName(layerName);\n lines.push(` ${layerId}[\"${label}\"]\\n`);\n }\n\n // Add layer-to-layer relationships for inter-layer dependencies\n const addedRelationships = new Set<string>();\n const layerRelationships: Array<{ sourceId: string; destId: string }> = [];\n\n for (const rel of data.interRelationships) {\n // Only include inter-layer relationships\n if (rel.layer !== rel.targetLayer && rel.targetLayer) {\n const key = `${rel.layer}-${rel.targetLayer}`;\n if (!addedRelationships.has(key)) {\n const sourceId = sanitizeMermaidId(rel.layer);\n const destId = sanitizeMermaidId(rel.targetLayer);\n layerRelationships.push({ sourceId, destId });\n addedRelationships.add(key);\n }\n }\n }\n\n // Sort and output relationships for deterministic ordering\n const sortedLayerRels = layerRelationships.sort((a, b) => {\n const aKey = `${a.sourceId}-${a.destId}`;\n const bKey = `${b.sourceId}-${b.destId}`;\n return aKey.localeCompare(bKey);\n });\n\n for (const { sourceId, destId } of sortedLayerRels) {\n lines.push(` ${sourceId} --> ${destId}\\n`);\n }\n\n // Highlight current layer\n const currentLayerId = sanitizeMermaidId(data.layerName);\n lines.push(` class ${currentLayerId} current\\n`);\n\n lines.push('```\\n');\n lines.push('\\n');\n\n return lines.join('');\n }\n\n private generateInterLayerTable(data: ModelLayerReportData): string {\n const lines: string[] = [];\n\n if (data.interRelationships.length === 0) {\n return '';\n }\n\n lines.push('## Inter-Layer Relationships Table\\n');\n lines.push('\\n');\n\n // Sort relationships for deterministic output\n const sortedRels = [...data.interRelationships].sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n const headers = ['Relationship ID', 'Source Node', 'Dest Node', 'Dest Layer', 'Predicate', 'Cardinality', 'Strength'];\n const rows: string[][] = [];\n\n for (const rel of sortedRels) {\n // Extract element types from paths (e.g., \"motivation.goal.customer-satisfaction\" -> \"motivation.goal\")\n const sourceType = this.extractElementType(rel.source);\n const targetType = this.extractElementType(rel.target);\n\n // Look up spec-level relationship properties\n const specs = getValidRelationships(sourceType, rel.predicate, targetType);\n const cardinality = specs.length > 0 ? specs[0].cardinality : 'unknown';\n const strength = specs.length > 0 ? specs[0].strength : 'unknown';\n\n // Use spec relationship ID when available, fall back to concatenated form\n const relationshipId = specs.length > 0 ? specs[0].id : `${rel.source}-${rel.predicate}-${rel.target}`;\n\n rows.push([\n `\\`${escapeMarkdown(relationshipId)}\\``,\n `\\`${escapeMarkdown(rel.source)}\\``,\n `\\`${escapeMarkdown(rel.target)}\\``,\n rel.targetLayer ? `\\`${escapeMarkdown(rel.targetLayer)}\\`` : '\u2014',\n `\\`${escapeMarkdown(rel.predicate)}\\``,\n cardinality,\n strength,\n ]);\n }\n\n const table = formatMarkdownTable(headers, rows);\n lines.push(table);\n lines.push('\\n');\n\n return lines.join('');\n }\n\n private generateElementReference(data: ModelLayerReportData): string {\n const lines: string[] = [];\n\n if (data.elements.length === 0) {\n return '';\n }\n\n lines.push('## Element Reference\\n');\n lines.push('\\n');\n\n for (const element of data.elements) {\n const anchor = createAnchor(element.name);\n lines.push(`### ${escapeMarkdown(element.name)} {#${anchor}}\\n`);\n lines.push('\\n');\n\n // ID and type\n lines.push(`**ID**: \\`${escapeMarkdown(element.path)}\\`\\n\\n`);\n lines.push(`**Type**: \\`${escapeMarkdown(element.type)}\\`\\n\\n`);\n\n // Description if available\n if (element.description) {\n lines.push(`${escapeMarkdown(element.description)}\\n\\n`);\n }\n\n // Attributes if available\n if (Object.keys(element.attributes).length > 0) {\n lines.push('#### Attributes\\n\\n');\n const attrHeaders = ['Name', 'Value'];\n const attrRows: string[][] = [];\n\n // Sort attributes for deterministic output\n const sortedAttrs = Object.entries(element.attributes).sort((a, b) =>\n a[0].localeCompare(b[0])\n );\n\n for (const [name, value] of sortedAttrs) {\n const valueStr = valueToMarkdown(value);\n attrRows.push([name, valueStr]);\n }\n\n const attrTable = formatMarkdownTable(attrHeaders, attrRows);\n lines.push(attrTable);\n lines.push('\\n');\n }\n\n // Relationships involving this element\n const elementRels = this.getElementRelationships(data, element.path);\n if (elementRels.length > 0) {\n lines.push('#### Relationships\\n\\n');\n const relHeaders = ['Type', 'Related Element', 'Predicate', 'Direction'];\n const relRows: string[][] = [];\n\n // Sort for deterministic output\n const sortedElementRels = [...elementRels].sort((a, b) => {\n const aKey = `${a.relType}-${a.rel.source}-${a.rel.predicate}-${a.rel.target}`;\n const bKey = `${b.relType}-${b.rel.source}-${b.rel.predicate}-${b.rel.target}`;\n return aKey.localeCompare(bKey);\n });\n\n for (const { rel, relType } of sortedElementRels) {\n const relatedElement = rel.source === element.path ? rel.target : rel.source;\n const direction = rel.source === element.path ? 'outbound' : 'inbound';\n\n relRows.push([\n relType,\n `\\`${escapeMarkdown(relatedElement)}\\``,\n `\\`${escapeMarkdown(rel.predicate)}\\``,\n direction,\n ]);\n }\n\n const relTable = formatMarkdownTable(relHeaders, relRows);\n lines.push(relTable);\n lines.push('\\n');\n }\n }\n\n return lines.join('');\n }\n\n private generateFooter(): string {\n const lines: string[] = [];\n lines.push('---\\n');\n lines.push('\\n');\n lines.push(`Generated: ${this.generatedAt} | Model Version: ${this.modelVersion}\\n`);\n return lines.join('');\n }\n\n /**\n * Create a markdown link to a layer report\n */\n private createLayerLink(layerName: string): string {\n const layerNumber = getLayerOrder(layerName);\n if (layerNumber === -1) {\n return layerName;\n }\n const filename = `./${getLayerReportFileName(layerName as CanonicalLayerName)}`;\n return `[${formatLayerName(layerName)}](${filename})`;\n }\n\n /**\n * Get relationships involving a specific element\n */\n private getElementRelationships(\n data: ModelLayerReportData,\n elementPath: string\n ): Array<{ rel: Relationship; relType: 'intra-layer' | 'inter-layer' }> {\n const rels: Array<{ rel: Relationship; relType: 'intra-layer' | 'inter-layer' }> = [];\n\n // Intra-layer relationships\n for (const rel of data.intraRelationships) {\n if (rel.source === elementPath || rel.target === elementPath) {\n rels.push({ rel, relType: 'intra-layer' });\n }\n }\n\n // Inter-layer relationships\n for (const rel of data.interRelationships) {\n if (rel.source === elementPath || rel.target === elementPath) {\n rels.push({ rel, relType: 'inter-layer' });\n }\n }\n\n return rels;\n }\n\n /**\n * Extract element type from element path in layer.type format\n * Example: \"motivation.goal.customer-satisfaction\" -> \"motivation.goal\"\n * This format matches the keys in RELATIONSHIPS_BY_SOURCE map.\n */\n private extractElementType(elementPath: string): string {\n const parts = elementPath.split('.');\n if (parts.length >= 2) {\n return parts.slice(0, 2).join('.');\n }\n return elementPath;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,116 @@
1
+ import { formatLayerName } from "../utils/layer-name-formatter.js";
2
+ import { formatMarkdownTable } from "../utils/markdown-table.js";
3
+ import { escapeMarkdown } from "../export/markdown-utils.js";
4
+ class ModelReadmeGenerator {
5
+ generatedAt;
6
+ constructor(generatedAt) {
7
+ this.generatedAt = generatedAt;
8
+ }
9
+ /**
10
+ * Generate complete model README markdown
11
+ */
12
+ generate(data) {
13
+ const lines = [];
14
+ return lines.push(this.generateHeader(data)), lines.push(this.generateStatistics(data)), lines.push(this.generateProjectSummary(data)), lines.push(this.generateIntroduction()), lines.push(this.generateLayerIndex(data)), lines.push(this.generateFooter(data)), lines.join("");
15
+ }
16
+ generateHeader(data) {
17
+ const lines = [];
18
+ return lines.push(`# ${escapeMarkdown(data.projectName)}
19
+ `), lines.push(`
20
+ `), lines.join("");
21
+ }
22
+ generateStatistics(data) {
23
+ const lines = [];
24
+ lines.push(`## Model Statistics
25
+ `), lines.push(`
26
+ `);
27
+ const headers = ["Metric", "Value"], rows = [
28
+ ["Total Elements", String(data.totalElements)],
29
+ ["Total Relationships", String(data.totalRelationships)],
30
+ ["Populated Layers", String(data.populatedLayerCount)],
31
+ ["Last Updated", data.lastUpdated],
32
+ ["CLI Version", data.cliVersion]
33
+ ];
34
+ data.specVersion && rows.push(["Spec Version", data.specVersion]);
35
+ const table = formatMarkdownTable(headers, rows);
36
+ return lines.push(table), lines.push(`
37
+ `), lines.join("");
38
+ }
39
+ generateProjectSummary(data) {
40
+ const lines = [];
41
+ lines.push(`## Project Summary
42
+ `), lines.push(`
43
+ `), data.projectDescription && lines.push(`${escapeMarkdown(data.projectDescription)}
44
+
45
+ `);
46
+ const populatedLayers = data.layers.filter((l) => l.elementCount > 0);
47
+ if (populatedLayers.length > 0) {
48
+ lines.push(`**Populated Layers**:
49
+
50
+ `);
51
+ const layerNames = populatedLayers.map((l) => `- ${formatLayerName(l.layerName)}`).join(`
52
+ `);
53
+ lines.push(`${layerNames}
54
+
55
+ `);
56
+ }
57
+ return lines.join("");
58
+ }
59
+ generateIntroduction() {
60
+ const lines = [];
61
+ lines.push(`## About Documentation Robotics
62
+ `), lines.push(`
63
+ `), lines.push(`Documentation Robotics is a toolkit for managing federated architecture data models across 13 interconnected layers. It provides a structured approach to documenting systems architecture by organizing elements and relationships across distinct layers that represent different concerns: from business motivation and strategy through to technology infrastructure and testing.
64
+
65
+ `), lines.push(`### The 13-Layer Architecture Model
66
+
67
+ `);
68
+ const layerDescriptions = [
69
+ "1. **Motivation** - Goals, requirements, drivers, and strategic outcomes",
70
+ "2. **Business** - Business processes, functions, roles, and services",
71
+ "3. **Product** - Product features, capabilities, personas, and milestones",
72
+ "4. **Security** - Authentication, authorization, threats, and controls",
73
+ "5. **Application** - Application components, services, and interactions",
74
+ "6. **Technology** - Infrastructure, platforms, and technology components",
75
+ "7. **API** - REST APIs, operations, and endpoints",
76
+ "8. **Data Model** - Data entities, relationships, and structure definitions",
77
+ "9. **Data Store** - Databases and persistence mechanisms",
78
+ "10. **UX** - User interface components and user experience elements",
79
+ "11. **Navigation** - Application routing and navigation flows",
80
+ "12. **APM** - Observability, monitoring, metrics, and tracing",
81
+ "13. **Testing** - Test strategies, test cases, and test coverage"
82
+ ];
83
+ return lines.push(layerDescriptions.join(`
84
+ `)), lines.push(`
85
+
86
+ `), lines.push(`Each layer is independently managed but interconnected, allowing elements in higher layers to reference elements in lower layers, creating a comprehensive dependency graph that ensures architectural coherence and traceability across all concerns.
87
+
88
+ `), lines.join("");
89
+ }
90
+ generateLayerIndex(data) {
91
+ const lines = [];
92
+ lines.push(`## Layer Reports
93
+ `), lines.push(`
94
+ `);
95
+ const headers = ["Layer", "Elements", "Report"], rows = [], sortedLayers = [...data.layers].sort((a, b) => a.layerNumber - b.layerNumber);
96
+ for (const layer of sortedLayers) {
97
+ const layerName = formatLayerName(layer.layerName), elementCount = String(layer.elementCount);
98
+ let report;
99
+ layer.elementCount > 0 ? report = `[${layer.reportFileName}](./reports/${layer.reportFileName})` : report = "\u2014", rows.push([layerName, elementCount, report]);
100
+ }
101
+ const table = formatMarkdownTable(headers, rows);
102
+ return lines.push(table), lines.push(`
103
+ `), lines.join("");
104
+ }
105
+ generateFooter(data) {
106
+ const lines = [];
107
+ return lines.push(`---
108
+ `), lines.push(`
109
+ `), lines.push(`Generated: ${this.generatedAt} | Model Version: ${data.modelVersion}
110
+ `), lines.join("");
111
+ }
112
+ }
113
+ export {
114
+ ModelReadmeGenerator
115
+ };
116
+ //# sourceMappingURL=model-readme-generator.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/reports/model-readme-generator.ts"],
4
+ "sourcesContent": ["/**\n * Model README Generator\n *\n * Generates the main README.md for the model documentation with:\n * - Model statistics (elements, relationships, versions)\n * - Project summary\n * - Introduction to documentation robotics\n * - Per-layer report index table\n */\n\nimport type { ModelReadmeData } from './model-report-data.js';\nimport { formatLayerName } from '../utils/layer-name-formatter.js';\nimport { formatMarkdownTable } from '../utils/markdown-table.js';\nimport { escapeMarkdown } from '../export/markdown-utils.js';\n\nexport class ModelReadmeGenerator {\n constructor(private generatedAt: string) {}\n\n /**\n * Generate complete model README markdown\n */\n generate(data: ModelReadmeData): string {\n const lines: string[] = [];\n\n // Header with project name\n lines.push(this.generateHeader(data));\n\n // Statistics section\n lines.push(this.generateStatistics(data));\n\n // Project summary\n lines.push(this.generateProjectSummary(data));\n\n // Documentation robotics introduction\n lines.push(this.generateIntroduction());\n\n // Layer index table\n lines.push(this.generateLayerIndex(data));\n\n // Footer\n lines.push(this.generateFooter(data));\n\n return lines.join('');\n }\n\n private generateHeader(data: ModelReadmeData): string {\n const lines: string[] = [];\n lines.push(`# ${escapeMarkdown(data.projectName)}\\n`);\n lines.push('\\n');\n return lines.join('');\n }\n\n private generateStatistics(data: ModelReadmeData): string {\n const lines: string[] = [];\n lines.push('## Model Statistics\\n');\n lines.push('\\n');\n\n const headers = ['Metric', 'Value'];\n const rows = [\n ['Total Elements', String(data.totalElements)],\n ['Total Relationships', String(data.totalRelationships)],\n ['Populated Layers', String(data.populatedLayerCount)],\n ['Last Updated', data.lastUpdated],\n ['CLI Version', data.cliVersion],\n ];\n\n // Add spec version if available\n if (data.specVersion) {\n rows.push(['Spec Version', data.specVersion]);\n }\n\n const table = formatMarkdownTable(headers, rows);\n lines.push(table);\n lines.push('\\n');\n\n return lines.join('');\n }\n\n private generateProjectSummary(data: ModelReadmeData): string {\n const lines: string[] = [];\n lines.push('## Project Summary\\n');\n lines.push('\\n');\n\n // Project description if available\n if (data.projectDescription) {\n lines.push(`${escapeMarkdown(data.projectDescription)}\\n\\n`);\n }\n\n // Layer population summary\n const populatedLayers = data.layers.filter(l => l.elementCount > 0);\n if (populatedLayers.length > 0) {\n lines.push('**Populated Layers**:\\n\\n');\n const layerNames = populatedLayers\n .map(l => `- ${formatLayerName(l.layerName)}`)\n .join('\\n');\n lines.push(`${layerNames}\\n\\n`);\n }\n\n return lines.join('');\n }\n\n private generateIntroduction(): string {\n const lines: string[] = [];\n lines.push('## About Documentation Robotics\\n');\n lines.push('\\n');\n lines.push(\n 'Documentation Robotics is a toolkit for managing federated architecture data models across 13 interconnected layers. ' +\n 'It provides a structured approach to documenting systems architecture by organizing elements and relationships across ' +\n 'distinct layers that represent different concerns: from business motivation and strategy through to technology infrastructure and testing.\\n\\n'\n );\n\n lines.push('### The 13-Layer Architecture Model\\n\\n');\n\n const layerDescriptions = [\n '1. **Motivation** - Goals, requirements, drivers, and strategic outcomes',\n '2. **Business** - Business processes, functions, roles, and services',\n '3. **Product** - Product features, capabilities, personas, and milestones',\n '4. **Security** - Authentication, authorization, threats, and controls',\n '5. **Application** - Application components, services, and interactions',\n '6. **Technology** - Infrastructure, platforms, and technology components',\n '7. **API** - REST APIs, operations, and endpoints',\n '8. **Data Model** - Data entities, relationships, and structure definitions',\n '9. **Data Store** - Databases and persistence mechanisms',\n '10. **UX** - User interface components and user experience elements',\n '11. **Navigation** - Application routing and navigation flows',\n '12. **APM** - Observability, monitoring, metrics, and tracing',\n '13. **Testing** - Test strategies, test cases, and test coverage',\n ];\n\n lines.push(layerDescriptions.join('\\n'));\n lines.push('\\n\\n');\n\n lines.push(\n 'Each layer is independently managed but interconnected, allowing elements in higher layers to reference elements in lower layers, ' +\n 'creating a comprehensive dependency graph that ensures architectural coherence and traceability across all concerns.\\n\\n'\n );\n\n return lines.join('');\n }\n\n private generateLayerIndex(data: ModelReadmeData): string {\n const lines: string[] = [];\n lines.push('## Layer Reports\\n');\n lines.push('\\n');\n\n const headers = ['Layer', 'Elements', 'Report'];\n const rows: string[][] = [];\n\n // Sort layers by layer number for consistent ordering\n const sortedLayers = [...data.layers].sort((a, b) => a.layerNumber - b.layerNumber);\n\n for (const layer of sortedLayers) {\n const layerName = formatLayerName(layer.layerName);\n const elementCount = String(layer.elementCount);\n\n let report: string;\n if (layer.elementCount > 0) {\n // Create link to layer report (in reports/ subdirectory)\n report = `[${layer.reportFileName}](./reports/${layer.reportFileName})`;\n } else {\n // No link for empty layers\n report = '\u2014';\n }\n\n rows.push([layerName, elementCount, report]);\n }\n\n const table = formatMarkdownTable(headers, rows);\n lines.push(table);\n lines.push('\\n');\n\n return lines.join('');\n }\n\n private generateFooter(data: ModelReadmeData): string {\n const lines: string[] = [];\n lines.push('---\\n');\n lines.push('\\n');\n lines.push(`Generated: ${this.generatedAt} | Model Version: ${data.modelVersion}\\n`);\n return lines.join('');\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,5 +1,7 @@
1
1
  import { Element } from "../core/element.js";
2
- import { getLayerOrder, isValidLayerName } from "../core/layers.js";
2
+ import { getLayerOrder, CANONICAL_LAYER_NAMES, isValidLayerName } from "../core/layers.js";
3
+ import { getCliVersion } from "../utils/spec-version.js";
4
+ import { getLayerReportFileName } from "./model-report-utils.js";
3
5
  class ModelReportDataCollector {
4
6
  /**
5
7
  * Collect report data for a specific layer
@@ -50,6 +52,40 @@ class ModelReportDataCollector {
50
52
  statistics
51
53
  };
52
54
  }
55
+ /**
56
+ * Collect aggregated model-wide data for the model README
57
+ *
58
+ * @param model - The live Model instance
59
+ * @returns Aggregated model-wide data including project metadata, per-layer summaries, and statistics
60
+ */
61
+ collectModelData(model) {
62
+ const layers = [];
63
+ let totalElements = 0;
64
+ for (const layerName of CANONICAL_LAYER_NAMES) {
65
+ const elementCount = model.graph.getNodesByLayer(layerName).length;
66
+ totalElements += elementCount;
67
+ const layerNumber = getLayerOrder(layerName), reportFileName = getLayerReportFileName(layerName);
68
+ layers.push({
69
+ layerName,
70
+ layerNumber,
71
+ elementCount,
72
+ reportFileName
73
+ });
74
+ }
75
+ const totalRelationships = model.relationships.getAll().length, populatedLayerCount = layers.filter((l) => l.elementCount > 0).length;
76
+ return {
77
+ projectName: model.manifest.name,
78
+ projectDescription: model.manifest.description,
79
+ modelVersion: model.manifest.version,
80
+ cliVersion: model.manifest.cliVersion || getCliVersion(),
81
+ specVersion: model.manifest.specVersion,
82
+ lastUpdated: model.manifest.modified,
83
+ totalElements,
84
+ totalRelationships,
85
+ populatedLayerCount,
86
+ layers
87
+ };
88
+ }
53
89
  }
54
90
  export {
55
91
  ModelReportDataCollector
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/reports/model-report-data.ts"],
4
- "sourcesContent": ["import type { Model } from '../core/model.js';\nimport { Element } from '../core/element.js';\nimport type { Relationship } from '../core/relationships.js';\nimport { getLayerOrder, type CanonicalLayerName, isValidLayerName } from '../core/layers.js';\n\n/**\n * Statistics about relationships in a layer\n */\nexport interface ModelLayerStatistics {\n elementCount: number;\n intraRelationshipCount: number;\n interRelationshipCount: number;\n inboundRelationshipCount: number; // inter-layer rels targeting this layer\n outboundRelationshipCount: number; // inter-layer rels sourced from this layer\n}\n\n/**\n * Report-friendly data structure for a single layer\n * Analogous to ReportDataModel in scripts/generate-layer-reports.ts\n */\nexport interface ModelLayerReportData {\n layerName: CanonicalLayerName;\n layerNumber: number;\n elements: Element[]; // sorted by element.path\n intraRelationships: Relationship[]; // both source and target in same layer\n interRelationships: Relationship[]; // source or target in a different layer\n upstreamLayers: CanonicalLayerName[]; // canonical layer names referencing INTO this layer\n downstreamLayers: CanonicalLayerName[]; // canonical layer names this layer references OUT TO\n statistics: ModelLayerStatistics;\n}\n\n/**\n * Data collector for generating layer report data from a live Model instance\n * Reads from Model/Layer/Relationships and transforms into report-friendly shapes\n */\nexport class ModelReportDataCollector {\n /**\n * Collect report data for a specific layer\n *\n * @param model - The live Model instance\n * @param layerName - The canonical layer name (e.g., 'api', 'data-model')\n * @returns Report-friendly data structure for the layer\n */\n collectLayerData(model: Model, layerName: CanonicalLayerName): ModelLayerReportData {\n // Get the layer number for file naming\n const layerNumber = getLayerOrder(layerName);\n\n // Get all elements in this layer using GraphModel's indexed lookup for O(1) layer-scoped access\n // Convert GraphNodes to Elements for report-friendly shape\n const graphNodes = model.graph.getNodesByLayer(layerName);\n const elements: Element[] = graphNodes\n .map(\n (node) =>\n new Element({\n id: node.uuid || node.id,\n path: node.id,\n spec_node_id: node.spec_node_id || '',\n type: node.type,\n layer_id: node.layer,\n name: node.name,\n description: node.description,\n attributes: node.attributes,\n source_reference: node.source_reference,\n metadata: node.metadata,\n })\n )\n .sort((a, b) => (a.path || a.id).localeCompare(b.path || b.id));\n\n // Get all relationships and classify them\n const allRelationships = model.relationships.getAll();\n\n // Filter relationships involving this layer\n const allRelatingRelationships = allRelationships.filter(\n r => r.layer === layerName || r.targetLayer === layerName\n );\n\n // Intra-layer relationships: both source and target in same layer\n // Note: Relationships with missing targetLayer are treated as intra-layer because targetLayer is\n // typically only populated for inter-layer relationships during persistence and loading.\n const intraRelationships = allRelatingRelationships.filter(\n r => r.layer === layerName && (r.targetLayer === layerName || !r.targetLayer)\n );\n\n // Inter-layer relationships: source or target in different layer (or other endpoint exists)\n const interRelationships = allRelatingRelationships.filter(\n r => !(r.layer === layerName && (r.targetLayer === layerName || !r.targetLayer))\n );\n\n // Compute upstream and downstream layers from inter-layer relationships\n const upstreamLayerSet = new Set<CanonicalLayerName>();\n const downstreamLayerSet = new Set<CanonicalLayerName>();\n let inboundRelationshipCount = 0;\n let outboundRelationshipCount = 0;\n\n for (const rel of interRelationships) {\n // Upstream: layers that have relationships targeting THIS layer\n if (rel.targetLayer === layerName && rel.layer !== layerName) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.layer)) {\n upstreamLayerSet.add(rel.layer);\n inboundRelationshipCount++;\n } else {\n console.warn(`Skipping invalid layer name in relationship: ${rel.layer}`);\n }\n }\n // Downstream: layers that THIS layer references\n if (rel.layer === layerName && rel.targetLayer && rel.targetLayer !== layerName) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.targetLayer)) {\n downstreamLayerSet.add(rel.targetLayer);\n outboundRelationshipCount++;\n } else {\n console.warn(`Skipping invalid layer name in relationship: ${rel.targetLayer}`);\n }\n }\n }\n\n // Sort upstream and downstream layers\n const upstreamLayers = Array.from(upstreamLayerSet).sort();\n const downstreamLayers = Array.from(downstreamLayerSet).sort();\n\n // Sort relationships deterministically by \"${source}-${predicate}-${target}\"\n intraRelationships.sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n interRelationships.sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n // Build statistics\n const statistics: ModelLayerStatistics = {\n elementCount: elements.length,\n intraRelationshipCount: intraRelationships.length,\n interRelationshipCount: interRelationships.length,\n inboundRelationshipCount,\n outboundRelationshipCount,\n };\n\n return {\n layerName,\n layerNumber,\n elements,\n intraRelationships,\n interRelationships,\n upstreamLayers,\n downstreamLayers,\n statistics,\n };\n }\n}\n"],
5
- "mappings": "AACA,SAAS,eAAe;AAExB,SAAS,eAAwC,wBAAwB;AAgCnE,MAAO,yBAAwB;;;;;;;;EAQnC,iBAAiB,OAAc,WAA6B;AAE1D,UAAM,cAAc,cAAc,SAAS,GAKrC,WADa,MAAM,MAAM,gBAAgB,SAAS,EAErD,IACC,CAAC,SACC,IAAI,QAAQ;MACV,IAAI,KAAK,QAAQ,KAAK;MACtB,MAAM,KAAK;MACX,cAAc,KAAK,gBAAgB;MACnC,MAAM,KAAK;MACX,UAAU,KAAK;MACf,MAAM,KAAK;MACX,aAAa,KAAK;MAClB,YAAY,KAAK;MACjB,kBAAkB,KAAK;MACvB,UAAU,KAAK;KAChB,CAAC,EAEL,KAAK,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,IAAI,cAAc,EAAE,QAAQ,EAAE,EAAE,CAAC,GAM1D,2BAHmB,MAAM,cAAc,OAAM,EAGD,OAChD,OAAK,EAAE,UAAU,aAAa,EAAE,gBAAgB,SAAS,GAMrD,qBAAqB,yBAAyB,OAClD,OAAK,EAAE,UAAU,cAAc,EAAE,gBAAgB,aAAa,CAAC,EAAE,YAAY,GAIzE,qBAAqB,yBAAyB,OAClD,OAAK,EAAE,EAAE,UAAU,cAAc,EAAE,gBAAgB,aAAa,CAAC,EAAE,aAAa,GAI5E,mBAAmB,oBAAI,IAAG,GAC1B,qBAAqB,oBAAI,IAAG;AAClC,QAAI,2BAA2B,GAC3B,4BAA4B;AAEhC,eAAW,OAAO;AAEhB,MAAI,IAAI,gBAAgB,aAAa,IAAI,UAAU,cAE7C,iBAAiB,IAAI,KAAK,KAC5B,iBAAiB,IAAI,IAAI,KAAK,GAC9B,8BAEA,QAAQ,KAAK,gDAAgD,IAAI,KAAK,EAAE,IAIxE,IAAI,UAAU,aAAa,IAAI,eAAe,IAAI,gBAAgB,cAEhE,iBAAiB,IAAI,WAAW,KAClC,mBAAmB,IAAI,IAAI,WAAW,GACtC,+BAEA,QAAQ,KAAK,gDAAgD,IAAI,WAAW,EAAE;AAMpF,UAAM,iBAAiB,MAAM,KAAK,gBAAgB,EAAE,KAAI,GAClD,mBAAmB,MAAM,KAAK,kBAAkB,EAAE,KAAI;AAG5D,uBAAmB,KAAK,CAAC,GAAG,MAAK;AAC/B,YAAM,OAAO,GAAG,EAAE,MAAM,IAAI,EAAE,SAAS,IAAI,EAAE,MAAM,IAC7C,OAAO,GAAG,EAAE,MAAM,IAAI,EAAE,SAAS,IAAI,EAAE,MAAM;AACnD,aAAO,KAAK,cAAc,IAAI;IAChC,CAAC,GAED,mBAAmB,KAAK,CAAC,GAAG,MAAK;AAC/B,YAAM,OAAO,GAAG,EAAE,MAAM,IAAI,EAAE,SAAS,IAAI,EAAE,MAAM,IAC7C,OAAO,GAAG,EAAE,MAAM,IAAI,EAAE,SAAS,IAAI,EAAE,MAAM;AACnD,aAAO,KAAK,cAAc,IAAI;IAChC,CAAC;AAGD,UAAM,aAAmC;MACvC,cAAc,SAAS;MACvB,wBAAwB,mBAAmB;MAC3C,wBAAwB,mBAAmB;MAC3C;MACA;;AAGF,WAAO;MACL;MACA;MACA;MACA;MACA;MACA;MACA;MACA;;EAEJ;;",
4
+ "sourcesContent": ["import type { Model } from '../core/model.js';\nimport { Element } from '../core/element.js';\nimport type { Relationship } from '../core/relationships.js';\nimport { getLayerOrder, CANONICAL_LAYER_NAMES, type CanonicalLayerName, isValidLayerName } from '../core/layers.js';\nimport { getCliVersion } from '../utils/spec-version.js';\nimport { getLayerReportFileName } from './model-report-utils.js';\n\n/**\n * Statistics about relationships in a layer\n */\nexport interface ModelLayerStatistics {\n elementCount: number;\n intraRelationshipCount: number;\n interRelationshipCount: number;\n inboundRelationshipCount: number; // inter-layer rels targeting this layer\n outboundRelationshipCount: number; // inter-layer rels sourced from this layer\n}\n\n/**\n * Report-friendly data structure for a single layer\n * Analogous to ReportDataModel in scripts/generate-layer-reports.ts\n */\nexport interface ModelLayerReportData {\n layerName: CanonicalLayerName;\n layerNumber: number;\n elements: Element[]; // sorted by element.path\n intraRelationships: Relationship[]; // both source and target in same layer\n interRelationships: Relationship[]; // source or target in a different layer\n upstreamLayers: CanonicalLayerName[]; // canonical layer names referencing INTO this layer\n downstreamLayers: CanonicalLayerName[]; // canonical layer names this layer references OUT TO\n statistics: ModelLayerStatistics;\n}\n\n/**\n * Per-layer summary for the model README\n */\nexport interface ModelReadmeLayerSummary {\n layerName: CanonicalLayerName;\n layerNumber: number;\n elementCount: number;\n reportFileName: string;\n}\n\n/**\n * Aggregated model-wide data for the model README\n */\nexport interface ModelReadmeData {\n projectName: string;\n projectDescription?: string;\n modelVersion: string;\n cliVersion: string;\n specVersion?: string;\n lastUpdated: string;\n totalElements: number;\n totalRelationships: number;\n populatedLayerCount: number;\n layers: ModelReadmeLayerSummary[];\n}\n\n/**\n * Data collector for generating layer report data from a live Model instance\n * Reads from Model/Layer/Relationships and transforms into report-friendly shapes\n */\nexport class ModelReportDataCollector {\n /**\n * Collect report data for a specific layer\n *\n * @param model - The live Model instance\n * @param layerName - The canonical layer name (e.g., 'api', 'data-model')\n * @returns Report-friendly data structure for the layer\n */\n collectLayerData(model: Model, layerName: CanonicalLayerName): ModelLayerReportData {\n // Get the layer number for file naming\n const layerNumber = getLayerOrder(layerName);\n\n // Get all elements in this layer using GraphModel's indexed lookup for O(1) layer-scoped access\n // Convert GraphNodes to Elements for report-friendly shape\n const graphNodes = model.graph.getNodesByLayer(layerName);\n const elements: Element[] = graphNodes\n .map(\n (node) =>\n new Element({\n id: node.uuid || node.id,\n path: node.id,\n spec_node_id: node.spec_node_id || '',\n type: node.type,\n layer_id: node.layer,\n name: node.name,\n description: node.description,\n attributes: node.attributes,\n source_reference: node.source_reference,\n metadata: node.metadata,\n })\n )\n .sort((a, b) => (a.path || a.id).localeCompare(b.path || b.id));\n\n // Get all relationships and classify them\n const allRelationships = model.relationships.getAll();\n\n // Filter relationships involving this layer\n const allRelatingRelationships = allRelationships.filter(\n r => r.layer === layerName || r.targetLayer === layerName\n );\n\n // Intra-layer relationships: both source and target in same layer\n // Note: Relationships with missing targetLayer are treated as intra-layer because targetLayer is\n // typically only populated for inter-layer relationships during persistence and loading.\n const intraRelationships = allRelatingRelationships.filter(\n r => r.layer === layerName && (r.targetLayer === layerName || !r.targetLayer)\n );\n\n // Inter-layer relationships: source or target in different layer (or other endpoint exists)\n const interRelationships = allRelatingRelationships.filter(\n r => !(r.layer === layerName && (r.targetLayer === layerName || !r.targetLayer))\n );\n\n // Compute upstream and downstream layers from inter-layer relationships\n const upstreamLayerSet = new Set<CanonicalLayerName>();\n const downstreamLayerSet = new Set<CanonicalLayerName>();\n let inboundRelationshipCount = 0;\n let outboundRelationshipCount = 0;\n\n for (const rel of interRelationships) {\n // Upstream: layers that have relationships targeting THIS layer\n if (rel.targetLayer === layerName && rel.layer !== layerName) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.layer)) {\n upstreamLayerSet.add(rel.layer);\n inboundRelationshipCount++;\n } else {\n console.warn(`Skipping invalid layer name in relationship: ${rel.layer}`);\n }\n }\n // Downstream: layers that THIS layer references\n if (rel.layer === layerName && rel.targetLayer && rel.targetLayer !== layerName) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.targetLayer)) {\n downstreamLayerSet.add(rel.targetLayer);\n outboundRelationshipCount++;\n } else {\n console.warn(`Skipping invalid layer name in relationship: ${rel.targetLayer}`);\n }\n }\n }\n\n // Sort upstream and downstream layers\n const upstreamLayers = Array.from(upstreamLayerSet).sort();\n const downstreamLayers = Array.from(downstreamLayerSet).sort();\n\n // Sort relationships deterministically by \"${source}-${predicate}-${target}\"\n intraRelationships.sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n interRelationships.sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n // Build statistics\n const statistics: ModelLayerStatistics = {\n elementCount: elements.length,\n intraRelationshipCount: intraRelationships.length,\n interRelationshipCount: interRelationships.length,\n inboundRelationshipCount,\n outboundRelationshipCount,\n };\n\n return {\n layerName,\n layerNumber,\n elements,\n intraRelationships,\n interRelationships,\n upstreamLayers,\n downstreamLayers,\n statistics,\n };\n }\n\n /**\n * Collect aggregated model-wide data for the model README\n *\n * @param model - The live Model instance\n * @returns Aggregated model-wide data including project metadata, per-layer summaries, and statistics\n */\n collectModelData(model: Model): ModelReadmeData {\n // Collect per-layer element counts\n const layers: ModelReadmeLayerSummary[] = [];\n let totalElements = 0;\n\n for (const layerName of CANONICAL_LAYER_NAMES) {\n const graphNodes = model.graph.getNodesByLayer(layerName);\n const elementCount = graphNodes.length;\n totalElements += elementCount;\n\n const layerNumber = getLayerOrder(layerName);\n const reportFileName = getLayerReportFileName(layerName);\n\n layers.push({\n layerName,\n layerNumber,\n elementCount,\n reportFileName,\n });\n }\n\n // Count total relationships\n const totalRelationships = model.relationships.getAll().length;\n\n // Count populated layers (layers with at least one element)\n const populatedLayerCount = layers.filter(l => l.elementCount > 0).length;\n\n return {\n projectName: model.manifest.name,\n projectDescription: model.manifest.description,\n modelVersion: model.manifest.version,\n cliVersion: model.manifest.cliVersion || getCliVersion(),\n specVersion: model.manifest.specVersion,\n lastUpdated: model.manifest.modified,\n totalElements,\n totalRelationships,\n populatedLayerCount,\n layers,\n };\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,9 @@
1
1
  import { ModelReportDataCollector } from "./model-report-data.js";
2
2
  import { ModelLayerReportGenerator } from "./model-layer-report-generator.js";
3
- import { CANONICAL_LAYER_NAMES, getLayerOrder, isValidLayerName } from "../core/layers.js";
3
+ import { ModelReadmeGenerator } from "./model-readme-generator.js";
4
+ import { CANONICAL_LAYER_NAMES, isValidLayerName } from "../core/layers.js";
4
5
  import { getErrorMessage } from "../utils/errors.js";
6
+ import { getLayerReportFileName } from "./model-report-utils.js";
5
7
  import * as fs from "fs/promises";
6
8
  import * as path from "path";
7
9
  class ModelReportOrchestrator {
@@ -9,19 +11,23 @@ class ModelReportOrchestrator {
9
11
  rootPath;
10
12
  collector;
11
13
  generator;
14
+ readmeGenerator;
12
15
  constructor(model, rootPath) {
13
16
  this.model = model, this.rootPath = rootPath, this.collector = new ModelReportDataCollector();
14
17
  const modelVersion = model.manifest.version || "unknown", generatedAt = (/* @__PURE__ */ new Date()).toISOString();
15
- this.generator = new ModelLayerReportGenerator(modelVersion, generatedAt);
18
+ this.generator = new ModelLayerReportGenerator(modelVersion, generatedAt), this.readmeGenerator = new ModelReadmeGenerator(generatedAt);
16
19
  }
17
20
  /**
18
21
  * Regenerate reports for a specific set of affected layers.
19
22
  * On first invocation or when reports don't exist, regenerates all 13 reports.
20
23
  * Invalid layer names in the affected set are skipped with a warning.
24
+ * Also regenerates the README since it contains model-wide stats that may have changed.
25
+ * README is generated first so it exists even if a layer report throws.
21
26
  */
22
27
  async regenerate(affectedLayers) {
23
28
  if (!await this.isInitialized())
24
29
  return this.regenerateAll();
30
+ await this.generateReadme();
25
31
  for (const layerName of affectedLayers) {
26
32
  if (!isValidLayerName(layerName)) {
27
33
  console.warn(`Skipping invalid layer name: ${layerName}`);
@@ -31,12 +37,13 @@ class ModelReportOrchestrator {
31
37
  }
32
38
  }
33
39
  /**
34
- * Regenerate all 13 layer reports.
40
+ * Regenerate all 13 layer reports and the README.
35
41
  * Lets mkdir errors propagate so callers can handle them with proper telemetry.
42
+ * README is generated first so it exists even if a layer report throws.
36
43
  */
37
44
  async regenerateAll() {
38
45
  const reportDir = this.getReportDir();
39
- await fs.mkdir(reportDir, { recursive: !0 });
46
+ await fs.mkdir(reportDir, { recursive: !0 }), await this.generateReadme();
40
47
  for (const layerName of CANONICAL_LAYER_NAMES)
41
48
  await this.generateLayerReport(layerName);
42
49
  }
@@ -78,7 +85,7 @@ class ModelReportOrchestrator {
78
85
  * Get the full file path for a layer report.
79
86
  */
80
87
  getReportFilePath(layerName) {
81
- const layerNumber = getLayerOrder(layerName), filename = `${String(layerNumber).padStart(2, "0")}-${layerName}-layer-report.md`;
88
+ const filename = getLayerReportFileName(layerName);
82
89
  return path.join(this.getReportDir(), filename);
83
90
  }
84
91
  /**
@@ -91,11 +98,27 @@ class ModelReportOrchestrator {
91
98
  try {
92
99
  data = this.collector.collectLayerData(this.model, layerName), markdown = this.generator.generate(data);
93
100
  } catch (error) {
94
- throw new Error(`Failed to generate report content for layer: ${layerName} (programming error or corrupted model) - ${getErrorMessage(error)}`);
101
+ throw new Error(`Failed to generate report content for layer: ${layerName} (programming error or corrupted model) - ${getErrorMessage(error)}`, { cause: error });
95
102
  }
96
103
  const filePath = this.getReportFilePath(layerName);
97
104
  await fs.writeFile(filePath, markdown, "utf-8");
98
105
  }
106
+ /**
107
+ * Generate and write the model README.
108
+ * Separates concerns to distinguish programming bugs from I/O errors.
109
+ * Lets generation and write errors propagate so callers can handle them with proper telemetry.
110
+ * README is written to documentation-robotics/README.md (outside reports/ folder).
111
+ */
112
+ async generateReadme() {
113
+ let data, markdown;
114
+ try {
115
+ data = this.collector.collectModelData(this.model), markdown = this.readmeGenerator.generate(data);
116
+ } catch (error) {
117
+ throw new Error(`Failed to generate README content (programming error or corrupted model) - ${getErrorMessage(error)}`, { cause: error });
118
+ }
119
+ const filePath = path.join(this.rootPath, "documentation-robotics", "README.md");
120
+ await fs.writeFile(filePath, markdown, "utf-8");
121
+ }
99
122
  }
100
123
  export {
101
124
  ModelReportOrchestrator
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/reports/model-report-orchestrator.ts"],
4
- "sourcesContent": ["/**\n * Model Report Orchestrator\n *\n * Coordinates report generation across all 13 architecture layers.\n * Determines which reports need regeneration and manages the report directory.\n */\n\nimport type { Model } from '../core/model.js';\nimport { ModelReportDataCollector } from './model-report-data.js';\nimport { ModelLayerReportGenerator } from './model-layer-report-generator.js';\nimport { CANONICAL_LAYER_NAMES, type CanonicalLayerName, getLayerOrder, isValidLayerName } from '../core/layers.js';\nimport { getErrorMessage } from '../utils/errors.js';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\n\nexport class ModelReportOrchestrator {\n private collector: ModelReportDataCollector;\n private generator: ModelLayerReportGenerator;\n\n constructor(private model: Model, private rootPath: string) {\n this.collector = new ModelReportDataCollector();\n const modelVersion = model.manifest.version || 'unknown';\n const generatedAt = new Date().toISOString();\n this.generator = new ModelLayerReportGenerator(modelVersion, generatedAt);\n }\n\n /**\n * Regenerate reports for a specific set of affected layers.\n * On first invocation or when reports don't exist, regenerates all 13 reports.\n * Invalid layer names in the affected set are skipped with a warning.\n */\n async regenerate(affectedLayers: Set<string>): Promise<void> {\n if (!(await this.isInitialized())) {\n return this.regenerateAll();\n }\n\n for (const layerName of affectedLayers) {\n // Type predicate narrows layerName to CanonicalLayerName automatically\n if (!isValidLayerName(layerName)) {\n console.warn(`Skipping invalid layer name: ${layerName}`);\n continue;\n }\n await this.generateLayerReport(layerName);\n }\n }\n\n /**\n * Regenerate all 13 layer reports.\n * Lets mkdir errors propagate so callers can handle them with proper telemetry.\n */\n async regenerateAll(): Promise<void> {\n // Ensure report directory exists\n const reportDir = this.getReportDir();\n await fs.mkdir(reportDir, { recursive: true });\n\n // Generate all 13 layer reports\n for (const layerName of CANONICAL_LAYER_NAMES) {\n await this.generateLayerReport(layerName);\n }\n }\n\n /**\n * Compute the set of layers affected by a change to a primary layer.\n * Returns the primary layer plus any layers that have cross-layer relationships with it.\n * Throws if the primary layer is not a recognized canonical layer name.\n */\n computeAffectedLayers(primaryLayer: CanonicalLayerName): Set<CanonicalLayerName> {\n // Defense-in-depth: validate despite type signature (guards against as casts from callers)\n if (!isValidLayerName(primaryLayer)) {\n throw new Error(\n `Invalid primary layer name: '${primaryLayer}' is not a recognized canonical layer. ` +\n `Valid layers are: ${CANONICAL_LAYER_NAMES.join(', ')}`\n );\n }\n\n const affected = new Set<CanonicalLayerName>([primaryLayer]);\n\n for (const rel of this.model.relationships.getAll()) {\n // Outbound: this layer references another layer\n if (rel.layer === primaryLayer && rel.targetLayer) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.targetLayer)) {\n affected.add(rel.targetLayer);\n }\n }\n // Inbound: another layer references this layer\n if (rel.targetLayer === primaryLayer) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.layer)) {\n affected.add(rel.layer);\n }\n }\n }\n\n return affected;\n }\n\n /**\n * Check if all 13 report files exist and are initialized.\n * Returns false only if files don't exist (ENOENT); propagates other errors like EACCES, EMFILE, EIO.\n */\n private async isInitialized(): Promise<boolean> {\n for (const layerName of CANONICAL_LAYER_NAMES) {\n try {\n await fs.access(this.getReportFilePath(layerName));\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n // Only ENOENT (file not found) should return false; other errors propagate\n if (err.code === 'ENOENT') {\n return false;\n }\n // Re-throw permission errors, I/O errors, etc.\n throw error;\n }\n }\n return true;\n }\n\n /**\n * Get the reports directory path.\n */\n private getReportDir(): string {\n return path.join(this.rootPath, 'documentation-robotics', 'reports');\n }\n\n /**\n * Get the full file path for a layer report.\n */\n private getReportFilePath(layerName: CanonicalLayerName): string {\n const layerNumber = getLayerOrder(layerName);\n const paddedNumber = String(layerNumber).padStart(2, '0');\n const filename = `${paddedNumber}-${layerName}-layer-report.md`;\n return path.join(this.getReportDir(), filename);\n }\n\n /**\n * Generate and write a single layer report.\n * Separates concerns to distinguish programming bugs from I/O errors.\n * Lets generation and write errors propagate so callers can handle them with proper telemetry.\n */\n private async generateLayerReport(layerName: CanonicalLayerName): Promise<void> {\n let data;\n let markdown;\n\n try {\n // Collect layer data (may throw if programming bug or model is corrupted)\n data = this.collector.collectLayerData(this.model, layerName);\n\n // Generate markdown report (may throw if programming bug)\n markdown = this.generator.generate(data);\n } catch (error) {\n throw new Error(\n `Failed to generate report content for layer: ${layerName} (programming error or corrupted model) - ${getErrorMessage(error)}`\n );\n }\n\n // Write to file (may throw for I/O errors like ENOSPC, EACCES, EIO)\n // Let write errors propagate so callers can handle them with proper telemetry\n const filePath = this.getReportFilePath(layerName);\n await fs.writeFile(filePath, markdown, 'utf-8');\n }\n}\n"],
5
- "mappings": "AAQA,SAAS,gCAAgC;AACzC,SAAS,iCAAiC;AAC1C,SAAS,uBAAgD,eAAe,wBAAwB;AAChG,SAAS,uBAAuB;AAChC,YAAY,QAAQ;AACpB,YAAY,UAAU;AAEhB,MAAO,wBAAuB;EAId;EAAsB;EAHlC;EACA;EAER,YAAoB,OAAsB,UAAgB;AAAtC,SAAA,QAAA,OAAsB,KAAA,WAAA,UACxC,KAAK,YAAY,IAAI,yBAAwB;AAC7C,UAAM,eAAe,MAAM,SAAS,WAAW,WACzC,eAAc,oBAAI,KAAI,GAAG,YAAW;AAC1C,SAAK,YAAY,IAAI,0BAA0B,cAAc,WAAW;EAC1E;;;;;;EAOA,MAAM,WAAW,gBAA2B;AAC1C,QAAI,CAAE,MAAM,KAAK,cAAa;AAC5B,aAAO,KAAK,cAAa;AAG3B,eAAW,aAAa,gBAAgB;AAEtC,UAAI,CAAC,iBAAiB,SAAS,GAAG;AAChC,gBAAQ,KAAK,gCAAgC,SAAS,EAAE;AACxD;MACF;AACA,YAAM,KAAK,oBAAoB,SAAS;IAC1C;EACF;;;;;EAMA,MAAM,gBAAa;AAEjB,UAAM,YAAY,KAAK,aAAY;AACnC,UAAM,GAAG,MAAM,WAAW,EAAE,WAAW,GAAI,CAAE;AAG7C,eAAW,aAAa;AACtB,YAAM,KAAK,oBAAoB,SAAS;EAE5C;;;;;;EAOA,sBAAsB,cAAgC;AAEpD,QAAI,CAAC,iBAAiB,YAAY;AAChC,YAAM,IAAI,MACR,gCAAgC,YAAY,4DACvB,sBAAsB,KAAK,IAAI,CAAC,EAAE;AAI3D,UAAM,WAAW,oBAAI,IAAwB,CAAC,YAAY,CAAC;AAE3D,eAAW,OAAO,KAAK,MAAM,cAAc,OAAM;AAE/C,MAAI,IAAI,UAAU,gBAAgB,IAAI,eAEhC,iBAAiB,IAAI,WAAW,KAClC,SAAS,IAAI,IAAI,WAAW,GAI5B,IAAI,gBAAgB,gBAElB,iBAAiB,IAAI,KAAK,KAC5B,SAAS,IAAI,IAAI,KAAK;AAK5B,WAAO;EACT;;;;;EAMQ,MAAM,gBAAa;AACzB,eAAW,aAAa;AACtB,UAAI;AACF,cAAM,GAAG,OAAO,KAAK,kBAAkB,SAAS,CAAC;MACnD,SAAS,OAAgB;AAGvB,YAFY,MAEJ,SAAS;AACf,iBAAO;AAGT,cAAM;MACR;AAEF,WAAO;EACT;;;;EAKQ,eAAY;AAClB,WAAO,KAAK,KAAK,KAAK,UAAU,0BAA0B,SAAS;EACrE;;;;EAKQ,kBAAkB,WAA6B;AACrD,UAAM,cAAc,cAAc,SAAS,GAErC,WAAW,GADI,OAAO,WAAW,EAAE,SAAS,GAAG,GAAG,CACxB,IAAI,SAAS;AAC7C,WAAO,KAAK,KAAK,KAAK,aAAY,GAAI,QAAQ;EAChD;;;;;;EAOQ,MAAM,oBAAoB,WAA6B;AAC7D,QAAI,MACA;AAEJ,QAAI;AAEF,aAAO,KAAK,UAAU,iBAAiB,KAAK,OAAO,SAAS,GAG5D,WAAW,KAAK,UAAU,SAAS,IAAI;IACzC,SAAS,OAAO;AACd,YAAM,IAAI,MACR,gDAAgD,SAAS,6CAA6C,gBAAgB,KAAK,CAAC,EAAE;IAElI;AAIA,UAAM,WAAW,KAAK,kBAAkB,SAAS;AACjD,UAAM,GAAG,UAAU,UAAU,UAAU,OAAO;EAChD;;",
4
+ "sourcesContent": ["/**\n * Model Report Orchestrator\n *\n * Coordinates report generation across all 13 architecture layers.\n * Determines which reports need regeneration and manages the report directory.\n */\n\nimport type { Model } from '../core/model.js';\nimport { ModelReportDataCollector } from './model-report-data.js';\nimport { ModelLayerReportGenerator } from './model-layer-report-generator.js';\nimport { ModelReadmeGenerator } from './model-readme-generator.js';\nimport { CANONICAL_LAYER_NAMES, type CanonicalLayerName, isValidLayerName } from '../core/layers.js';\nimport { getErrorMessage } from '../utils/errors.js';\nimport { getLayerReportFileName } from './model-report-utils.js';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\n\nexport class ModelReportOrchestrator {\n private collector: ModelReportDataCollector;\n private generator: ModelLayerReportGenerator;\n private readmeGenerator: ModelReadmeGenerator;\n\n constructor(private model: Model, private rootPath: string) {\n this.collector = new ModelReportDataCollector();\n const modelVersion = model.manifest.version || 'unknown';\n const generatedAt = new Date().toISOString();\n this.generator = new ModelLayerReportGenerator(modelVersion, generatedAt);\n this.readmeGenerator = new ModelReadmeGenerator(generatedAt);\n }\n\n /**\n * Regenerate reports for a specific set of affected layers.\n * On first invocation or when reports don't exist, regenerates all 13 reports.\n * Invalid layer names in the affected set are skipped with a warning.\n * Also regenerates the README since it contains model-wide stats that may have changed.\n * README is generated first so it exists even if a layer report throws.\n */\n async regenerate(affectedLayers: Set<string>): Promise<void> {\n if (!(await this.isInitialized())) {\n return this.regenerateAll();\n }\n\n // Generate README first so it exists even if a layer report throws\n await this.generateReadme();\n\n for (const layerName of affectedLayers) {\n // Type predicate narrows layerName to CanonicalLayerName automatically\n if (!isValidLayerName(layerName)) {\n console.warn(`Skipping invalid layer name: ${layerName}`);\n continue;\n }\n await this.generateLayerReport(layerName);\n }\n }\n\n /**\n * Regenerate all 13 layer reports and the README.\n * Lets mkdir errors propagate so callers can handle them with proper telemetry.\n * README is generated first so it exists even if a layer report throws.\n */\n async regenerateAll(): Promise<void> {\n // Ensure report directory exists\n const reportDir = this.getReportDir();\n await fs.mkdir(reportDir, { recursive: true });\n\n // Generate README first so it exists even if a layer report throws\n await this.generateReadme();\n\n // Generate all 13 layer reports\n for (const layerName of CANONICAL_LAYER_NAMES) {\n await this.generateLayerReport(layerName);\n }\n }\n\n /**\n * Compute the set of layers affected by a change to a primary layer.\n * Returns the primary layer plus any layers that have cross-layer relationships with it.\n * Throws if the primary layer is not a recognized canonical layer name.\n */\n computeAffectedLayers(primaryLayer: CanonicalLayerName): Set<CanonicalLayerName> {\n // Defense-in-depth: validate despite type signature (guards against as casts from callers)\n if (!isValidLayerName(primaryLayer)) {\n throw new Error(\n `Invalid primary layer name: '${primaryLayer}' is not a recognized canonical layer. ` +\n `Valid layers are: ${CANONICAL_LAYER_NAMES.join(', ')}`\n );\n }\n\n const affected = new Set<CanonicalLayerName>([primaryLayer]);\n\n for (const rel of this.model.relationships.getAll()) {\n // Outbound: this layer references another layer\n if (rel.layer === primaryLayer && rel.targetLayer) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.targetLayer)) {\n affected.add(rel.targetLayer);\n }\n }\n // Inbound: another layer references this layer\n if (rel.targetLayer === primaryLayer) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.layer)) {\n affected.add(rel.layer);\n }\n }\n }\n\n return affected;\n }\n\n /**\n * Check if all 13 report files exist and are initialized.\n * Returns false only if files don't exist (ENOENT); propagates other errors like EACCES, EMFILE, EIO.\n */\n private async isInitialized(): Promise<boolean> {\n for (const layerName of CANONICAL_LAYER_NAMES) {\n try {\n await fs.access(this.getReportFilePath(layerName));\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n // Only ENOENT (file not found) should return false; other errors propagate\n if (err.code === 'ENOENT') {\n return false;\n }\n // Re-throw permission errors, I/O errors, etc.\n throw error;\n }\n }\n return true;\n }\n\n /**\n * Get the reports directory path.\n */\n private getReportDir(): string {\n return path.join(this.rootPath, 'documentation-robotics', 'reports');\n }\n\n /**\n * Get the full file path for a layer report.\n */\n private getReportFilePath(layerName: CanonicalLayerName): string {\n const filename = getLayerReportFileName(layerName);\n return path.join(this.getReportDir(), filename);\n }\n\n /**\n * Generate and write a single layer report.\n * Separates concerns to distinguish programming bugs from I/O errors.\n * Lets generation and write errors propagate so callers can handle them with proper telemetry.\n */\n private async generateLayerReport(layerName: CanonicalLayerName): Promise<void> {\n let data;\n let markdown;\n\n try {\n // Collect layer data (may throw if programming bug or model is corrupted)\n data = this.collector.collectLayerData(this.model, layerName);\n\n // Generate markdown report (may throw if programming bug)\n markdown = this.generator.generate(data);\n } catch (error) {\n throw new Error(\n `Failed to generate report content for layer: ${layerName} (programming error or corrupted model) - ${getErrorMessage(error)}`,\n { cause: error }\n );\n }\n\n // Write to file (may throw for I/O errors like ENOSPC, EACCES, EIO)\n // Let write errors propagate so callers can handle them with proper telemetry\n const filePath = this.getReportFilePath(layerName);\n await fs.writeFile(filePath, markdown, 'utf-8');\n }\n\n /**\n * Generate and write the model README.\n * Separates concerns to distinguish programming bugs from I/O errors.\n * Lets generation and write errors propagate so callers can handle them with proper telemetry.\n * README is written to documentation-robotics/README.md (outside reports/ folder).\n */\n private async generateReadme(): Promise<void> {\n let data;\n let markdown;\n\n try {\n // Collect model-wide data (may throw if programming bug or model is corrupted)\n data = this.collector.collectModelData(this.model);\n\n // Generate markdown README (may throw if programming bug)\n markdown = this.readmeGenerator.generate(data);\n } catch (error) {\n throw new Error(\n `Failed to generate README content (programming error or corrupted model) - ${getErrorMessage(error)}`,\n { cause: error }\n );\n }\n\n // Write to file (may throw for I/O errors like ENOSPC, EACCES, EIO)\n // Let write errors propagate so callers can handle them with proper telemetry\n const filePath = path.join(this.rootPath, 'documentation-robotics', 'README.md');\n await fs.writeFile(filePath, markdown, 'utf-8');\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,9 @@
1
+ import { getLayerOrder } from "../core/layers.js";
2
+ function getLayerReportFileName(layerName) {
3
+ const layerNumber = getLayerOrder(layerName);
4
+ return `${String(layerNumber).padStart(2, "0")}-${layerName}-layer-report.md`;
5
+ }
6
+ export {
7
+ getLayerReportFileName
8
+ };
9
+ //# sourceMappingURL=model-report-utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/reports/model-report-utils.ts"],
4
+ "sourcesContent": ["import type { CanonicalLayerName } from '../core/layers.js';\nimport { getLayerOrder } from '../core/layers.js';\n\n/**\n * Generate the report filename for a layer using the shared pattern: {NN}-{layerName}-layer-report.md\n *\n * @param layerName - The canonical layer name (e.g., 'api', 'data-model')\n * @returns The filename string with zero-padded layer number\n */\nexport function getLayerReportFileName(layerName: CanonicalLayerName): string {\n const layerNumber = getLayerOrder(layerName);\n const paddedNumber = String(layerNumber).padStart(2, '0');\n return `${paddedNumber}-${layerName}-layer-report.md`;\n}\n"],
5
+ "mappings": "AACA,SAAS,qBAAqB;AAQxB,SAAU,uBAAuB,WAA6B;AAClE,QAAM,cAAc,cAAc,SAAS;AAE3C,SAAO,GADc,OAAO,WAAW,EAAE,SAAS,GAAG,GAAG,CAClC,IAAI,SAAS;AACrC;",
6
+ "names": []
7
+ }