@devtrack-solution/codesdd 1.2.2 → 1.2.3

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 (345) hide show
  1. package/.sdd/skills/curated/api-clean-flask-langgraph/SKILL.md +17 -17
  2. package/.sdd/skills/curated/devtrack-api/SKILL.md +160 -28
  3. package/.sdd/skills/curated/devtrack-api/agents/openai.yaml +1 -1
  4. package/.sdd/skills/curated/devtrack-api/references/architecture-governance.md +8 -7
  5. package/.sdd/skills/curated/devtrack-api/references/consumer-sync-policy.md +93 -0
  6. package/.sdd/skills/curated/devtrack-api/references/contract-pack.yaml +317 -0
  7. package/.sdd/skills/curated/devtrack-api/references/field-validation-protocol.md +95 -0
  8. package/.sdd/skills/curated/devtrack-api/references/foundation-layout.md +295 -0
  9. package/.sdd/skills/curated/devtrack-api/references/implementation-checklist.md +4 -4
  10. package/.sdd/skills/curated/devtrack-api/references/imports-lint.md +4 -0
  11. package/.sdd/skills/curated/devtrack-api/references/testing-validation.md +2 -2
  12. package/LICENSE +1 -1
  13. package/README.md +243 -51
  14. package/bin/codesdd.js +3 -2
  15. package/dist/cli/index.d.ts +2 -2
  16. package/dist/cli/index.js +11 -558
  17. package/dist/cli/program.d.ts +14 -0
  18. package/dist/cli/program.js +645 -0
  19. package/dist/commands/change.js +5 -5
  20. package/dist/commands/completion.d.ts +1 -1
  21. package/dist/commands/completion.js +9 -2
  22. package/dist/commands/config.js +159 -20
  23. package/dist/commands/feedback.js +1 -1
  24. package/dist/commands/schema.d.ts +63 -0
  25. package/dist/commands/schema.js +12 -12
  26. package/dist/commands/sdd/backlog.d.ts +3 -0
  27. package/dist/commands/sdd/backlog.js +54 -0
  28. package/dist/commands/sdd/execution.js +147 -16
  29. package/dist/commands/sdd/plugin.d.ts +3 -0
  30. package/dist/commands/sdd/plugin.js +153 -0
  31. package/dist/commands/sdd/shared.js +2 -23
  32. package/dist/commands/sdd/skills.js +7 -0
  33. package/dist/commands/sdd.js +69 -12
  34. package/dist/commands/spec.js +9 -9
  35. package/dist/commands/validate.js +6 -6
  36. package/dist/commands/workflow/instructions.js +6 -6
  37. package/dist/commands/workflow/new-change.js +3 -3
  38. package/dist/commands/workflow/shared.d.ts +1 -1
  39. package/dist/commands/workflow/shared.js +4 -4
  40. package/dist/core/archive.js +15 -5
  41. package/dist/core/artifact-graph/instruction-loader.d.ts +1 -1
  42. package/dist/core/artifact-graph/instruction-loader.js +3 -3
  43. package/dist/core/artifact-graph/resolver.d.ts +4 -4
  44. package/dist/core/artifact-graph/resolver.js +6 -6
  45. package/dist/core/branding.js +3 -3
  46. package/dist/core/cli/command-matrix.js +10 -1
  47. package/dist/core/cli-command-quality.d.ts +27 -0
  48. package/dist/core/cli-command-quality.js +171 -0
  49. package/dist/core/command-generation/adapters/costrict.d.ts +1 -1
  50. package/dist/core/command-generation/adapters/costrict.js +2 -2
  51. package/dist/core/command-generation/types.d.ts +1 -1
  52. package/dist/core/completions/command-registry.d.ts +1 -1
  53. package/dist/core/completions/command-registry.js +155 -12
  54. package/dist/core/completions/completion-provider.d.ts +14 -1
  55. package/dist/core/completions/completion-provider.js +29 -1
  56. package/dist/core/completions/generators/bash-generator.d.ts +1 -1
  57. package/dist/core/completions/generators/bash-generator.js +20 -12
  58. package/dist/core/completions/generators/fish-generator.d.ts +9 -1
  59. package/dist/core/completions/generators/fish-generator.js +39 -25
  60. package/dist/core/completions/generators/powershell-generator.d.ts +1 -1
  61. package/dist/core/completions/generators/powershell-generator.js +21 -11
  62. package/dist/core/completions/generators/zsh-generator.d.ts +3 -6
  63. package/dist/core/completions/generators/zsh-generator.js +21 -42
  64. package/dist/core/completions/installers/bash-installer.js +6 -6
  65. package/dist/core/completions/installers/fish-installer.js +1 -1
  66. package/dist/core/completions/installers/powershell-installer.js +14 -14
  67. package/dist/core/completions/installers/zsh-installer.d.ts +7 -1
  68. package/dist/core/completions/installers/zsh-installer.js +36 -8
  69. package/dist/core/completions/templates/bash-templates.d.ts +1 -1
  70. package/dist/core/completions/templates/bash-templates.js +12 -6
  71. package/dist/core/completions/templates/fish-templates.d.ts +2 -2
  72. package/dist/core/completions/templates/fish-templates.js +20 -9
  73. package/dist/core/completions/templates/powershell-templates.d.ts +1 -1
  74. package/dist/core/completions/templates/powershell-templates.js +13 -4
  75. package/dist/core/completions/templates/zsh-templates.d.ts +1 -1
  76. package/dist/core/completions/templates/zsh-templates.js +18 -9
  77. package/dist/core/config-schema.d.ts +3 -1
  78. package/dist/core/config-schema.js +26 -1
  79. package/dist/core/config.d.ts +3 -3
  80. package/dist/core/config.js +4 -4
  81. package/dist/core/global-config.d.ts +41 -12
  82. package/dist/core/global-config.js +344 -27
  83. package/dist/core/index.d.ts +1 -1
  84. package/dist/core/index.js +2 -2
  85. package/dist/core/init.d.ts +6 -1
  86. package/dist/core/init.js +99 -77
  87. package/dist/core/legacy-cleanup.d.ts +17 -17
  88. package/dist/core/legacy-cleanup.js +96 -79
  89. package/dist/core/list.js +18 -4
  90. package/dist/core/migration.d.ts +3 -1
  91. package/dist/core/migration.js +7 -8
  92. package/dist/core/parsers/change-parser.js +1 -1
  93. package/dist/core/parsers/markdown-parser.js +2 -2
  94. package/dist/core/profile-sync-drift.d.ts +1 -1
  95. package/dist/core/profile-sync-drift.js +13 -13
  96. package/dist/core/project-config.d.ts +4 -4
  97. package/dist/core/project-config.js +11 -11
  98. package/dist/core/schemas/change.schema.d.ts +1 -1
  99. package/dist/core/schemas/change.schema.js +1 -1
  100. package/dist/core/schemas/spec.schema.d.ts +1 -1
  101. package/dist/core/schemas/spec.schema.js +1 -1
  102. package/dist/core/sdd/adr.js +23 -1
  103. package/dist/core/sdd/agent-binding.d.ts +346 -0
  104. package/dist/core/sdd/agent-binding.js +343 -0
  105. package/dist/core/sdd/backlog-cli.d.ts +16 -0
  106. package/dist/core/sdd/backlog-cli.js +146 -0
  107. package/dist/core/sdd/backlog-conflict-policy.d.ts +58 -0
  108. package/dist/core/sdd/backlog-conflict-policy.js +230 -0
  109. package/dist/core/sdd/backlog-projection.d.ts +8 -0
  110. package/dist/core/sdd/backlog-projection.js +89 -0
  111. package/dist/core/sdd/backlog-provider-contract.d.ts +252 -0
  112. package/dist/core/sdd/backlog-provider-contract.js +158 -0
  113. package/dist/core/sdd/bootstrap.js +2 -2
  114. package/dist/core/sdd/check.d.ts +42 -0
  115. package/dist/core/sdd/check.js +22 -22
  116. package/dist/core/sdd/contract.d.ts +13 -0
  117. package/dist/core/sdd/contract.js +36 -0
  118. package/dist/core/sdd/coordination/coordination-adapters.d.ts +38 -0
  119. package/dist/core/sdd/coordination/coordination-adapters.js +139 -1
  120. package/dist/core/sdd/deepagent-contracts.d.ts +276 -0
  121. package/dist/core/sdd/deepagent-contracts.js +173 -0
  122. package/dist/core/sdd/deepagents/adr-governor.d.ts +2 -0
  123. package/dist/core/sdd/deepagents/adr-governor.js +30 -0
  124. package/dist/core/sdd/deepagents/backend.d.ts +63 -0
  125. package/dist/core/sdd/deepagents/backend.js +174 -0
  126. package/dist/core/sdd/deepagents/codesdd-tools.d.ts +39 -0
  127. package/dist/core/sdd/deepagents/codesdd-tools.js +83 -0
  128. package/dist/core/sdd/deepagents/evidence-mapper.d.ts +86 -0
  129. package/dist/core/sdd/deepagents/evidence-mapper.js +178 -0
  130. package/dist/core/sdd/deepagents/model-provider.d.ts +53 -0
  131. package/dist/core/sdd/deepagents/model-provider.js +379 -0
  132. package/dist/core/sdd/deepagents/policy-enforcement.d.ts +30 -0
  133. package/dist/core/sdd/deepagents/policy-enforcement.js +90 -0
  134. package/dist/core/sdd/deepagents/policy.d.ts +75 -0
  135. package/dist/core/sdd/deepagents/policy.js +358 -0
  136. package/dist/core/sdd/deepagents/quality-witness.d.ts +3 -0
  137. package/dist/core/sdd/deepagents/quality-witness.js +77 -0
  138. package/dist/core/sdd/deepagents/reversa-subagents.d.ts +75 -0
  139. package/dist/core/sdd/deepagents/reversa-subagents.js +182 -0
  140. package/dist/core/sdd/deepagents/runtime-factory.d.ts +90 -0
  141. package/dist/core/sdd/deepagents/runtime-factory.js +231 -0
  142. package/dist/core/sdd/deepagents/runtime-loader.d.ts +16 -0
  143. package/dist/core/sdd/deepagents/runtime-loader.js +65 -0
  144. package/dist/core/sdd/default-bootstrap-files.d.ts +2 -2
  145. package/dist/core/sdd/default-bootstrap-files.js +36 -2
  146. package/dist/core/sdd/default-skills.d.ts +30 -0
  147. package/dist/core/sdd/default-skills.js +181 -5
  148. package/dist/core/sdd/devtrack-api-appliance.d.ts +84 -0
  149. package/dist/core/sdd/devtrack-api-appliance.js +257 -0
  150. package/dist/core/sdd/devtrack-api-architecture.d.ts +31 -0
  151. package/dist/core/sdd/devtrack-api-architecture.js +608 -0
  152. package/dist/core/sdd/devtrack-api-import-boundary.d.ts +19 -0
  153. package/dist/core/sdd/devtrack-api-import-boundary.js +32 -0
  154. package/dist/core/sdd/diagnose.d.ts +59 -0
  155. package/dist/core/sdd/diagnose.js +37 -37
  156. package/dist/core/sdd/docs-sync.js +33 -5
  157. package/dist/core/sdd/domain/post-active-validation.d.ts +7 -0
  158. package/dist/core/sdd/domain/post-active-validation.js +61 -0
  159. package/dist/core/sdd/domain/transition-engine.js +1 -0
  160. package/dist/core/sdd/entity-reference.d.ts +5 -0
  161. package/dist/core/sdd/entity-reference.js +22 -0
  162. package/dist/core/sdd/governance-backfill.d.ts +31 -0
  163. package/dist/core/sdd/governance-backfill.js +359 -0
  164. package/dist/core/sdd/governance-parser.d.ts +21 -0
  165. package/dist/core/sdd/governance-parser.js +91 -0
  166. package/dist/core/sdd/governance-schemas.d.ts +245 -0
  167. package/dist/core/sdd/governance-schemas.js +143 -0
  168. package/dist/core/sdd/{import-openspec.d.ts → import-legacy-spec.d.ts} +7 -7
  169. package/dist/core/sdd/{import-openspec.js → import-legacy-spec.js} +21 -29
  170. package/dist/core/sdd/init.d.ts +3 -0
  171. package/dist/core/sdd/init.js +6 -3
  172. package/dist/core/sdd/json-schema.js +100 -6
  173. package/dist/core/sdd/knowledge-graph.d.ts +45 -0
  174. package/dist/core/sdd/knowledge-graph.js +288 -0
  175. package/dist/core/sdd/legacy-operations.js +431 -43
  176. package/dist/core/sdd/lenses.d.ts +1 -0
  177. package/dist/core/sdd/lenses.js +29 -1
  178. package/dist/core/sdd/migrate-workspace.js +56 -2
  179. package/dist/core/sdd/migrate.d.ts +1 -1
  180. package/dist/core/sdd/migrate.js +36 -2
  181. package/dist/core/sdd/package-structure-gate.d.ts +83 -0
  182. package/dist/core/sdd/package-structure-gate.js +362 -0
  183. package/dist/core/sdd/parallel-feat-automation.d.ts +152 -0
  184. package/dist/core/sdd/parallel-feat-automation.js +212 -0
  185. package/dist/core/sdd/plugin-broker.d.ts +558 -0
  186. package/dist/core/sdd/plugin-broker.js +482 -0
  187. package/dist/core/sdd/plugin-certification.d.ts +79 -0
  188. package/dist/core/sdd/plugin-certification.js +453 -0
  189. package/dist/core/sdd/plugin-cli.d.ts +109 -0
  190. package/dist/core/sdd/plugin-cli.js +198 -0
  191. package/dist/core/sdd/plugin-evidence.d.ts +275 -0
  192. package/dist/core/sdd/plugin-evidence.js +307 -0
  193. package/dist/core/sdd/plugin-manifest.d.ts +164 -0
  194. package/dist/core/sdd/plugin-manifest.js +215 -0
  195. package/dist/core/sdd/plugin-policy-pack.d.ts +88 -0
  196. package/dist/core/sdd/plugin-policy-pack.js +236 -0
  197. package/dist/core/sdd/plugin-policy.d.ts +68 -0
  198. package/dist/core/sdd/plugin-policy.js +212 -0
  199. package/dist/core/sdd/plugin-registry.d.ts +311 -0
  200. package/dist/core/sdd/plugin-registry.js +138 -0
  201. package/dist/core/sdd/plugin-skill-binding.d.ts +151 -0
  202. package/dist/core/sdd/plugin-skill-binding.js +339 -0
  203. package/dist/core/sdd/quality-artifact-manifest-validator.d.ts +28 -0
  204. package/dist/core/sdd/quality-artifact-manifest-validator.js +167 -0
  205. package/dist/core/sdd/quality-evidence-renderer.d.ts +65 -0
  206. package/dist/core/sdd/quality-evidence-renderer.js +218 -0
  207. package/dist/core/sdd/quality-scenario-runner.d.ts +42 -0
  208. package/dist/core/sdd/quality-scenario-runner.js +613 -0
  209. package/dist/core/sdd/quality-validation.d.ts +547 -0
  210. package/dist/core/sdd/quality-validation.js +239 -0
  211. package/dist/core/sdd/resolve-project-root.d.ts +2 -2
  212. package/dist/core/sdd/resolve-project-root.js +11 -5
  213. package/dist/core/sdd/sanitize.d.ts +30 -1
  214. package/dist/core/sdd/sanitize.js +23 -23
  215. package/dist/core/sdd/services/agent-run.service.d.ts +65 -0
  216. package/dist/core/sdd/services/agent-run.service.js +189 -0
  217. package/dist/core/sdd/services/breakdown.service.js +2 -1
  218. package/dist/core/sdd/services/context.service.js +18 -16
  219. package/dist/core/sdd/services/debate.service.js +15 -2
  220. package/dist/core/sdd/services/feature-lint.service.d.ts +22 -0
  221. package/dist/core/sdd/services/feature-lint.service.js +105 -5
  222. package/dist/core/sdd/services/finalize.service.d.ts +80 -0
  223. package/dist/core/sdd/services/finalize.service.js +323 -24
  224. package/dist/core/sdd/services/frontend-gap.service.js +22 -7
  225. package/dist/core/sdd/services/governance-control-plane-runtime-adapters.d.ts +17 -0
  226. package/dist/core/sdd/services/governance-control-plane-runtime-adapters.js +38 -0
  227. package/dist/core/sdd/services/governance-control-plane.service.d.ts +66 -0
  228. package/dist/core/sdd/services/governance-control-plane.service.js +134 -0
  229. package/dist/core/sdd/services/ingest-deposito.service.js +1 -1
  230. package/dist/core/sdd/services/legacy-capability.service.d.ts +10 -7
  231. package/dist/core/sdd/services/legacy-capability.service.js +38 -21
  232. package/dist/core/sdd/services/mcp-runtime.service.d.ts +123 -8
  233. package/dist/core/sdd/services/mcp-runtime.service.js +1085 -33
  234. package/dist/core/sdd/services/onboard.service.js +2 -1
  235. package/dist/core/sdd/services/rebuild.service.js +6 -1
  236. package/dist/core/sdd/services/skills-sync.service.d.ts +17 -5
  237. package/dist/core/sdd/services/skills-sync.service.js +55 -2
  238. package/dist/core/sdd/services/start.service.js +6 -4
  239. package/dist/core/sdd/skill-bundles-curation-schema.d.ts +66 -0
  240. package/dist/core/sdd/skill-bundles-curation-schema.js +52 -0
  241. package/dist/core/sdd/skill-evidence.d.ts +19 -0
  242. package/dist/core/sdd/skill-evidence.js +38 -0
  243. package/dist/core/sdd/skill-policy-pool.d.ts +46 -0
  244. package/dist/core/sdd/skill-policy-pool.js +185 -0
  245. package/dist/core/sdd/state.d.ts +22 -0
  246. package/dist/core/sdd/state.js +66 -41
  247. package/dist/core/sdd/structural-health.d.ts +42 -42
  248. package/dist/core/sdd/types.d.ts +33 -7
  249. package/dist/core/sdd/types.js +17 -0
  250. package/dist/core/sdd/upgrade-to-codesdd.d.ts +45 -0
  251. package/dist/core/sdd/upgrade-to-codesdd.js +179 -0
  252. package/dist/core/sdd/workspace-schemas.d.ts +285 -14
  253. package/dist/core/sdd/workspace-schemas.js +148 -0
  254. package/dist/core/sdd/write-manifest.js +22 -4
  255. package/dist/core/shared/skill-generation.d.ts +1 -1
  256. package/dist/core/shared/skill-generation.js +15 -15
  257. package/dist/core/shared/tool-detection.d.ts +3 -3
  258. package/dist/core/shared/tool-detection.js +14 -14
  259. package/dist/core/specs-apply.js +6 -6
  260. package/dist/core/templates/index.d.ts +1 -1
  261. package/dist/core/templates/index.js +1 -1
  262. package/dist/core/templates/workflows/apply-change.js +14 -14
  263. package/dist/core/templates/workflows/archive-change.js +32 -32
  264. package/dist/core/templates/workflows/bulk-archive-change.js +25 -25
  265. package/dist/core/templates/workflows/continue-change.js +12 -12
  266. package/dist/core/templates/workflows/explore.js +29 -29
  267. package/dist/core/templates/workflows/feedback.js +6 -6
  268. package/dist/core/templates/workflows/ff-change.js +24 -24
  269. package/dist/core/templates/workflows/new-change.js +20 -20
  270. package/dist/core/templates/workflows/onboard.js +33 -33
  271. package/dist/core/templates/workflows/propose.js +23 -23
  272. package/dist/core/templates/workflows/sdd.js +8 -8
  273. package/dist/core/templates/workflows/sync-specs.js +19 -19
  274. package/dist/core/templates/workflows/verify-change.js +17 -17
  275. package/dist/core/update.d.ts +2 -2
  276. package/dist/core/update.js +16 -15
  277. package/dist/core/validation/constants.d.ts +1 -1
  278. package/dist/core/validation/constants.js +1 -1
  279. package/dist/core/view.js +11 -11
  280. package/dist/telemetry/config.d.ts +2 -1
  281. package/dist/telemetry/config.js +17 -8
  282. package/dist/telemetry/index.d.ts +10 -2
  283. package/dist/telemetry/index.js +40 -7
  284. package/dist/ui/ascii-patterns.d.ts +2 -2
  285. package/dist/ui/ascii-patterns.js +2 -2
  286. package/dist/ui/welcome-screen.js +2 -2
  287. package/dist/utils/change-metadata.d.ts +4 -4
  288. package/dist/utils/change-metadata.js +6 -6
  289. package/dist/utils/change-utils.d.ts +3 -3
  290. package/dist/utils/change-utils.js +5 -5
  291. package/dist/utils/file-system.js +1 -1
  292. package/dist/utils/interactive.js +1 -1
  293. package/dist/utils/item-discovery.js +4 -4
  294. package/dist/utils/legacy-spec-compat.d.ts +2 -0
  295. package/dist/utils/legacy-spec-compat.js +2 -0
  296. package/dist/utils/shell-detection.d.ts +1 -0
  297. package/dist/utils/shell-detection.js +16 -0
  298. package/package.json +27 -17
  299. package/schemas/sdd/1-spec.schema.json +1 -1
  300. package/schemas/sdd/2-plan.schema.json +73 -1
  301. package/schemas/sdd/3-tasks.schema.json +73 -1
  302. package/schemas/sdd/4-changelog.schema.json +1 -1
  303. package/schemas/sdd/5-quality.schema.json +442 -2
  304. package/schemas/sdd/adr.schema.json +148 -0
  305. package/schemas/sdd/agent-binding-adapter.schema.json +210 -0
  306. package/schemas/sdd/agent-binding-resolution.schema.json +338 -0
  307. package/schemas/sdd/backlog-projection-plan.schema.json +180 -0
  308. package/schemas/sdd/backlog-provider-contract.schema.json +260 -0
  309. package/schemas/sdd/codesdd-plugin.schema.json +474 -0
  310. package/schemas/sdd/debate.schema.json +244 -0
  311. package/schemas/sdd/deepagent-decision-evidence.schema.json +58 -0
  312. package/schemas/sdd/deepagent-env-contract.schema.json +143 -0
  313. package/schemas/sdd/deepagent-quality-evidence.schema.json +108 -0
  314. package/schemas/sdd/deepagent-run-evidence.schema.json +192 -0
  315. package/schemas/sdd/deepagent-run-plan.schema.json +197 -0
  316. package/schemas/sdd/deepagent-run-request.schema.json +321 -0
  317. package/schemas/sdd/deepagent-subagent-evidence.schema.json +110 -0
  318. package/schemas/sdd/deepagent-tool-call-evidence.schema.json +78 -0
  319. package/schemas/sdd/discarded.schema.json +127 -0
  320. package/schemas/sdd/epic.schema.json +147 -0
  321. package/schemas/sdd/insight.schema.json +136 -0
  322. package/schemas/sdd/parallel-feat-automation-plan.schema.json +215 -0
  323. package/schemas/sdd/parallel-feat-automation-request.schema.json +109 -0
  324. package/schemas/sdd/plugin-artifact-manifest.schema.json +150 -0
  325. package/schemas/sdd/plugin-compliance-index.schema.json +136 -0
  326. package/schemas/sdd/plugin-dry-run-plan.schema.json +260 -0
  327. package/schemas/sdd/plugin-evidence-manifest.schema.json +569 -0
  328. package/schemas/sdd/plugin-policy-evaluation.schema.json +92 -0
  329. package/schemas/sdd/plugin-policy-pack-evaluation.schema.json +94 -0
  330. package/schemas/sdd/plugin-policy-pack.schema.json +196 -0
  331. package/schemas/sdd/plugin-registry.schema.json +558 -0
  332. package/schemas/sdd/plugin-rollback-manifest.schema.json +87 -0
  333. package/schemas/sdd/plugin-runtime-invocation-plan.schema.json +845 -0
  334. package/schemas/sdd/plugin-skill-binding-resolution.schema.json +305 -0
  335. package/schemas/sdd/plugin-skill-binding.schema.json +88 -0
  336. package/schemas/sdd/plugin-validation-manifest.schema.json +123 -0
  337. package/schemas/sdd/quality-architecture-schema.schema.json +216 -0
  338. package/schemas/sdd/quality-evidence-bundle.schema.json +1228 -0
  339. package/schemas/sdd/quality-run.schema.json +197 -0
  340. package/schemas/sdd/quality-scenario.schema.json +252 -0
  341. package/schemas/sdd/workspace-catalog.schema.json +9841 -22
  342. package/schemas/spec-driven/schema.yaml +4 -4
  343. package/schemas/spec-driven/templates/proposal.md +1 -1
  344. package/dist/utils/openspec-compat.d.ts +0 -2
  345. package/dist/utils/openspec-compat.js +0 -2
@@ -0,0 +1,608 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import ts from 'typescript';
4
+ import { parseQualityArchitectureSchema, } from './quality-validation.js';
5
+ export const DEVTRACK_API_ARCHITECTURE_SCHEMA_ID = 'ARCH-GATE-devtrack-api-p0';
6
+ const DEFAULT_SOURCE_ROOTS = ['src', 'prisma'];
7
+ const TYPESCRIPT_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts'];
8
+ const IGNORED_DIRECTORIES = new Set(['.git', 'node_modules', 'dist', 'build', 'coverage', '.turbo']);
9
+ const LEGACY_ENTITY_PATTERN_CONTEXTS = new Set(['pessoas', 'wallets']);
10
+ const PRESENTATION_TRANSPORTS = new Set(['rest', 'graphql', 'cli', 'websocket', 'agent']);
11
+ const DEVTRACK_ALIAS_PREFIXES = [
12
+ '@domain/',
13
+ '@application/',
14
+ '@presentation/',
15
+ '@infrastructure/',
16
+ '@infrastructure/adapter/',
17
+ '@shared/',
18
+ '@src/',
19
+ ];
20
+ export function buildDevTrackApiArchitectureSchema() {
21
+ return parseQualityArchitectureSchema({
22
+ schema_version: 1,
23
+ id: DEVTRACK_API_ARCHITECTURE_SCHEMA_ID,
24
+ title: 'DevTrack API P0 architecture warning gate',
25
+ stack: 'typescript-nestjs-typeorm',
26
+ authority: [
27
+ {
28
+ type: 'skill',
29
+ ref: 'devtrack-api',
30
+ path: '.sdd/skills/curated/devtrack-api/SKILL.md',
31
+ update_policy: 'Review when the devtrack-api skill changes.',
32
+ },
33
+ {
34
+ type: 'foundation',
35
+ ref: 'devtrack-foundation-api',
36
+ path: '.sdd/skills/curated/devtrack-api/references/foundation-layout.md',
37
+ update_policy: 'Review when the Foundation layout reference changes.',
38
+ },
39
+ ],
40
+ applies_to: {
41
+ plugin: 'codesdd-plugin-devtrack-api',
42
+ project_kind: 'backend-api',
43
+ },
44
+ layers: {
45
+ domain: {
46
+ path_patterns: ['src/domain/**'],
47
+ forbidden_file_patterns: ['**/*.orm-entity.ts', '**/*.entity.ts', '**/*.value-object.ts'],
48
+ forbidden_imports: ['@application/**', '@presentation/**', '@infrastructure/**', '@nestjs/**', 'typeorm', 'class-validator'],
49
+ required_evidence: ['import_graph'],
50
+ },
51
+ application: {
52
+ path_patterns: ['src/application/**'],
53
+ forbidden_imports: ['@presentation/**', '@infrastructure/adapters/**', '@src/infrastructure/adapters/**'],
54
+ required_evidence: ['import_graph'],
55
+ },
56
+ presentation: {
57
+ path_patterns: ['src/presentation/**'],
58
+ forbidden_imports: ['@infrastructure/adapters/**', '@src/infrastructure/adapters/**', 'typeorm'],
59
+ required_evidence: ['import_graph'],
60
+ },
61
+ infrastructure: {
62
+ path_patterns: ['src/infrastructure/**'],
63
+ allowed_file_patterns: [
64
+ 'src/infrastructure/adapters/orm/entities/**/*.orm-entity.ts',
65
+ 'src/infrastructure/adapters/orm/repositories/**/*.typeorm-repository.ts',
66
+ ],
67
+ forbidden_imports: ['@prisma/**', 'prisma'],
68
+ required_evidence: ['filesystem_snapshot', 'import_graph'],
69
+ },
70
+ },
71
+ exceptions: {
72
+ require_adr: true,
73
+ require_expiration: true,
74
+ allowed_refs: [],
75
+ },
76
+ update_policy: {
77
+ owner: 'architecture',
78
+ review_trigger: 'DevTrack API skill, Foundation layout, or TypeORM architecture change',
79
+ stale_after_days: 90,
80
+ },
81
+ });
82
+ }
83
+ export async function collectDevTrackApiImportGraph(input) {
84
+ const files = await resolveTypeScriptFiles(input);
85
+ const edges = [];
86
+ for (const file of files) {
87
+ const absolutePath = path.resolve(input.projectRoot, file);
88
+ const sourceText = await fs.readFile(absolutePath, 'utf-8');
89
+ const imports = input.collector === 'text'
90
+ ? collectTextImportSpecifiers(sourceText)
91
+ : collectTypeScriptImportSpecifiers(sourceText, file);
92
+ for (const importSpecifier of imports) {
93
+ edges.push({
94
+ from: file,
95
+ to: importSpecifier,
96
+ kind: importKind(importSpecifier),
97
+ });
98
+ }
99
+ }
100
+ return {
101
+ nodes: files,
102
+ edges: edges.sort(compareImportEdges),
103
+ };
104
+ }
105
+ export async function validateDevTrackApiArchitecture(input) {
106
+ const importGraph = input.importGraph ?? await collectDevTrackApiImportGraph(input);
107
+ const files = await resolveArchitectureFiles(input, importGraph);
108
+ const fileContents = await readExistingFiles(input.projectRoot, files);
109
+ const findings = [];
110
+ for (const edge of importGraph.edges) {
111
+ const fromLayer = layerFromPath(edge.from);
112
+ const targetLayer = layerFromImport(edge.from, edge.to);
113
+ if (edge.kind === 'relative') {
114
+ findings.push(finding('ARCH_RELATIVE_IMPORT_FORBIDDEN', edge.from, edge.to, `Relative import "${edge.to}" is not allowed in ${edge.from}.`, 'Use the DevTrack API aliases such as @domain/*, @application/*, @infrastructure/*, @presentation/*, or @shared/*.'));
115
+ }
116
+ if (isPrismaImport(edge.to)) {
117
+ findings.push(finding('ARCH_PRISMA_IMPORT_FORBIDDEN', edge.from, edge.to, `Prisma import "${edge.to}" is not allowed in DevTrack API code.`, 'Use TypeORM infrastructure adapters and remove Prisma clients, schemas, migrations, or generated imports.'));
118
+ }
119
+ if (fromLayer === 'domain') {
120
+ if (isDomainForbiddenImport(edge.to) || targetLayer === 'application' || targetLayer === 'presentation' || targetLayer === 'infrastructure') {
121
+ findings.push(finding('ARCH_DOMAIN_FORBIDDEN_IMPORT', edge.from, edge.to, `Domain file ${edge.from} imports a forbidden dependency through "${edge.to}".`, 'Keep domain inward-only: depend on domain/shared primitives and move orchestration, transport, and adapters outward.'));
122
+ }
123
+ }
124
+ if (fromLayer === 'application' && targetLayer === 'presentation') {
125
+ findings.push(finding('ARCH_APPLICATION_PRESENTATION_IMPORT', edge.from, edge.to, `Application file ${edge.from} imports presentation through "${edge.to}".`, 'Keep transport dependencies in presentation and expose application behavior through use cases or ports.'));
126
+ }
127
+ if (fromLayer === 'application' && isConcreteInfrastructureImport(edge.to)) {
128
+ findings.push(finding('ARCH_APPLICATION_CONCRETE_INFRA_IMPORT', edge.from, edge.to, `Application file ${edge.from} imports concrete infrastructure through "${edge.to}".`, 'Depend on application-owned ports or shared adapter ports instead of concrete infrastructure adapters.'));
129
+ }
130
+ if (fromLayer === 'presentation' && (isConcreteInfrastructureImport(edge.to) || edge.to === 'typeorm')) {
131
+ findings.push(finding('ARCH_PRESENTATION_INFRA_IMPORT', edge.from, edge.to, `Presentation file ${edge.from} imports infrastructure through "${edge.to}".`, 'Keep presentation thin and call application use cases instead of concrete infrastructure or ORM contracts.'));
132
+ }
133
+ if (fromLayer !== 'infrastructure' && isTypeOrmImport(edge.to)) {
134
+ findings.push(finding('ARCH_TYPEORM_IMPORT_OUTSIDE_INFRASTRUCTURE', edge.from, edge.to, `TypeORM import "${edge.to}" appears outside infrastructure in ${edge.from}.`, 'Move TypeORM decorators, repositories, and ORM-specific types into src/infrastructure/adapters/orm/.'));
135
+ }
136
+ }
137
+ for (const [file, content] of fileContents) {
138
+ const layer = layerFromPath(file);
139
+ if (isPrismaFile(file, content)) {
140
+ findings.push(finding('ARCH_PRISMA_FILE_FORBIDDEN', file, undefined, `Prisma artifact or usage was found in ${file}.`, 'Remove Prisma artifacts and model persistence with TypeORM infrastructure adapters only.'));
141
+ }
142
+ if (isTypeOrmEntityFile(file, content) && !isCanonicalTypeOrmEntityPath(file)) {
143
+ findings.push(finding(layer === 'infrastructure' ? 'ARCH_TYPEORM_ENTITY_PATH_INVALID' : 'ARCH_ENTITY_OUTSIDE_INFRASTRUCTURE', file, undefined, `TypeORM entity ${file} is outside src/infrastructure/adapters/orm/entities/.`, 'Move TypeORM decorators and ORM entity files to src/infrastructure/adapters/orm/entities/.'));
144
+ }
145
+ if (isTypeOrmEntityLikePath(file, content) && !normalizeSlashes(file).endsWith('.orm-entity.ts')) {
146
+ findings.push(finding('ARCH_TYPEORM_ENTITY_SUFFIX_INVALID', file, undefined, `TypeORM entity ${file} does not use the .orm-entity.ts suffix.`, 'Rename ORM entity files to <name>.orm-entity.ts.'));
147
+ }
148
+ if (isRepositoryImplementationFile(file) && !isCanonicalTypeOrmRepositoryPath(file)) {
149
+ findings.push(finding(layer === 'infrastructure' ? 'ARCH_TYPEORM_REPOSITORY_PATH_INVALID' : 'ARCH_REPOSITORY_OUTSIDE_INFRASTRUCTURE', file, undefined, `Repository implementation ${file} is outside src/infrastructure/adapters/orm/repositories/.`, 'Keep concrete repositories under src/infrastructure/adapters/orm/repositories/ and keep application repository ports as *-repository.port.ts.'));
150
+ }
151
+ if (isRepositoryImplementationFile(file) && !normalizeSlashes(file).endsWith('.typeorm-repository.ts')) {
152
+ findings.push(finding('ARCH_TYPEORM_REPOSITORY_SUFFIX_INVALID', file, undefined, `Repository implementation ${file} does not use the .typeorm-repository.ts suffix.`, 'Rename concrete ORM repository files to <name>.typeorm-repository.ts.'));
153
+ }
154
+ collectDivergenceFindings(file, findings);
155
+ collectSemanticFindings(file, content, findings);
156
+ }
157
+ detectMissingAggregateModules(files, fileContents, findings);
158
+ detectMissingRuntimePersistence(files, fileContents, findings);
159
+ const dedupedFindings = dedupeFindings(findings);
160
+ return {
161
+ status: dedupedFindings.length > 0 ? 'warning' : 'passed',
162
+ architecture_schema: buildDevTrackApiArchitectureSchema(),
163
+ import_graph: importGraph,
164
+ findings: dedupedFindings,
165
+ };
166
+ }
167
+ function finding(code, filePath, importSpecifier, message, remediation) {
168
+ return {
169
+ code,
170
+ severity: 'warn',
171
+ path: filePath,
172
+ import_specifier: importSpecifier,
173
+ message,
174
+ remediation,
175
+ };
176
+ }
177
+ async function resolveTypeScriptFiles(input) {
178
+ const files = input.files
179
+ ? input.files.map((file) => normalizeProjectPath(input.projectRoot, file))
180
+ : await collectFiles(input.projectRoot, sourceRoots(input), isTypeScriptSourceFile);
181
+ return unique(files.filter(isTypeScriptSourceFile).filter(isSafeProjectPath)).sort();
182
+ }
183
+ async function resolveArchitectureFiles(input, importGraph) {
184
+ if (input.files) {
185
+ return unique(input.files.map((file) => normalizeProjectPath(input.projectRoot, file)).filter(isSafeProjectPath)).sort();
186
+ }
187
+ const collected = await collectFiles(input.projectRoot, sourceRoots(input), isArchitectureSourceFile);
188
+ return unique([...importGraph.nodes, ...collected].filter(isSafeProjectPath)).sort();
189
+ }
190
+ async function collectFiles(projectRoot, roots, includeFile) {
191
+ const files = [];
192
+ for (const root of roots) {
193
+ const safeRoot = normalizeProjectPath(projectRoot, root);
194
+ if (!isSafeProjectPath(safeRoot))
195
+ continue;
196
+ const absoluteRoot = path.resolve(projectRoot, safeRoot);
197
+ await collectFilesRecursive(projectRoot, absoluteRoot, files, includeFile);
198
+ }
199
+ return unique(files).sort();
200
+ }
201
+ async function collectFilesRecursive(projectRoot, directory, files, includeFile) {
202
+ const entries = await fs.readdir(directory, { withFileTypes: true }).catch(() => []);
203
+ for (const entry of entries) {
204
+ if (entry.isDirectory()) {
205
+ if (IGNORED_DIRECTORIES.has(entry.name))
206
+ continue;
207
+ await collectFilesRecursive(projectRoot, path.join(directory, entry.name), files, includeFile);
208
+ continue;
209
+ }
210
+ if (!entry.isFile())
211
+ continue;
212
+ const relativePath = normalizeProjectPath(projectRoot, path.join(directory, entry.name));
213
+ if (includeFile(relativePath))
214
+ files.push(relativePath);
215
+ }
216
+ }
217
+ async function readExistingFiles(projectRoot, files) {
218
+ const result = new Map();
219
+ for (const file of files) {
220
+ if (!isSafeProjectPath(file))
221
+ continue;
222
+ const absolutePath = path.resolve(projectRoot, file);
223
+ const content = await fs.readFile(absolutePath, 'utf-8').catch(() => null);
224
+ if (content !== null)
225
+ result.set(file, content);
226
+ }
227
+ return result;
228
+ }
229
+ function collectTypeScriptImportSpecifiers(sourceText, filePath) {
230
+ try {
231
+ const sourceFile = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true, scriptKind(filePath));
232
+ const imports = [];
233
+ const visit = (node) => {
234
+ if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
235
+ node.moduleSpecifier &&
236
+ isStringLiteralLike(node.moduleSpecifier)) {
237
+ imports.push(node.moduleSpecifier.text);
238
+ }
239
+ if (ts.isCallExpression(node)) {
240
+ const firstArgument = node.arguments[0];
241
+ if (node.expression.kind === ts.SyntaxKind.ImportKeyword && firstArgument && isStringLiteralLike(firstArgument)) {
242
+ imports.push(firstArgument.text);
243
+ }
244
+ if (ts.isIdentifier(node.expression) && node.expression.text === 'require' && firstArgument && isStringLiteralLike(firstArgument)) {
245
+ imports.push(firstArgument.text);
246
+ }
247
+ }
248
+ if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && isStringLiteralLike(node.argument.literal)) {
249
+ imports.push(node.argument.literal.text);
250
+ }
251
+ ts.forEachChild(node, visit);
252
+ };
253
+ visit(sourceFile);
254
+ return unique(imports);
255
+ }
256
+ catch {
257
+ return collectTextImportSpecifiers(sourceText);
258
+ }
259
+ }
260
+ function collectTextImportSpecifiers(sourceText) {
261
+ const withoutComments = stripComments(sourceText);
262
+ const imports = [];
263
+ const patterns = [
264
+ /\bimport\s+(?:type\s+)?(?:[^'";]*?\s+from\s+)?['"]([^'"]+)['"]/gmu,
265
+ /\bexport\s+(?:type\s+)?[^'";]*?\s+from\s+['"]([^'"]+)['"]/gmu,
266
+ /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/gmu,
267
+ /\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/gmu,
268
+ ];
269
+ for (const pattern of patterns) {
270
+ for (const match of withoutComments.matchAll(pattern)) {
271
+ const importSpecifier = match[1];
272
+ if (importSpecifier)
273
+ imports.push(importSpecifier);
274
+ }
275
+ }
276
+ return unique(imports);
277
+ }
278
+ function stripComments(sourceText) {
279
+ return sourceText
280
+ .replace(/\/\*[\s\S]*?\*\//gu, '')
281
+ .replace(/(^|[^:])\/\/.*$/gmu, '$1');
282
+ }
283
+ function scriptKind(filePath) {
284
+ if (filePath.endsWith('.tsx'))
285
+ return ts.ScriptKind.TSX;
286
+ if (filePath.endsWith('.jsx'))
287
+ return ts.ScriptKind.JSX;
288
+ return ts.ScriptKind.TS;
289
+ }
290
+ function isStringLiteralLike(node) {
291
+ return ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node);
292
+ }
293
+ function importKind(importSpecifier) {
294
+ if (importSpecifier.startsWith('.'))
295
+ return 'relative';
296
+ if (DEVTRACK_ALIAS_PREFIXES.some((prefix) => importSpecifier.startsWith(prefix)))
297
+ return 'alias';
298
+ return 'package';
299
+ }
300
+ function layerFromImport(fromPath, importSpecifier) {
301
+ const aliasLayer = layerFromAlias(importSpecifier);
302
+ if (aliasLayer)
303
+ return aliasLayer;
304
+ if (!importSpecifier.startsWith('.'))
305
+ return undefined;
306
+ const resolved = path.posix.normalize(path.posix.join(path.posix.dirname(fromPath), importSpecifier));
307
+ return layerFromPath(resolved);
308
+ }
309
+ function layerFromAlias(importSpecifier) {
310
+ if (importSpecifier.startsWith('@domain/'))
311
+ return 'domain';
312
+ if (importSpecifier.startsWith('@application/'))
313
+ return 'application';
314
+ if (importSpecifier.startsWith('@presentation/'))
315
+ return 'presentation';
316
+ if (importSpecifier.startsWith('@infrastructure/'))
317
+ return 'infrastructure';
318
+ if (importSpecifier.startsWith('@shared/'))
319
+ return 'shared';
320
+ if (importSpecifier.startsWith('@src/domain/'))
321
+ return 'domain';
322
+ if (importSpecifier.startsWith('@src/application/'))
323
+ return 'application';
324
+ if (importSpecifier.startsWith('@src/presentation/'))
325
+ return 'presentation';
326
+ if (importSpecifier.startsWith('@src/infrastructure/'))
327
+ return 'infrastructure';
328
+ if (importSpecifier.startsWith('@src/shared/'))
329
+ return 'shared';
330
+ return undefined;
331
+ }
332
+ function layerFromPath(filePath) {
333
+ const normalized = normalizeSlashes(filePath);
334
+ if (normalized === 'src/domain' || normalized.startsWith('src/domain/'))
335
+ return 'domain';
336
+ if (normalized === 'src/application' || normalized.startsWith('src/application/'))
337
+ return 'application';
338
+ if (normalized === 'src/presentation' || normalized.startsWith('src/presentation/'))
339
+ return 'presentation';
340
+ if (normalized === 'src/infrastructure' || normalized.startsWith('src/infrastructure/'))
341
+ return 'infrastructure';
342
+ if (normalized === 'src/shared' || normalized.startsWith('src/shared/'))
343
+ return 'shared';
344
+ return undefined;
345
+ }
346
+ function isPrismaImport(importSpecifier) {
347
+ const normalized = importSpecifier.toLowerCase();
348
+ return normalized === 'prisma' || normalized.startsWith('@prisma/') || normalized.includes('/prisma/');
349
+ }
350
+ function isDomainForbiddenImport(importSpecifier) {
351
+ const normalized = importSpecifier.toLowerCase();
352
+ return (normalized.startsWith('@nestjs/') ||
353
+ normalized === 'typeorm' ||
354
+ normalized === '@nestjs/typeorm' ||
355
+ normalized === 'class-validator' ||
356
+ normalized.startsWith('class-validator/'));
357
+ }
358
+ function isConcreteInfrastructureImport(importSpecifier) {
359
+ return (importSpecifier.startsWith('@infrastructure/adapters/') ||
360
+ importSpecifier.startsWith('@src/infrastructure/adapters/'));
361
+ }
362
+ function isTypeOrmImport(importSpecifier) {
363
+ const normalized = importSpecifier.toLowerCase();
364
+ return normalized === 'typeorm' || normalized === '@nestjs/typeorm' || normalized.includes('/typeorm');
365
+ }
366
+ function isPrismaFile(filePath, content) {
367
+ const normalized = normalizeSlashes(filePath).toLowerCase();
368
+ const segments = normalized.split('/');
369
+ return (normalized.endsWith('.prisma') ||
370
+ segments.includes('prisma') ||
371
+ content.includes('@prisma/client') ||
372
+ /\bPrismaClient\b/u.test(content));
373
+ }
374
+ function isTypeOrmEntityFile(filePath, content) {
375
+ const normalized = normalizeSlashes(filePath);
376
+ return (normalized.endsWith('.orm-entity.ts') ||
377
+ (normalized.endsWith('.entity.ts') && content.includes('typeorm')) ||
378
+ /@Entity\s*\(/u.test(content));
379
+ }
380
+ function isTypeOrmEntityLikePath(filePath, content) {
381
+ const normalized = normalizeSlashes(filePath);
382
+ return normalized.endsWith('.entity.ts') || normalized.endsWith('.orm-entity.ts') || /@Entity\s*\(/u.test(content);
383
+ }
384
+ function isCanonicalTypeOrmEntityPath(filePath) {
385
+ const normalized = normalizeSlashes(filePath);
386
+ return normalized.startsWith('src/infrastructure/adapters/orm/entities/') && normalized.endsWith('.orm-entity.ts');
387
+ }
388
+ function isRepositoryImplementationFile(filePath) {
389
+ const normalized = normalizeSlashes(filePath);
390
+ const fileName = path.posix.basename(normalized);
391
+ return (normalized.includes('/repositories/') ||
392
+ fileName.endsWith('.typeorm-repository.ts') ||
393
+ (fileName.endsWith('.repository.ts') && !fileName.endsWith('-repository.port.ts')));
394
+ }
395
+ function isCanonicalTypeOrmRepositoryPath(filePath) {
396
+ const normalized = normalizeSlashes(filePath);
397
+ return (normalized.startsWith('src/infrastructure/adapters/orm/repositories/') &&
398
+ normalized.endsWith('.typeorm-repository.ts'));
399
+ }
400
+ const CANONICAL_BO_METHODS = new Set(['toJSON', 'equals', 'fromJSON', 'toPersistence', 'fromPersistence']);
401
+ const CANONICAL_VO_METHODS = new Set(['normalize', 'isValid', 'getValue', 'toString', 'equals']);
402
+ function collectSemanticFindings(filePath, content, findings) {
403
+ const normalized = normalizeSlashes(filePath);
404
+ detectFakeTypeOrmRepository(normalized, content, findings);
405
+ detectPresentationCompositionRoot(normalized, content, findings);
406
+ detectPortMissingSymbol(normalized, content, findings);
407
+ detectNonCanonicalBoVo(normalized, content, findings);
408
+ }
409
+ function detectFakeTypeOrmRepository(filePath, content, findings) {
410
+ if (!filePath.endsWith('.typeorm-repository.ts'))
411
+ return;
412
+ const hasInjectRepository = /@InjectRepository\s*\(/u.test(content);
413
+ const usesRepositoryType = /\bRepository\s*[<]/u.test(content);
414
+ const hasRepositoryExtends = /\bextends\s+\w*Repository\b/u.test(content);
415
+ const isRealTypeOrm = hasInjectRepository || usesRepositoryType || hasRepositoryExtends;
416
+ if (!isRealTypeOrm) {
417
+ findings.push(finding('ARCH_TYPEORM_REPOSITORY_FAKE', filePath, undefined, `TypeORM repository ${filePath} lacks real TypeORM wiring (no @InjectRepository, no Repository<Entity> usage).`, 'Implement real TypeORM wiring with @InjectRepository(Entity) and Repository<Entity> or rename to reflect it is a prototype/adapter.'));
418
+ return;
419
+ }
420
+ if (/\bnew\s+Map\s*\(/u.test(content)) {
421
+ findings.push(finding('ARCH_TYPEORM_REPOSITORY_FAKE', filePath, undefined, `TypeORM repository ${filePath} uses in-memory Map persistence, indicating a fake/stub implementation.`, 'Replace in-memory Map with real TypeORM Repository operations or rename to indicate prototype status.'));
422
+ }
423
+ }
424
+ function detectPresentationCompositionRoot(filePath, content, findings) {
425
+ if (!filePath.startsWith('src/presentation/'))
426
+ return;
427
+ const hasModuleDecorator = /@Module\s*\(\s*\{/u.test(content);
428
+ if (!hasModuleDecorator)
429
+ return;
430
+ const hasServiceInstantiation = /\bnew\s+\w*(?:UseCase|Repository|Adapter|Service|Port)\w*\s*\(/u.test(content);
431
+ if (hasServiceInstantiation) {
432
+ findings.push(finding('ARCH_PRESENTATION_COMPOSITION_ROOT', filePath, undefined, `Presentation file ${filePath} directly instantiates application/infrastructure classes.`, 'Inject dependencies through NestJS DI and keep presentation importing only application boundaries.'));
433
+ }
434
+ }
435
+ function detectPortMissingSymbol(filePath, content, findings) {
436
+ if (!filePath.endsWith('.port.ts'))
437
+ return;
438
+ const hasExportedSymbol = /\bexport\s+(const|let|var)\s+\w+\s*=\s*Symbol\s*\(/u.test(content);
439
+ if (!hasExportedSymbol) {
440
+ findings.push(finding('ARCH_PORT_MISSING_SYMBOL', filePath, undefined, `Port file ${filePath} lacks an exported Symbol for the port contract.`, 'Add an exported Symbol for the port, e.g.: export const ORDER_REPOSITORY_PORT = Symbol(\'ORDER_REPOSITORY_PORT\');'));
441
+ }
442
+ detectPortGroupedUnrelated(filePath, content, findings);
443
+ }
444
+ function detectPortGroupedUnrelated(filePath, content, findings) {
445
+ if (filePath.includes('/ports/in/')) {
446
+ const exportedInterfaces = [...content.matchAll(/\bexport\s+interface\s+(\w+)/gmu)]
447
+ .map((match) => match[1])
448
+ .filter(Boolean);
449
+ const useCasePorts = exportedInterfaces.filter((name) => /(?:UseCase|UseCasePort)$/u.test(name));
450
+ if (useCasePorts.length > 1) {
451
+ findings.push(finding('ARCH_PORT_GROUPED_UNRELATED', filePath, undefined, `Input port file ${filePath} groups multiple use-case ports: ${useCasePorts.join(', ')}.`, 'Split input ports by use case so each *.use-case.port.ts file owns one use-case contract plus its exported Symbol.'));
452
+ }
453
+ }
454
+ }
455
+ function detectNonCanonicalBoVo(filePath, content, findings) {
456
+ if (filePath.endsWith('.bo.ts')) {
457
+ const missingMethods = [];
458
+ for (const method of CANONICAL_BO_METHODS) {
459
+ if (!new RegExp(`\\b${method}\\s*\\(`, 'u').test(content)) {
460
+ missingMethods.push(method);
461
+ }
462
+ }
463
+ if (missingMethods.length >= 2) {
464
+ findings.push(finding('ARCH_BO_MISSING_METHODS', filePath, undefined, `BO ${filePath} is missing canonical methods: ${missingMethods.join(', ')}.`, `Add at least the following methods to the business object: ${[...CANONICAL_BO_METHODS].join(', ')}.`));
465
+ }
466
+ }
467
+ if (filePath.endsWith('.vo.ts')) {
468
+ const missingMethods = [];
469
+ for (const method of CANONICAL_VO_METHODS) {
470
+ if (!new RegExp(`\\b${method}\\s*\\(`, 'u').test(content)) {
471
+ missingMethods.push(method);
472
+ }
473
+ }
474
+ if (missingMethods.length >= 2) {
475
+ findings.push(finding('ARCH_VO_MISSING_METHODS', filePath, undefined, `VO ${filePath} is missing canonical methods: ${missingMethods.join(', ')}.`, `Add at least the following methods to the value object: ${[...CANONICAL_VO_METHODS].join(', ')}.`));
476
+ }
477
+ }
478
+ }
479
+ function detectMissingAggregateModules(files, fileContents, findings) {
480
+ const fileSet = new Set(files.map(f => normalizeSlashes(f)));
481
+ const hasAppModule = [...fileSet].some(f => f === 'src/application/application.module.ts' ||
482
+ /^src\/application\/\w+\/application\.module\.ts$/.test(f));
483
+ if (!hasAppModule) {
484
+ findings.push(finding('ARCH_AGGREGATE_MODULE_MISSING', 'src/application', undefined, 'Missing aggregate module: src/application/application.module.ts was not found.', 'Create src/application/application.module.ts with @Module({...}) decorator and appropriate providers/exports.'));
485
+ }
486
+ const hasInfraModule = [...fileSet].some(f => f === 'src/infrastructure/infrastructure.module.ts' ||
487
+ /^src\/infrastructure\/\w+\/infrastructure\.module\.ts$/.test(f));
488
+ if (!hasInfraModule) {
489
+ findings.push(finding('ARCH_AGGREGATE_MODULE_MISSING', 'src/infrastructure', undefined, 'Missing aggregate module: src/infrastructure/infrastructure.module.ts was not found.', 'Create src/infrastructure/infrastructure.module.ts with @Module({...}) decorator and TypeORM imports.'));
490
+ }
491
+ const hasPresModuleByTransport = [...fileSet].some(f => f === 'src/presentation/presentation.module.ts' ||
492
+ [...PRESENTATION_TRANSPORTS].some(transport => f === `src/presentation/${transport}/${transport}.module.ts` ||
493
+ f === `src/presentation/${transport}.module.ts`));
494
+ if (!hasPresModuleByTransport) {
495
+ findings.push(finding('ARCH_AGGREGATE_MODULE_MISSING', 'src/presentation', undefined, 'Missing aggregate module: src/presentation/presentation.module.ts or transport-specific module was not found.', 'Create src/presentation/presentation.module.ts or a transport-specific module with @Module({...}) decorator.'));
496
+ }
497
+ for (const [filePath, content] of fileContents) {
498
+ const normalized = normalizeSlashes(filePath);
499
+ if (/\.module\.ts$/.test(normalized)) {
500
+ const hasNestJSModule = /@Module\s*\(\s*\{/u.test(content);
501
+ if (!hasNestJSModule) {
502
+ findings.push(finding('ARCH_AGGREGATE_MODULE_NOT_NESTJS', normalized, undefined, `Module file ${normalized} does not contain a NestJS @Module({...}) decorator.`, 'Add @Module({...}) decorator to the module class or rename the file if it is not a NestJS module.'));
503
+ }
504
+ }
505
+ }
506
+ }
507
+ function detectMissingRuntimePersistence(files, fileContents, findings) {
508
+ const fileSet = new Set(files.map(f => normalizeSlashes(f)));
509
+ const hasTypeOrmRepos = [...fileSet].some(f => f.endsWith('.typeorm-repository.ts'));
510
+ const hasTypeOrmEntities = [...fileSet].some(f => f.endsWith('.orm-entity.ts'));
511
+ if (!hasTypeOrmRepos && !hasTypeOrmEntities)
512
+ return;
513
+ const hasDataSource = [...fileContents.entries()].some(([_, content]) => /\bnew\s+DataSource\s*\(/u.test(content) ||
514
+ /TypeOrmModule\.(?:forRoot|forRootAsync)\s*\(/u.test(content));
515
+ if (!hasDataSource) {
516
+ findings.push(finding('ARCH_MISSING_DATASOURCE', 'src/infrastructure', undefined, 'Missing DataSource or TypeORM root configuration.', 'Add a DataSource configuration or TypeOrmModule.forRoot() call in the infrastructure module.'));
517
+ }
518
+ const hasMigrations = [...fileSet].some(f => f.includes('/migrations/'));
519
+ if (!hasMigrations) {
520
+ findings.push(finding('ARCH_MISSING_MIGRATIONS', 'src/infrastructure/adapters/orm/migrations', undefined, 'Missing migrations directory or files under src/infrastructure/adapters/orm/migrations/.', 'Create migrations for persistent entities under src/infrastructure/adapters/orm/migrations/.'));
521
+ }
522
+ const hasForFeature = [...fileContents.entries()].some(([f, content]) => normalizeSlashes(f).startsWith('src/infrastructure/') &&
523
+ /TypeOrmModule\.forFeature\s*\(/u.test(content));
524
+ if (!hasForFeature) {
525
+ findings.push(finding('ARCH_MISSING_TYPEORM_MODULE_FORFEATURE', 'src/infrastructure', undefined, 'Missing TypeOrmModule.forFeature() wiring in infrastructure module.', 'Add TypeOrmModule.forFeature([Entity]) to the infrastructure module imports for concrete TypeORM repositories.'));
526
+ }
527
+ }
528
+ function collectDivergenceFindings(filePath, findings) {
529
+ const normalized = normalizeSlashes(filePath);
530
+ const segments = normalized.split('/');
531
+ if (segments[0] === 'src' && segments[1] === 'domain') {
532
+ const context = segments[2];
533
+ const subfolder = segments[3];
534
+ if (context && subfolder === 'entities' && !LEGACY_ENTITY_PATTERN_CONTEXTS.has(context)) {
535
+ findings.push(finding('ARCH_DOMAIN_ENTITY_CONTEXT_FORBIDDEN', normalized, undefined, `New domain context ${context} uses legacy entities/ layout.`, 'Use src/domain/<context>/business-objects/<name>.bo.ts for new contexts, or record an ADR for an entity-pattern exception.'));
536
+ }
537
+ if (context && subfolder === 'repository-ports' && !LEGACY_ENTITY_PATTERN_CONTEXTS.has(context)) {
538
+ findings.push(finding('ARCH_DOMAIN_REPOSITORY_PORT_CONTEXT_FORBIDDEN', normalized, undefined, `New domain context ${context} declares repository ports in domain.`, 'Move BO-pattern repository ports to src/application/business/<context>/ports/out/.'));
539
+ }
540
+ if (subfolder === 'exceptions') {
541
+ findings.push(finding('ARCH_DOMAIN_EXCEPTION_PATH_FORBIDDEN', normalized, undefined, `Domain exception path ${normalized} is not canonical.`, 'Use centralized exceptions under src/shared/domain/exceptions/ or record an ADR-backed exception.'));
542
+ }
543
+ if (subfolder === 'value-objects' && normalized.endsWith('.value-object.ts')) {
544
+ findings.push(finding('ARCH_VALUE_OBJECT_SUFFIX_INVALID', normalized, undefined, `Value object ${normalized} uses the non-canonical .value-object.ts suffix.`, 'Rename value objects to <name>.vo.ts.'));
545
+ }
546
+ }
547
+ if (segments[0] === 'src' && segments[1] === 'presentation') {
548
+ const transport = segments[2];
549
+ if (transport && !PRESENTATION_TRANSPORTS.has(transport)) {
550
+ findings.push(finding('ARCH_PRESENTATION_TRANSPORT_MISSING', normalized, undefined, `Presentation path ${normalized} is not segmented by a known transport.`, 'Use src/presentation/rest/<context>/... or another explicit transport segment.'));
551
+ }
552
+ }
553
+ if (segments[0] === 'src' && segments[1] === 'infrastructure') {
554
+ if (segments[2] && segments[2] !== 'adapters' && segments[3] === 'adapters') {
555
+ findings.push(finding('ARCH_INFRASTRUCTURE_CONTEXT_ADAPTER_FORBIDDEN', normalized, undefined, `Infrastructure path ${normalized} organizes adapters by business context.`, 'Use src/infrastructure/adapters/<subsystem>/... instead of src/infrastructure/<context>/adapters/.'));
556
+ }
557
+ if (segments[2] && segments[2] !== 'adapters' && segments.includes('persistence') && segments.includes('typeorm')) {
558
+ findings.push(finding('ARCH_INFRASTRUCTURE_CONTEXT_PERSISTENCE_FORBIDDEN', normalized, undefined, `Infrastructure path ${normalized} uses context-local TypeORM persistence.`, 'Use centralized TypeORM paths under src/infrastructure/adapters/orm/.'));
559
+ }
560
+ }
561
+ }
562
+ function isTypeScriptSourceFile(filePath) {
563
+ const normalized = normalizeSlashes(filePath);
564
+ return TYPESCRIPT_EXTENSIONS.some((extension) => normalized.endsWith(extension)) && !normalized.endsWith('.d.ts');
565
+ }
566
+ function isArchitectureSourceFile(filePath) {
567
+ const normalized = normalizeSlashes(filePath);
568
+ return isTypeScriptSourceFile(normalized) || normalized.endsWith('.prisma');
569
+ }
570
+ function sourceRoots(input) {
571
+ if (input.sourceRoots)
572
+ return input.sourceRoots;
573
+ if (input.sourceRoot)
574
+ return [input.sourceRoot];
575
+ return DEFAULT_SOURCE_ROOTS;
576
+ }
577
+ function normalizeProjectPath(projectRoot, filePath) {
578
+ const relativePath = path.isAbsolute(filePath) ? path.relative(projectRoot, filePath) : filePath;
579
+ return normalizeSlashes(relativePath).replace(/^\.\//u, '');
580
+ }
581
+ function normalizeSlashes(value) {
582
+ return value.replace(/\\/gu, '/');
583
+ }
584
+ function isSafeProjectPath(filePath) {
585
+ const normalized = normalizeSlashes(filePath);
586
+ return Boolean(normalized) && !normalized.startsWith('/') && !normalized.split('/').includes('..');
587
+ }
588
+ function dedupeFindings(findings) {
589
+ const seen = new Set();
590
+ const result = [];
591
+ for (const item of findings) {
592
+ const key = item.code === 'ARCH_PRISMA_FORBIDDEN'
593
+ ? `${item.code}:${item.path ?? ''}`
594
+ : `${item.code}:${item.path ?? ''}:${item.import_specifier ?? ''}`;
595
+ if (seen.has(key))
596
+ continue;
597
+ seen.add(key);
598
+ result.push(item);
599
+ }
600
+ return result.sort((left, right) => `${left.path ?? ''}:${left.import_specifier ?? ''}:${left.code}`.localeCompare(`${right.path ?? ''}:${right.import_specifier ?? ''}:${right.code}`));
601
+ }
602
+ function compareImportEdges(left, right) {
603
+ return `${left.from}:${left.to}`.localeCompare(`${right.from}:${right.to}`);
604
+ }
605
+ function unique(values) {
606
+ return [...new Set(values)];
607
+ }
608
+ //# sourceMappingURL=devtrack-api-architecture.js.map
@@ -0,0 +1,19 @@
1
+ import { type CollectDevTrackApiImportGraphInput, type DevTrackApiArchitectureFinding } from './devtrack-api-architecture.js';
2
+ import type { QualityArchitectureSchema, QualityImportGraph } from './quality-validation.js';
3
+ export type DevTrackApiImportBoundaryIssueCode = 'ARCH_RELATIVE_IMPORT_FORBIDDEN' | 'ARCH_DOMAIN_FORBIDDEN_IMPORT' | 'ARCH_APPLICATION_PRESENTATION_IMPORT' | 'ARCH_APPLICATION_CONCRETE_INFRA_IMPORT' | 'ARCH_PRESENTATION_INFRA_IMPORT' | 'ARCH_TYPEORM_IMPORT_OUTSIDE_INFRASTRUCTURE' | 'ARCH_PRISMA_IMPORT_FORBIDDEN';
4
+ export type DevTrackApiImportBoundaryFinding = DevTrackApiArchitectureFinding & {
5
+ code: DevTrackApiImportBoundaryIssueCode;
6
+ import_specifier: string;
7
+ };
8
+ export interface ValidateDevTrackApiImportBoundariesInput extends CollectDevTrackApiImportGraphInput {
9
+ importGraph?: QualityImportGraph;
10
+ }
11
+ export interface DevTrackApiImportBoundaryValidationResult {
12
+ status: 'passed' | 'warning';
13
+ architecture_schema: QualityArchitectureSchema;
14
+ import_graph: QualityImportGraph;
15
+ findings: DevTrackApiImportBoundaryFinding[];
16
+ }
17
+ export declare function validateDevTrackApiImportBoundaries(input: ValidateDevTrackApiImportBoundariesInput): Promise<DevTrackApiImportBoundaryValidationResult>;
18
+ export declare function buildDevTrackApiImportBoundarySchema(): QualityArchitectureSchema;
19
+ //# sourceMappingURL=devtrack-api-import-boundary.d.ts.map
@@ -0,0 +1,32 @@
1
+ import { buildDevTrackApiArchitectureSchema, collectDevTrackApiImportGraph, validateDevTrackApiArchitecture, } from './devtrack-api-architecture.js';
2
+ const IMPORT_BOUNDARY_CODES = new Set([
3
+ 'ARCH_RELATIVE_IMPORT_FORBIDDEN',
4
+ 'ARCH_DOMAIN_FORBIDDEN_IMPORT',
5
+ 'ARCH_APPLICATION_PRESENTATION_IMPORT',
6
+ 'ARCH_APPLICATION_CONCRETE_INFRA_IMPORT',
7
+ 'ARCH_PRESENTATION_INFRA_IMPORT',
8
+ 'ARCH_TYPEORM_IMPORT_OUTSIDE_INFRASTRUCTURE',
9
+ 'ARCH_PRISMA_IMPORT_FORBIDDEN',
10
+ ]);
11
+ export async function validateDevTrackApiImportBoundaries(input) {
12
+ const importGraph = input.importGraph ?? await collectDevTrackApiImportGraph(input);
13
+ const architectureResult = await validateDevTrackApiArchitecture({
14
+ ...input,
15
+ importGraph,
16
+ });
17
+ const findings = architectureResult.findings.filter(isImportBoundaryFinding);
18
+ return {
19
+ status: findings.length > 0 ? 'warning' : 'passed',
20
+ architecture_schema: architectureResult.architecture_schema,
21
+ import_graph: architectureResult.import_graph,
22
+ findings,
23
+ };
24
+ }
25
+ export function buildDevTrackApiImportBoundarySchema() {
26
+ return buildDevTrackApiArchitectureSchema();
27
+ }
28
+ function isImportBoundaryFinding(finding) {
29
+ return Boolean(finding.import_specifier &&
30
+ IMPORT_BOUNDARY_CODES.has(finding.code));
31
+ }
32
+ //# sourceMappingURL=devtrack-api-import-boundary.js.map