agent-sdd 1.0.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 (304) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1028 -0
  3. package/README.ru.md +1046 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.js +867 -0
  6. package/dist/features/approve/adapters/inbound/CliApproveHandler.d.ts +17 -0
  7. package/dist/features/approve/adapters/inbound/CliApproveHandler.js +108 -0
  8. package/dist/features/approve/adapters/outbound/NodeApproveFileSystem.d.ts +8 -0
  9. package/dist/features/approve/adapters/outbound/NodeApproveFileSystem.js +147 -0
  10. package/dist/features/approve/adapters/outbound/NodePlanFileWriter.d.ts +6 -0
  11. package/dist/features/approve/adapters/outbound/NodePlanFileWriter.js +92 -0
  12. package/dist/features/approve/adapters/outbound/SystemApproveClock.d.ts +4 -0
  13. package/dist/features/approve/adapters/outbound/SystemApproveClock.js +5 -0
  14. package/dist/features/approve/application/ApplyApproval.d.ts +19 -0
  15. package/dist/features/approve/application/ApplyApproval.js +30 -0
  16. package/dist/features/approve/application/WriteAttestation.d.ts +19 -0
  17. package/dist/features/approve/application/WriteAttestation.js +23 -0
  18. package/dist/features/approve/domain/ApproveRequest.d.ts +24 -0
  19. package/dist/features/approve/domain/ApproveRequest.js +24 -0
  20. package/dist/features/approve/domain/Rewrite.d.ts +1 -0
  21. package/dist/features/approve/domain/Rewrite.js +6 -0
  22. package/dist/features/approve/ports/inbound/ApproveCommand.d.ts +10 -0
  23. package/dist/features/approve/ports/inbound/ApproveCommand.js +1 -0
  24. package/dist/features/approve/ports/outbound/ApproveClock.d.ts +3 -0
  25. package/dist/features/approve/ports/outbound/ApproveClock.js +1 -0
  26. package/dist/features/approve/ports/outbound/ApproveConfigPort.d.ts +4 -0
  27. package/dist/features/approve/ports/outbound/ApproveConfigPort.js +1 -0
  28. package/dist/features/approve/ports/outbound/ApproveFileSystem.d.ts +8 -0
  29. package/dist/features/approve/ports/outbound/ApproveFileSystem.js +1 -0
  30. package/dist/features/approve/ports/outbound/PlanFileWriter.d.ts +13 -0
  31. package/dist/features/approve/ports/outbound/PlanFileWriter.js +1 -0
  32. package/dist/features/check/adapters/inbound/CliCheckHandler.d.ts +8 -0
  33. package/dist/features/check/adapters/inbound/CliCheckHandler.js +62 -0
  34. package/dist/features/check/adapters/outbound/ChildProcessCheckGit.d.ts +8 -0
  35. package/dist/features/check/adapters/outbound/ChildProcessCheckGit.js +112 -0
  36. package/dist/features/check/adapters/outbound/NodeCheckFileReader.d.ts +7 -0
  37. package/dist/features/check/adapters/outbound/NodeCheckFileReader.js +44 -0
  38. package/dist/features/check/application/CheckBaseline.d.ts +28 -0
  39. package/dist/features/check/application/CheckBaseline.js +54 -0
  40. package/dist/features/check/domain/BaselineComparison.d.ts +1 -0
  41. package/dist/features/check/domain/BaselineComparison.js +2 -0
  42. package/dist/features/check/ports/inbound/CheckCommand.d.ts +4 -0
  43. package/dist/features/check/ports/inbound/CheckCommand.js +1 -0
  44. package/dist/features/check/ports/outbound/CheckConfigPort.d.ts +4 -0
  45. package/dist/features/check/ports/outbound/CheckConfigPort.js +1 -0
  46. package/dist/features/check/ports/outbound/CheckGitPort.d.ts +7 -0
  47. package/dist/features/check/ports/outbound/CheckGitPort.js +1 -0
  48. package/dist/features/check/ports/outbound/CheckSpecPort.d.ts +9 -0
  49. package/dist/features/check/ports/outbound/CheckSpecPort.js +1 -0
  50. package/dist/features/doctor/adapters/inbound/CliDoctorHandler.d.ts +8 -0
  51. package/dist/features/doctor/adapters/inbound/CliDoctorHandler.js +77 -0
  52. package/dist/features/doctor/adapters/outbound/NodeRegistryReader.d.ts +5 -0
  53. package/dist/features/doctor/adapters/outbound/NodeRegistryReader.js +40 -0
  54. package/dist/features/doctor/application/RunDoctor.d.ts +30 -0
  55. package/dist/features/doctor/application/RunDoctor.js +78 -0
  56. package/dist/features/doctor/domain/RegistryRow.d.ts +23 -0
  57. package/dist/features/doctor/domain/RegistryRow.js +114 -0
  58. package/dist/features/doctor/domain/SemverRange.d.ts +7 -0
  59. package/dist/features/doctor/domain/SemverRange.js +82 -0
  60. package/dist/features/doctor/ports/inbound/DoctorCommand.d.ts +8 -0
  61. package/dist/features/doctor/ports/inbound/DoctorCommand.js +1 -0
  62. package/dist/features/doctor/ports/outbound/RegistryReader.d.ts +12 -0
  63. package/dist/features/doctor/ports/outbound/RegistryReader.js +1 -0
  64. package/dist/features/finalize/adapters/inbound/CliFinalizeHandler.d.ts +8 -0
  65. package/dist/features/finalize/adapters/inbound/CliFinalizeHandler.js +80 -0
  66. package/dist/features/finalize/adapters/outbound/NodeFinalizeFileSystem.d.ts +11 -0
  67. package/dist/features/finalize/adapters/outbound/NodeFinalizeFileSystem.js +167 -0
  68. package/dist/features/finalize/adapters/outbound/NodePlanRepo.d.ts +7 -0
  69. package/dist/features/finalize/adapters/outbound/NodePlanRepo.js +82 -0
  70. package/dist/features/finalize/adapters/outbound/SystemFinalizeClock.d.ts +4 -0
  71. package/dist/features/finalize/adapters/outbound/SystemFinalizeClock.js +5 -0
  72. package/dist/features/finalize/application/RunFinalize.d.ts +34 -0
  73. package/dist/features/finalize/application/RunFinalize.js +98 -0
  74. package/dist/features/finalize/domain/ValidateFinalizeGraph.d.ts +9 -0
  75. package/dist/features/finalize/domain/ValidateFinalizeGraph.js +86 -0
  76. package/dist/features/finalize/ports/inbound/FinalizeCommand.d.ts +7 -0
  77. package/dist/features/finalize/ports/inbound/FinalizeCommand.js +1 -0
  78. package/dist/features/finalize/ports/outbound/FinalizeClock.d.ts +3 -0
  79. package/dist/features/finalize/ports/outbound/FinalizeClock.js +1 -0
  80. package/dist/features/finalize/ports/outbound/FinalizeConfigPort.d.ts +4 -0
  81. package/dist/features/finalize/ports/outbound/FinalizeConfigPort.js +1 -0
  82. package/dist/features/finalize/ports/outbound/FinalizeFileSystem.d.ts +14 -0
  83. package/dist/features/finalize/ports/outbound/FinalizeFileSystem.js +1 -0
  84. package/dist/features/finalize/ports/outbound/PlanRepo.d.ts +21 -0
  85. package/dist/features/finalize/ports/outbound/PlanRepo.js +1 -0
  86. package/dist/features/install/adapters/inbound/CliInstallHandler.d.ts +8 -0
  87. package/dist/features/install/adapters/inbound/CliInstallHandler.js +54 -0
  88. package/dist/features/install/adapters/outbound/NodeInstallSource.d.ts +7 -0
  89. package/dist/features/install/adapters/outbound/NodeInstallSource.js +24 -0
  90. package/dist/features/install/adapters/outbound/NodeInstallTargetFs.d.ts +7 -0
  91. package/dist/features/install/adapters/outbound/NodeInstallTargetFs.js +30 -0
  92. package/dist/features/install/application/InstallRules.d.ts +10 -0
  93. package/dist/features/install/application/InstallRules.js +73 -0
  94. package/dist/features/install/domain/InstallPlan.d.ts +27 -0
  95. package/dist/features/install/domain/InstallPlan.js +168 -0
  96. package/dist/features/install/domain/InstallResult.d.ts +23 -0
  97. package/dist/features/install/domain/InstallResult.js +1 -0
  98. package/dist/features/install/domain/InstallTarget.d.ts +6 -0
  99. package/dist/features/install/domain/InstallTarget.js +7 -0
  100. package/dist/features/install/domain/ManagedBlock.d.ts +3 -0
  101. package/dist/features/install/domain/ManagedBlock.js +20 -0
  102. package/dist/features/install/domain/RuleManifest.d.ts +17 -0
  103. package/dist/features/install/domain/RuleManifest.js +69 -0
  104. package/dist/features/install/domain/SettingsMerge.d.ts +5 -0
  105. package/dist/features/install/domain/SettingsMerge.js +43 -0
  106. package/dist/features/install/ports/inbound/InstallCommand.d.ts +10 -0
  107. package/dist/features/install/ports/inbound/InstallCommand.js +1 -0
  108. package/dist/features/install/ports/outbound/InstallSource.d.ts +4 -0
  109. package/dist/features/install/ports/outbound/InstallSource.js +1 -0
  110. package/dist/features/install/ports/outbound/InstallTargetFs.d.ts +6 -0
  111. package/dist/features/install/ports/outbound/InstallTargetFs.js +1 -0
  112. package/dist/features/lint/adapters/inbound/CliLintHandler.d.ts +8 -0
  113. package/dist/features/lint/adapters/inbound/CliLintHandler.js +61 -0
  114. package/dist/features/lint/adapters/outbound/NodeLintFileReader.d.ts +7 -0
  115. package/dist/features/lint/adapters/outbound/NodeLintFileReader.js +165 -0
  116. package/dist/features/lint/application/RunLint.d.ts +10 -0
  117. package/dist/features/lint/application/RunLint.js +100 -0
  118. package/dist/features/lint/domain/Diagnostic.d.ts +1 -0
  119. package/dist/features/lint/domain/Diagnostic.js +2 -0
  120. package/dist/features/lint/domain/Record.d.ts +1 -0
  121. package/dist/features/lint/domain/Record.js +5 -0
  122. package/dist/features/lint/domain/Rules.d.ts +1 -0
  123. package/dist/features/lint/domain/Rules.js +2 -0
  124. package/dist/features/lint/domain/SpecParser.d.ts +1 -0
  125. package/dist/features/lint/domain/SpecParser.js +2 -0
  126. package/dist/features/lint/ports/inbound/LintCommand.d.ts +4 -0
  127. package/dist/features/lint/ports/inbound/LintCommand.js +1 -0
  128. package/dist/features/lint/ports/outbound/LintConfigPort.d.ts +4 -0
  129. package/dist/features/lint/ports/outbound/LintConfigPort.js +1 -0
  130. package/dist/features/lint/ports/outbound/LintFileReader.d.ts +10 -0
  131. package/dist/features/lint/ports/outbound/LintFileReader.js +1 -0
  132. package/dist/features/plan/adapters/inbound/CliPlanShowHandler.d.ts +8 -0
  133. package/dist/features/plan/adapters/inbound/CliPlanShowHandler.js +73 -0
  134. package/dist/features/plan/adapters/outbound/NodePlanReader.d.ts +7 -0
  135. package/dist/features/plan/adapters/outbound/NodePlanReader.js +68 -0
  136. package/dist/features/plan/application/ShowPlan.d.ts +7 -0
  137. package/dist/features/plan/application/ShowPlan.js +4 -0
  138. package/dist/features/plan/ports/inbound/PlanShowCommand.d.ts +7 -0
  139. package/dist/features/plan/ports/inbound/PlanShowCommand.js +1 -0
  140. package/dist/features/plan/ports/outbound/PlanConfigPort.d.ts +4 -0
  141. package/dist/features/plan/ports/outbound/PlanConfigPort.js +1 -0
  142. package/dist/features/plan/ports/outbound/PlanReader.d.ts +19 -0
  143. package/dist/features/plan/ports/outbound/PlanReader.js +1 -0
  144. package/dist/features/ready/adapters/inbound/CliReadyHandler.d.ts +8 -0
  145. package/dist/features/ready/adapters/inbound/CliReadyHandler.js +79 -0
  146. package/dist/features/ready/adapters/outbound/ChildProcessReadyGit.d.ts +9 -0
  147. package/dist/features/ready/adapters/outbound/ChildProcessReadyGit.js +113 -0
  148. package/dist/features/ready/adapters/outbound/NodeReadyFileSystem.d.ts +8 -0
  149. package/dist/features/ready/adapters/outbound/NodeReadyFileSystem.js +159 -0
  150. package/dist/features/ready/application/RunReady.d.ts +16 -0
  151. package/dist/features/ready/application/RunReady.js +572 -0
  152. package/dist/features/ready/domain/AggregatedRules.d.ts +16 -0
  153. package/dist/features/ready/domain/AggregatedRules.js +42 -0
  154. package/dist/features/ready/domain/MarkerParser.d.ts +17 -0
  155. package/dist/features/ready/domain/MarkerParser.js +108 -0
  156. package/dist/features/ready/domain/PartitionResolver.d.ts +1 -0
  157. package/dist/features/ready/domain/PartitionResolver.js +5 -0
  158. package/dist/features/ready/domain/ReadyInput.d.ts +6 -0
  159. package/dist/features/ready/domain/ReadyInput.js +1 -0
  160. package/dist/features/ready/domain/ReadyViolation.d.ts +38 -0
  161. package/dist/features/ready/domain/ReadyViolation.js +19 -0
  162. package/dist/features/ready/domain/Rules.d.ts +22 -0
  163. package/dist/features/ready/domain/Rules.js +243 -0
  164. package/dist/features/ready/domain/SpecDiff.d.ts +33 -0
  165. package/dist/features/ready/domain/SpecDiff.js +321 -0
  166. package/dist/features/ready/ports/inbound/ReadyCommand.d.ts +4 -0
  167. package/dist/features/ready/ports/inbound/ReadyCommand.js +1 -0
  168. package/dist/features/ready/ports/outbound/ReadyConfigPort.d.ts +4 -0
  169. package/dist/features/ready/ports/outbound/ReadyConfigPort.js +1 -0
  170. package/dist/features/ready/ports/outbound/ReadyFileReader.d.ts +12 -0
  171. package/dist/features/ready/ports/outbound/ReadyFileReader.js +1 -0
  172. package/dist/features/ready/ports/outbound/ReadyGitPort.d.ts +10 -0
  173. package/dist/features/ready/ports/outbound/ReadyGitPort.js +5 -0
  174. package/dist/features/record/adapters/inbound/CliRecordHandler.d.ts +10 -0
  175. package/dist/features/record/adapters/inbound/CliRecordHandler.js +111 -0
  176. package/dist/features/record/adapters/outbound/NodeRecordFileSystem.d.ts +9 -0
  177. package/dist/features/record/adapters/outbound/NodeRecordFileSystem.js +152 -0
  178. package/dist/features/record/application/AddRecord.d.ts +11 -0
  179. package/dist/features/record/application/AddRecord.js +84 -0
  180. package/dist/features/record/application/GetRecord.d.ts +8 -0
  181. package/dist/features/record/application/GetRecord.js +22 -0
  182. package/dist/features/record/application/ListRecords.d.ts +9 -0
  183. package/dist/features/record/application/ListRecords.js +24 -0
  184. package/dist/features/record/application/SetRecord.d.ts +11 -0
  185. package/dist/features/record/application/SetRecord.js +68 -0
  186. package/dist/features/record/domain/RecordBody.d.ts +12 -0
  187. package/dist/features/record/domain/RecordBody.js +66 -0
  188. package/dist/features/record/domain/RecordPartition.d.ts +1 -0
  189. package/dist/features/record/domain/RecordPartition.js +7 -0
  190. package/dist/features/record/domain/RecordSlice.d.ts +7 -0
  191. package/dist/features/record/domain/RecordSlice.js +1 -0
  192. package/dist/features/record/domain/RecordSummary.d.ts +11 -0
  193. package/dist/features/record/domain/RecordSummary.js +13 -0
  194. package/dist/features/record/domain/RecordWrite.d.ts +14 -0
  195. package/dist/features/record/domain/RecordWrite.js +8 -0
  196. package/dist/features/record/ports/inbound/RecordCommand.d.ts +19 -0
  197. package/dist/features/record/ports/inbound/RecordCommand.js +1 -0
  198. package/dist/features/record/ports/outbound/RecordConfigPort.d.ts +4 -0
  199. package/dist/features/record/ports/outbound/RecordConfigPort.js +1 -0
  200. package/dist/features/record/ports/outbound/RecordFileReader.d.ts +10 -0
  201. package/dist/features/record/ports/outbound/RecordFileReader.js +1 -0
  202. package/dist/features/record/ports/outbound/RecordFileWriter.d.ts +6 -0
  203. package/dist/features/record/ports/outbound/RecordFileWriter.js +1 -0
  204. package/dist/features/refresh/adapters/inbound/CliRefreshHandler.d.ts +8 -0
  205. package/dist/features/refresh/adapters/inbound/CliRefreshHandler.js +24 -0
  206. package/dist/features/refresh/adapters/outbound/ChildProcessRefreshGit.d.ts +8 -0
  207. package/dist/features/refresh/adapters/outbound/ChildProcessRefreshGit.js +118 -0
  208. package/dist/features/refresh/adapters/outbound/NodeRefreshFileReader.d.ts +7 -0
  209. package/dist/features/refresh/adapters/outbound/NodeRefreshFileReader.js +44 -0
  210. package/dist/features/refresh/adapters/outbound/SystemRefreshClock.d.ts +4 -0
  211. package/dist/features/refresh/adapters/outbound/SystemRefreshClock.js +5 -0
  212. package/dist/features/refresh/application/BuildRefreshStubs.d.ts +25 -0
  213. package/dist/features/refresh/application/BuildRefreshStubs.js +43 -0
  214. package/dist/features/refresh/domain/DiffStubs.d.ts +24 -0
  215. package/dist/features/refresh/domain/DiffStubs.js +81 -0
  216. package/dist/features/refresh/domain/Footprint.d.ts +14 -0
  217. package/dist/features/refresh/domain/Footprint.js +45 -0
  218. package/dist/features/refresh/ports/inbound/RefreshCommand.d.ts +4 -0
  219. package/dist/features/refresh/ports/inbound/RefreshCommand.js +1 -0
  220. package/dist/features/refresh/ports/outbound/RefreshClockPort.d.ts +3 -0
  221. package/dist/features/refresh/ports/outbound/RefreshClockPort.js +1 -0
  222. package/dist/features/refresh/ports/outbound/RefreshConfigPort.d.ts +4 -0
  223. package/dist/features/refresh/ports/outbound/RefreshConfigPort.js +1 -0
  224. package/dist/features/refresh/ports/outbound/RefreshGitPort.d.ts +7 -0
  225. package/dist/features/refresh/ports/outbound/RefreshGitPort.js +1 -0
  226. package/dist/features/refresh/ports/outbound/RefreshSpecPort.d.ts +9 -0
  227. package/dist/features/refresh/ports/outbound/RefreshSpecPort.js +1 -0
  228. package/dist/features/report/adapters/inbound/CliReportHandler.d.ts +8 -0
  229. package/dist/features/report/adapters/inbound/CliReportHandler.js +35 -0
  230. package/dist/features/report/adapters/outbound/NodeReportFileSystem.d.ts +7 -0
  231. package/dist/features/report/adapters/outbound/NodeReportFileSystem.js +128 -0
  232. package/dist/features/report/application/RunReport.d.ts +19 -0
  233. package/dist/features/report/application/RunReport.js +161 -0
  234. package/dist/features/report/ports/inbound/ReportCommand.d.ts +8 -0
  235. package/dist/features/report/ports/inbound/ReportCommand.js +1 -0
  236. package/dist/features/report/ports/outbound/ReportConfigPort.d.ts +4 -0
  237. package/dist/features/report/ports/outbound/ReportConfigPort.js +1 -0
  238. package/dist/features/report/ports/outbound/ReportFileReader.d.ts +7 -0
  239. package/dist/features/report/ports/outbound/ReportFileReader.js +1 -0
  240. package/dist/features/token/adapters/inbound/CliTokenHandler.d.ts +8 -0
  241. package/dist/features/token/adapters/inbound/CliTokenHandler.js +53 -0
  242. package/dist/features/token/adapters/outbound/ChildProcessTokenGit.d.ts +8 -0
  243. package/dist/features/token/adapters/outbound/ChildProcessTokenGit.js +112 -0
  244. package/dist/features/token/adapters/outbound/NodeTokenConfigReader.d.ts +5 -0
  245. package/dist/features/token/adapters/outbound/NodeTokenConfigReader.js +41 -0
  246. package/dist/features/token/application/ComputeToken.d.ts +18 -0
  247. package/dist/features/token/application/ComputeToken.js +27 -0
  248. package/dist/features/token/ports/inbound/TokenCommand.d.ts +4 -0
  249. package/dist/features/token/ports/inbound/TokenCommand.js +1 -0
  250. package/dist/features/token/ports/outbound/TokenConfigPort.d.ts +4 -0
  251. package/dist/features/token/ports/outbound/TokenConfigPort.js +1 -0
  252. package/dist/features/token/ports/outbound/TokenGitPort.d.ts +7 -0
  253. package/dist/features/token/ports/outbound/TokenGitPort.js +1 -0
  254. package/dist/shared/domain/AgentBlocklist.d.ts +5 -0
  255. package/dist/shared/domain/AgentBlocklist.js +28 -0
  256. package/dist/shared/domain/BoundaryReachability.d.ts +5 -0
  257. package/dist/shared/domain/BoundaryReachability.js +71 -0
  258. package/dist/shared/domain/CheckOutcome.d.ts +10 -0
  259. package/dist/shared/domain/CheckOutcome.js +7 -0
  260. package/dist/shared/domain/CliOutput.d.ts +10 -0
  261. package/dist/shared/domain/CliOutput.js +29 -0
  262. package/dist/shared/domain/Config.d.ts +29 -0
  263. package/dist/shared/domain/Config.js +201 -0
  264. package/dist/shared/domain/DiagnosticRegistry.d.ts +8 -0
  265. package/dist/shared/domain/DiagnosticRegistry.js +71 -0
  266. package/dist/shared/domain/Errors.d.ts +12 -0
  267. package/dist/shared/domain/Errors.js +23 -0
  268. package/dist/shared/domain/GlobMatch.d.ts +2 -0
  269. package/dist/shared/domain/GlobMatch.js +58 -0
  270. package/dist/shared/domain/LintReport.d.ts +16 -0
  271. package/dist/shared/domain/LintReport.js +11 -0
  272. package/dist/shared/domain/LintRules.d.ts +67 -0
  273. package/dist/shared/domain/LintRules.js +956 -0
  274. package/dist/shared/domain/PartitionGrammar.d.ts +4 -0
  275. package/dist/shared/domain/PartitionGrammar.js +16 -0
  276. package/dist/shared/domain/PlanFile.d.ts +28 -0
  277. package/dist/shared/domain/PlanFile.js +112 -0
  278. package/dist/shared/domain/Scope.d.ts +1 -0
  279. package/dist/shared/domain/Scope.js +3 -0
  280. package/dist/shared/domain/SpecApprovalRewrite.d.ts +23 -0
  281. package/dist/shared/domain/SpecApprovalRewrite.js +254 -0
  282. package/dist/shared/domain/SpecBlocks.d.ts +12 -0
  283. package/dist/shared/domain/SpecBlocks.js +96 -0
  284. package/dist/shared/domain/SpecRecord.d.ts +17 -0
  285. package/dist/shared/domain/SpecRecord.js +208 -0
  286. package/dist/shared/domain/TemplateFieldMetadata.d.ts +2 -0
  287. package/dist/shared/domain/TemplateFieldMetadata.js +177 -0
  288. package/dist/shared/domain/Token.d.ts +2 -0
  289. package/dist/shared/domain/Token.js +5 -0
  290. package/dist/shared/domain/WeaselWords.d.ts +3 -0
  291. package/dist/shared/domain/WeaselWords.js +32 -0
  292. package/package.json +71 -0
  293. package/rules/enforcement_registry.md +126 -0
  294. package/rules/hooks/sdd-lint-reminder.sh +33 -0
  295. package/rules/hooks/sdd-spec-read-guard.sh +73 -0
  296. package/rules/manifest.json +15 -0
  297. package/rules/review-sdd.md +9 -0
  298. package/rules/sdd-cli-usage.md +91 -0
  299. package/rules/skills/spec-driven-development/SKILL.md +554 -0
  300. package/rules/skills/spec-driven-development/data/weasel-words.json +22 -0
  301. package/rules/spec-driven-development.md +69 -0
  302. package/rules/tdd-sdd.md +127 -0
  303. package/rules/workflow-sdd.md +56 -0
  304. package/schema/sdd.config.schema.json +104 -0
@@ -0,0 +1,572 @@
1
+ import { CliFailure } from "../../../shared/domain/Errors.js";
2
+ import { appendDiagnostic, emptyReport, } from "../../../shared/domain/LintReport.js";
3
+ import { applicabilityRequiredRule, approvalRecordRules, assumptionDowngradeApprovalRule, baselineVersionRequiredRule, boundaryConcurrencyModelRule, boundaryPolicyRefRule, dataScopeRequiredRule, debtBudgetFormRule, deprecatedFieldsRequiredRule, fieldTypeRules, generatedArtifactSurfaceRefRule, lifecycleStatusRules, migrationCrossPartitionRule, migrationEnforcementStageRule, partitionDefaultPolicySetRule, REQUIRED_PARTITION_SECTIONS, sectionViolations, testObligationRules, weaselFindings, } from "../../../shared/domain/LintRules.js";
4
+ import { reachableBoundaryIds } from "../../../shared/domain/BoundaryReachability.js";
5
+ import { lintRecordsFromMarkdown, } from "../../../shared/domain/SpecRecord.js";
6
+ import { actualBump, bumpAtLeast, classifyDiff, generatedArtifactStructuralDiffs, requiredSurfaceBumps, } from "../domain/SpecDiff.js";
7
+ import { TOKEN_MECHANISM, token as computeToken, } from "../../../shared/domain/Token.js";
8
+ import { aggregatedCheckViolations, aggregatedLintViolations, } from "../domain/AggregatedRules.js";
9
+ import { parseMarkers, parseNearMisses, } from "../domain/MarkerParser.js";
10
+ import { ruleOrphanCovers, ruleRemovedCompatActionMismatch, ruleRemovedNoCompatTest, ruleSurfaceUnapprovedRef, ruleUnapproved, ruleUncovered, ruleUnknownPartitionCovers, } from "../domain/Rules.js";
11
+ import { envelopeFromError, } from "../domain/ReadyViolation.js";
12
+ export async function runReady(cwd, input, ports) {
13
+ let config;
14
+ try {
15
+ config = await ports.config.config(cwd);
16
+ }
17
+ catch (error) {
18
+ return envelopeFromError(toReadyError(error, "config_invalid"));
19
+ }
20
+ const filterName = input.partitionFilter;
21
+ if (filterName !== undefined &&
22
+ !config.partitions.some((p) => p.name === filterName)) {
23
+ return envelopeFromError({
24
+ kind: "config_invalid",
25
+ message: `unknown partition: ${filterName} (configured: ${config.partitions.map((p) => p.name).join(", ") || "<none>"})`,
26
+ });
27
+ }
28
+ const partitions = config.partitions;
29
+ const evaluatedPartitions = filterName === undefined
30
+ ? partitions
31
+ : partitions.filter((p) => p.name === filterName);
32
+ /* 1. Parse spec files for every configured partition. */
33
+ const recordsResult = await parseAllPartitionRecords(cwd, partitions, ports);
34
+ if (recordsResult.kind === "error") {
35
+ return envelopeFromError(recordsResult.error);
36
+ }
37
+ const recordsByPartition = recordsResult.recordsByPartition;
38
+ /* 2. Scan test files for every configured partition. */
39
+ const markersResult = await scanAllPartitionMarkers(cwd, partitions, ports);
40
+ if (markersResult.kind === "error") {
41
+ return envelopeFromError(markersResult.error);
42
+ }
43
+ const { markersByPartition, advisories } = markersResult;
44
+ /* 3 + 4. Per-partition and marker-level rule evaluation. */
45
+ const violations = [
46
+ ...perPartitionViolations(evaluatedPartitions, recordsByPartition, markersByPartition),
47
+ ...markerLevelViolations(partitions, recordsByPartition, markersByPartition),
48
+ ];
49
+ /* 5. Aggregated lint over the union of evaluated partitions' spec files. */
50
+ const lintSpecPaths = uniqueSpecPaths(evaluatedPartitions, config);
51
+ const lintReport = await aggregatedLintReport(cwd, ports.files, lintSpecPaths, config.lint.approverBlocklist);
52
+ violations.push(...aggregatedLintViolations(lintReport.diagnostics));
53
+ /* 6. Aggregated check (skipped silently when not in a git repository). */
54
+ const checkOutcome = await aggregatedCheckOutcome(cwd, config, recordsByPartition, ports.git);
55
+ if (checkOutcome.kind === "error") {
56
+ return envelopeFromError(checkOutcome.error);
57
+ }
58
+ violations.push(...aggregatedCheckViolations(checkOutcome.outcome));
59
+ /* 7. Semver cascade + debt-budget monotonicity (only with --against). */
60
+ violations.push(...(await againstViolations(cwd, input, lintSpecPaths, ports)));
61
+ return { ok: violations.length === 0, error: null, violations, advisories };
62
+ }
63
+ /* ENF-004A / ENF-020 — semver cascade and debt-budget monotonicity run only
64
+ * with --against; without it, ready behaves exactly as in v0.3.x. */
65
+ async function againstViolations(cwd, input, lintSpecPaths, ports) {
66
+ if (input.against === undefined || ports.git === undefined) {
67
+ return [];
68
+ }
69
+ return [
70
+ ...(await semverCascadeViolations(cwd, input.against, lintSpecPaths, ports.files, ports.git)),
71
+ ...(await debtBudgetMonotonicityViolations(cwd, input.against, lintSpecPaths, ports.files, ports.git)),
72
+ ];
73
+ }
74
+ /* Phase 1 parses spec files for every configured partition (orphan_covers needs
75
+ * global record knowledge even when --partition filters evaluation). */
76
+ async function parseAllPartitionRecords(cwd, partitions, ports) {
77
+ const recordsByPartition = new Map();
78
+ for (const p of partitions) {
79
+ let entries;
80
+ try {
81
+ entries = await ports.files.resolveSpecFiles(cwd, p.specPaths);
82
+ }
83
+ catch (error) {
84
+ return { kind: "error", error: toReadyError(error, "config_invalid") };
85
+ }
86
+ const records = [];
87
+ for (const entry of entries) {
88
+ try {
89
+ records.push(...lintRecordsFromMarkdown(entry.path, entry.content));
90
+ }
91
+ catch (error) {
92
+ return {
93
+ kind: "error",
94
+ error: {
95
+ kind: "spec_parse_failed",
96
+ message: error instanceof Error ? error.message : String(error),
97
+ file: entry.path,
98
+ },
99
+ };
100
+ }
101
+ }
102
+ recordsByPartition.set(p.name, records);
103
+ }
104
+ return { kind: "ok", recordsByPartition };
105
+ }
106
+ /* Phase 2 scans test files per partition. A unique file is read once per
107
+ * partition that lists it; markers carry the file path. */
108
+ async function scanAllPartitionMarkers(cwd, partitions, ports) {
109
+ const markersByPartition = new Map();
110
+ const nearMissByKey = new Map();
111
+ for (const p of partitions) {
112
+ if (p.testPaths.length === 0) {
113
+ markersByPartition.set(p.name, []);
114
+ continue;
115
+ }
116
+ let entries;
117
+ try {
118
+ entries = await ports.files.resolveTestFiles(cwd, p.testPaths);
119
+ }
120
+ catch (error) {
121
+ return {
122
+ kind: "error",
123
+ error: {
124
+ kind: "unreadable_test_paths",
125
+ message: error instanceof Error ? error.message : String(error),
126
+ },
127
+ };
128
+ }
129
+ const markers = [];
130
+ for (const entry of entries) {
131
+ markers.push(...parseMarkers(entry.content, entry.path));
132
+ for (const nm of parseNearMisses(entry.content, entry.path)) {
133
+ nearMissByKey.set(`${nm.file}:${nm.line}:${nm.text}`, {
134
+ kind: "covers_near_miss",
135
+ file: nm.file,
136
+ line: nm.line,
137
+ text: nm.text,
138
+ remediation: `\`@covers ${nm.text}\` looks like a marker but fails the partition grammar (lowercase segments only); fix the prefix or remove the stray @covers text`,
139
+ });
140
+ }
141
+ }
142
+ markersByPartition.set(p.name, markers);
143
+ }
144
+ return {
145
+ kind: "ok",
146
+ markersByPartition,
147
+ advisories: [...nearMissByKey.values()],
148
+ };
149
+ }
150
+ function perPartitionViolations(evaluatedPartitions, recordsByPartition, markersByPartition) {
151
+ const violations = [];
152
+ for (const partition of evaluatedPartitions) {
153
+ const records = recordsByPartition.get(partition.name) ?? [];
154
+ const recordsById = new Map();
155
+ for (const r of records) {
156
+ recordsById.set(r.id, r);
157
+ }
158
+ const credited = new Map();
159
+ const ownMarkers = markersByPartition.get(partition.name) ?? [];
160
+ for (const m of ownMarkers) {
161
+ if (m.partition !== partition.name) {
162
+ continue;
163
+ }
164
+ const id = `${m.partition}:${m.id}`;
165
+ const list = credited.get(id) ?? [];
166
+ list.push(m);
167
+ credited.set(id, list);
168
+ }
169
+ const view = {
170
+ partition,
171
+ records,
172
+ recordsById,
173
+ creditedMarkersById: credited,
174
+ };
175
+ violations.push(...ruleUnapproved(view), ...ruleUncovered(view), ...ruleRemovedNoCompatTest(view), ...ruleRemovedCompatActionMismatch(view), ...ruleSurfaceUnapprovedRef(view));
176
+ }
177
+ return violations;
178
+ }
179
+ /* Phase 4 dedups markers by (file, line, partition, id) — a file listed in
180
+ * multiple partitions' test_paths must not be double-counted — then surfaces
181
+ * unknown_partition_covers and orphan_covers globally regardless of --partition. */
182
+ function markerLevelViolations(partitions, recordsByPartition, markersByPartition) {
183
+ const partitionsByName = new Map(partitions.map((p) => [p.name, p]));
184
+ const seen = new Set();
185
+ const allMarkers = [];
186
+ for (const ms of markersByPartition.values()) {
187
+ for (const m of ms) {
188
+ const key = `${m.file}:${m.line}:${m.partition}:${m.id}`;
189
+ if (seen.has(key)) {
190
+ continue;
191
+ }
192
+ seen.add(key);
193
+ allMarkers.push(m);
194
+ }
195
+ }
196
+ const scanned = [];
197
+ for (const m of allMarkers) {
198
+ const fullId = `${m.partition}:${m.id}`;
199
+ const partition = partitionsByName.get(m.partition);
200
+ const isPartitionConfigured = partition !== undefined;
201
+ const records = isPartitionConfigured
202
+ ? (recordsByPartition.get(m.partition) ?? [])
203
+ : [];
204
+ const hasMatchingRecord = records.some((r) => r.id === fullId);
205
+ scanned.push({ marker: m, isPartitionConfigured, hasMatchingRecord });
206
+ }
207
+ return [...ruleUnknownPartitionCovers(scanned), ...ruleOrphanCovers(scanned)];
208
+ }
209
+ async function debtBudgetMonotonicityViolations(cwd, ref, specPaths, files, git) {
210
+ if (!(await git.isGitRepo(cwd))) {
211
+ return [];
212
+ }
213
+ const repoRoot = await git.repoRoot(cwd);
214
+ let curr;
215
+ try {
216
+ curr = await files.resolveSpecFiles(cwd, specPaths);
217
+ }
218
+ catch {
219
+ return [];
220
+ }
221
+ const currRecords = [];
222
+ for (const e of curr) {
223
+ currRecords.push(...lintRecordsFromMarkdown(e.path, e.content));
224
+ }
225
+ const prevRecordsByFile = new Map();
226
+ for (const e of curr) {
227
+ const prevContent = await git.readAtRef(repoRoot, ref, e.path);
228
+ if (prevContent === null) {
229
+ continue;
230
+ }
231
+ prevRecordsByFile.set(e.path, lintRecordsFromMarkdown(e.path, prevContent));
232
+ }
233
+ const prevRecords = [];
234
+ for (const arr of prevRecordsByFile.values()) {
235
+ prevRecords.push(...arr);
236
+ }
237
+ const out = [];
238
+ for (const part of currRecords.filter((r) => r.template === "Partition")) {
239
+ const currBudget = readDebtBudget(part);
240
+ if (currBudget === null) {
241
+ continue;
242
+ }
243
+ const prevPart = prevRecords.find((r) => r.id === part.id);
244
+ if (prevPart === undefined) {
245
+ continue;
246
+ }
247
+ const prevBudget = readDebtBudget(prevPart);
248
+ if (prevBudget === null) {
249
+ continue;
250
+ }
251
+ const violationKind = compareBudgets(currBudget, prevBudget);
252
+ if (violationKind === null) {
253
+ continue;
254
+ }
255
+ out.push({
256
+ kind: "debt_budget_increased",
257
+ id: part.id,
258
+ file: part.file,
259
+ line: part.line,
260
+ expected: violationKind.expected,
261
+ actual: violationKind.actual,
262
+ remediation: `Partition ${part.id} unmodeled_budget.current=${currBudget.current} (was ${prevBudget.current} at ${ref}, trend=${currBudget.trend}); ${violationKind.remediation}`,
263
+ });
264
+ }
265
+ return out;
266
+ }
267
+ function isRecord(value) {
268
+ return typeof value === "object" && value !== null && !Array.isArray(value);
269
+ }
270
+ function readDebtBudget(rec) {
271
+ const b = rec.parsed.unmodeled_budget;
272
+ if (!isRecord(b)) {
273
+ return null;
274
+ }
275
+ const current = b.current;
276
+ const trend = b.trend;
277
+ if (typeof current !== "number") {
278
+ return null;
279
+ }
280
+ if (trend !== "monotonic_non_increasing" &&
281
+ trend !== "monotonic_decreasing") {
282
+ return null;
283
+ }
284
+ return { current, trend };
285
+ }
286
+ function compareBudgets(curr, prev) {
287
+ if (curr.trend === "monotonic_non_increasing") {
288
+ if (curr.current > prev.current) {
289
+ return {
290
+ expected: `<= ${prev.current}`,
291
+ actual: String(curr.current),
292
+ remediation: "trend=monotonic_non_increasing requires current <= previous current; reduce debt or weaken the trend",
293
+ };
294
+ }
295
+ return null;
296
+ }
297
+ /* monotonic_decreasing */
298
+ if (curr.current >= prev.current) {
299
+ return {
300
+ expected: `< ${prev.current}`,
301
+ actual: String(curr.current),
302
+ remediation: "trend=monotonic_decreasing requires current < previous current; reduce debt this PR",
303
+ };
304
+ }
305
+ return null;
306
+ }
307
+ async function semverCascadeViolations(cwd, ref, specPaths, files, git) {
308
+ if (!(await git.isGitRepo(cwd))) {
309
+ return [];
310
+ }
311
+ const repoRoot = await git.repoRoot(cwd);
312
+ let curr;
313
+ try {
314
+ curr = await files.resolveSpecFiles(cwd, specPaths);
315
+ }
316
+ catch {
317
+ return [];
318
+ }
319
+ const currRecords = [];
320
+ for (const e of curr) {
321
+ currRecords.push(...lintRecordsFromMarkdown(e.path, e.content));
322
+ }
323
+ const prevRecords = [];
324
+ for (const e of curr) {
325
+ const prevContent = await git.readAtRef(repoRoot, ref, e.path);
326
+ if (prevContent === null) {
327
+ continue;
328
+ } /* file new at HEAD — every record reads as additive content_change */
329
+ prevRecords.push(...lintRecordsFromMarkdown(e.path, prevContent));
330
+ }
331
+ const diffs = classifyDiff(prevRecords, currRecords);
332
+ const bumps = requiredSurfaceBumps(prevRecords, currRecords, diffs);
333
+ return [
334
+ ...surfaceCascadeViolations(bumps, currRecords),
335
+ ...gaStructuralViolations(prevRecords, currRecords, diffs, bumps),
336
+ ];
337
+ }
338
+ function surfaceCascadeViolations(bumps, currRecords) {
339
+ const out = [];
340
+ for (const b of bumps) {
341
+ const actual = actualBump(b.prevDeclaredVersion, b.declaredVersion);
342
+ if (b.required === "patch") {
343
+ continue;
344
+ }
345
+ if (bumpAtLeast(actual, b.required)) {
346
+ continue;
347
+ }
348
+ const surfaceRec = currRecords.find((r) => r.id === b.surfaceId);
349
+ out.push({
350
+ kind: "surface_semver_cascade",
351
+ id: b.surfaceId,
352
+ file: surfaceRec?.file,
353
+ line: surfaceRec?.line,
354
+ expected: b.required,
355
+ actual: actual ?? "patch",
356
+ remediation: `Surface ${b.surfaceId} reachable change requires a ${b.required} bump (declared ${b.prevDeclaredVersion ?? "?"} → ${b.declaredVersion ?? "?"}). Driven by: ${b.drivenBy
357
+ .map((d) => `${d.id} (${d.classification})`)
358
+ .slice(0, 4)
359
+ .join(", ")}${b.drivenBy.length > 4 ? "..." : ""}`,
360
+ });
361
+ }
362
+ return out;
363
+ }
364
+ /*
365
+ * ENF-019 — emit a dedicated GA-naming violation (always, for traceability)
366
+ * when a published GA structural diff lacks a major Surface bump.
367
+ */
368
+ function gaStructuralViolations(prevRecords, currRecords, diffs, bumps) {
369
+ const out = [];
370
+ const gaDiffs = generatedArtifactStructuralDiffs(prevRecords, currRecords, diffs);
371
+ for (const ga of gaDiffs) {
372
+ const surfaceRec = currRecords.find((r) => r.id === ga.surfaceId);
373
+ const bump = bumps.find((b) => b.surfaceId === ga.surfaceId);
374
+ const actual = bump !== undefined
375
+ ? actualBump(bump.prevDeclaredVersion, bump.declaredVersion)
376
+ : null;
377
+ if (bumpAtLeast(actual, "major")) {
378
+ continue;
379
+ }
380
+ out.push({
381
+ kind: "generated_artifact_structural_diff_unbumped",
382
+ id: ga.surfaceId,
383
+ file: surfaceRec?.file,
384
+ line: surfaceRec?.line,
385
+ expected: "major",
386
+ actual: actual ?? "patch",
387
+ remediation: `Surface ${ga.surfaceId} reaches GeneratedArtifact ${ga.generatedArtifactId} (published_surface=yes) with ${ga.classification}; structural diff in a published GA requires a major bump on the parent Surface (ENF-019).`,
388
+ });
389
+ }
390
+ return out;
391
+ }
392
+ function uniqueSpecPaths(evaluated, config) {
393
+ const all = evaluated.length > 0
394
+ ? evaluated.flatMap((p) => p.specPaths)
395
+ : config.lint.specFiles;
396
+ return [...new Set(all)];
397
+ }
398
+ async function aggregatedLintReport(cwd, files, specPaths, approverBlocklist) {
399
+ let report = emptyReport();
400
+ if (specPaths.length === 0) {
401
+ return report;
402
+ }
403
+ let entries;
404
+ try {
405
+ entries = await files.resolveSpecFiles(cwd, specPaths);
406
+ }
407
+ catch {
408
+ return report;
409
+ }
410
+ for (const entry of entries) {
411
+ report = lintFileInto(report, entry, approverBlocklist);
412
+ }
413
+ return report;
414
+ }
415
+ function lintFileInto(report, entry, approverBlocklist) {
416
+ let next = report;
417
+ if (looksLikePartitionFile(entry.content)) {
418
+ for (const v of sectionViolations(entry.content)) {
419
+ next = appendDiagnostic(next, {
420
+ severity: "error",
421
+ rule: v.rule,
422
+ file: entry.path,
423
+ message: v.message,
424
+ });
425
+ }
426
+ }
427
+ const records = lintRecordsFromMarkdown(entry.path, entry.content);
428
+ const boundaryIds = reachableBoundaryIds(records);
429
+ for (const w of weaselFindings(entry.content, records)) {
430
+ const where = w.field !== undefined
431
+ ? `normative field ${w.field}`
432
+ : `normative section "${w.section}"`;
433
+ next = appendDiagnostic(next, {
434
+ severity: "error",
435
+ rule: "sdd:weasel-word",
436
+ file: entry.path,
437
+ line: w.line,
438
+ message: `Banned phrase "${w.word}" in ${where} (SDD §5.1).`,
439
+ });
440
+ }
441
+ for (const rec of records) {
442
+ for (const d of [
443
+ ...lifecycleStatusRules(rec),
444
+ ...approvalRecordRules(rec),
445
+ ...testObligationRules(rec),
446
+ ...fieldTypeRules(rec),
447
+ ...baselineVersionRequiredRule(rec),
448
+ ...deprecatedFieldsRequiredRule(rec),
449
+ ...assumptionDowngradeApprovalRule(rec, approverBlocklist),
450
+ ...partitionDefaultPolicySetRule(rec),
451
+ ...generatedArtifactSurfaceRefRule(rec),
452
+ ...boundaryPolicyRefRule(rec, boundaryIds),
453
+ ...boundaryConcurrencyModelRule(rec, boundaryIds),
454
+ ...applicabilityRequiredRule(rec, boundaryIds),
455
+ ...dataScopeRequiredRule(rec, boundaryIds),
456
+ ...migrationEnforcementStageRule(rec, records),
457
+ ...migrationCrossPartitionRule(rec),
458
+ ...debtBudgetFormRule(rec),
459
+ ]) {
460
+ next = appendDiagnostic(next, d);
461
+ }
462
+ }
463
+ return next;
464
+ }
465
+ function looksLikePartitionFile(markdown) {
466
+ const firstNumberedHeading = REQUIRED_PARTITION_SECTIONS[0];
467
+ const re = new RegExp(`^##\\s+${firstNumberedHeading.replace(/\./g, "\\.").replace(/ /g, "\\s+")}`, "m");
468
+ return re.test(markdown);
469
+ }
470
+ async function aggregatedCheckOutcome(cwd, config, recordsByPartition, git) {
471
+ const inRepo = await git.isGitRepo(cwd).catch(() => false);
472
+ if (!inRepo) {
473
+ return { kind: "outcome", outcome: { kind: "skipped" } };
474
+ }
475
+ const baseline = findBaseline(config.baselineId, recordsByPartition);
476
+ if (baseline === null) {
477
+ /*
478
+ * Baseline-id pattern is enforced by config; absence of the block in spec
479
+ * is itself a baseline-stale-equivalent. We surface it via aggregated
480
+ * check rather than treating it as evaluate-failure, to match `sdd lint`'s
481
+ * tolerance of unparseable BL blocks (it just doesn't lint them).
482
+ */
483
+ return { kind: "outcome", outcome: { kind: "skipped" } };
484
+ }
485
+ if (baseline.freshnessToken === "TODO" ||
486
+ baseline.freshnessToken.length === 0) {
487
+ return { kind: "outcome", outcome: { kind: "skipped" } };
488
+ }
489
+ let repoRoot;
490
+ try {
491
+ repoRoot = await git.repoRoot(cwd);
492
+ }
493
+ catch {
494
+ return { kind: "outcome", outcome: { kind: "skipped" } };
495
+ }
496
+ let dirty;
497
+ try {
498
+ dirty = await git.dirtyPaths(repoRoot, config.discoveryScope);
499
+ }
500
+ catch (error) {
501
+ return {
502
+ kind: "error",
503
+ error: {
504
+ kind: "internal",
505
+ message: error instanceof Error ? error.message : String(error),
506
+ },
507
+ };
508
+ }
509
+ if (dirty.length > 0) {
510
+ return { kind: "outcome", outcome: { kind: "dirty", dirtyPaths: dirty } };
511
+ }
512
+ let bytes;
513
+ try {
514
+ bytes = await git.treeBytes(repoRoot, config.discoveryScope);
515
+ }
516
+ catch (error) {
517
+ return {
518
+ kind: "error",
519
+ error: {
520
+ kind: "internal",
521
+ message: error instanceof Error ? error.message : String(error),
522
+ },
523
+ };
524
+ }
525
+ const recomputed = computeToken(bytes);
526
+ if (recomputed === baseline.freshnessToken) {
527
+ return { kind: "outcome", outcome: { kind: "match" } };
528
+ }
529
+ return {
530
+ kind: "outcome",
531
+ outcome: {
532
+ kind: "stale",
533
+ recordedToken: baseline.freshnessToken,
534
+ recomputedToken: recomputed,
535
+ },
536
+ };
537
+ }
538
+ function findBaseline(baselineId, recordsByPartition) {
539
+ for (const records of recordsByPartition.values()) {
540
+ for (const rec of records) {
541
+ if (rec.id !== baselineId) {
542
+ continue;
543
+ }
544
+ const ft = rec.parsed.freshness_token;
545
+ if (typeof ft === "string") {
546
+ return { freshnessToken: ft };
547
+ }
548
+ }
549
+ }
550
+ return null;
551
+ }
552
+ function toReadyError(error, fallback) {
553
+ if (error instanceof CliFailure) {
554
+ if (error.reason === "config-missing" ||
555
+ error.reason === "config-invalid") {
556
+ return {
557
+ kind: "config_invalid",
558
+ message: error.message,
559
+ file: error.path,
560
+ };
561
+ }
562
+ }
563
+ return {
564
+ kind: fallback,
565
+ message: error instanceof Error ? error.message : String(error),
566
+ };
567
+ }
568
+ /* Side-effect-free public helper for tests/unit symmetry. */
569
+ export function appendLintDiagnostic(report, d) {
570
+ return appendDiagnostic(report, d);
571
+ }
572
+ export { TOKEN_MECHANISM };
@@ -0,0 +1,16 @@
1
+ import type { Diagnostic } from "../../../shared/domain/LintReport.js";
2
+ import type { ReadyViolation } from "./ReadyViolation.js";
3
+ export declare function aggregatedLintViolations(diagnostics: readonly Diagnostic[]): ReadyViolation[];
4
+ export type AggregatedCheckOutcome = {
5
+ kind: "match";
6
+ } | {
7
+ kind: "dirty";
8
+ dirtyPaths: readonly string[];
9
+ } | {
10
+ kind: "stale";
11
+ recordedToken: string;
12
+ recomputedToken: string;
13
+ } | {
14
+ kind: "skipped";
15
+ };
16
+ export declare function aggregatedCheckViolations(outcome: AggregatedCheckOutcome): ReadyViolation[];
@@ -0,0 +1,42 @@
1
+ /*
2
+ * Adapt a LintReport's error-severity diagnostics into ReadyViolations of kind
3
+ * `aggregated_lint`. Per OQ-015 (default a), warn-severity diagnostics are
4
+ * dropped; only errors block the merge gate.
5
+ */
6
+ export function aggregatedLintViolations(diagnostics) {
7
+ const out = [];
8
+ for (const d of diagnostics) {
9
+ if (d.severity !== "error") {
10
+ continue;
11
+ }
12
+ out.push({
13
+ kind: "aggregated_lint",
14
+ file: d.file,
15
+ line: d.line,
16
+ remediation: d.message,
17
+ source: d.rule,
18
+ });
19
+ }
20
+ return out;
21
+ }
22
+ export function aggregatedCheckViolations(outcome) {
23
+ if (outcome.kind === "match" || outcome.kind === "skipped") {
24
+ return [];
25
+ }
26
+ if (outcome.kind === "dirty") {
27
+ const head = outcome.dirtyPaths[0];
28
+ return [
29
+ {
30
+ kind: "aggregated_check",
31
+ remediation: `baseline-dirty: ${outcome.dirtyPaths.length} uncommitted path(s) inside discovery_scope`,
32
+ file: head,
33
+ },
34
+ ];
35
+ }
36
+ return [
37
+ {
38
+ kind: "aggregated_check",
39
+ remediation: `baseline-stale: recorded freshness_token does not match recomputed (recorded=${outcome.recordedToken.slice(0, 12)}…, recomputed=${outcome.recomputedToken.slice(0, 12)}…)`,
40
+ },
41
+ ];
42
+ }
@@ -0,0 +1,17 @@
1
+ export interface Marker {
2
+ partition: string;
3
+ id: string;
4
+ tail: Record<string, string>;
5
+ file: string;
6
+ line: number;
7
+ }
8
+ export declare const ALLOWED_MARKER_KEYS: ReadonlySet<string>;
9
+ export interface NearMissMarker {
10
+ text: string;
11
+ file: string;
12
+ line: number;
13
+ }
14
+ /** `@covers`-shaped tokens that look like a marker attempt (valid id tail) but
15
+ * fail the strict partition grammar. Strictly-valid markers are excluded. */
16
+ export declare function parseNearMisses(text: string, file: string): NearMissMarker[];
17
+ export declare function parseMarkers(text: string, file: string): Marker[];