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,161 @@
1
+ /*
2
+ * ENF-007A..ENF-007E: `sdd report --pr-summary` emits a 5-section markdown
3
+ * block (test obligations, internal decisions, ASSUMPTIONs, Open-Q residuals,
4
+ * debt-budget delta), read-only on the working tree. See spec record.
5
+ */
6
+ import { lintRecordsFromMarkdown, } from "../../../shared/domain/SpecRecord.js";
7
+ export async function runReport(cwd, input, ports) {
8
+ const config = await ports.config.config(cwd);
9
+ const entries = await ports.files.resolveSpecFiles(cwd, config.lint.specFiles);
10
+ const records = [];
11
+ for (const e of entries) {
12
+ records.push(...lintRecordsFromMarkdown(e.path, e.content));
13
+ }
14
+ const lines = [];
15
+ lines.push("## SDD PR Report");
16
+ lines.push("");
17
+ lines.push(...closedObligationsSection(records));
18
+ lines.push(...internalDecisionsSection());
19
+ lines.push(...assumptionsSection(records));
20
+ lines.push(...openQResidualsSection(records));
21
+ lines.push("### Debt budget delta");
22
+ lines.push("");
23
+ const debt = await debtBudgetDelta(cwd, input, records, entries, ports);
24
+ for (const l of debt) {
25
+ lines.push(l);
26
+ }
27
+ lines.push("");
28
+ return { markdown: lines.join("\n") };
29
+ }
30
+ function closedObligationsSection(records) {
31
+ const lines = [];
32
+ lines.push("### Closed Test obligations");
33
+ lines.push("");
34
+ const withObligations = records.filter((r) => r.testObligations.length > 0 || r.hasAliasedObligations);
35
+ if (withObligations.length === 0) {
36
+ lines.push("- _No records with `test_obligation` in scope._");
37
+ }
38
+ else {
39
+ lines.push("<!-- TODO(agent): cross-reference against `@covers <id>` markers under tests/** to confirm closure. -->");
40
+ for (const r of withObligations) {
41
+ lines.push(`- [${r.id}] (${r.template}) — declares test_obligation`);
42
+ }
43
+ }
44
+ lines.push("");
45
+ return lines;
46
+ }
47
+ function internalDecisionsSection() {
48
+ return [
49
+ "### Internal decisions (candidates for new Constraint/Policy/ASSUMPTION)",
50
+ "",
51
+ "<!-- TODO(agent): list internal decisions taken in this PR — names, structures, libraries — that are not yet codified. Examples:",
52
+ " - chose <X> over <Y>, rationale: ...",
53
+ " - introduced module <foo>, no Constraint binding yet",
54
+ " Each becomes a candidate for a new Constraint / Policy / ASSUMPTION in a follow-up PR. -->",
55
+ "",
56
+ ];
57
+ }
58
+ function assumptionsSection(records) {
59
+ const lines = [];
60
+ lines.push("### ASSUMPTIONs");
61
+ lines.push("");
62
+ const assumptions = records.filter((r) => r.template === "ASSUMPTION");
63
+ if (assumptions.length === 0) {
64
+ lines.push("- _No ASSUMPTION records in scope._");
65
+ }
66
+ else {
67
+ for (const r of assumptions) {
68
+ const reviewBy = typeof r.parsed.review_by === "string"
69
+ ? r.parsed.review_by
70
+ : "(no review_by)";
71
+ const blocking = typeof r.parsed.blocking === "string" ? r.parsed.blocking : "?";
72
+ lines.push(`- [${r.id}] blocking=${blocking}, review_by=${reviewBy}`);
73
+ }
74
+ }
75
+ lines.push("");
76
+ return lines;
77
+ }
78
+ function openQResidualsSection(records) {
79
+ const lines = [];
80
+ lines.push("### Open-Q residuals");
81
+ lines.push("");
82
+ const openQs = records.filter((r) => r.template === "Open-Q" &&
83
+ (r.lifecycleStatus === "proposed" || r.lifecycleStatus === "draft"));
84
+ if (openQs.length === 0) {
85
+ lines.push("- _No open Open-Q records in scope._");
86
+ }
87
+ else {
88
+ for (const r of openQs) {
89
+ const blocking = typeof r.parsed.blocking === "string" ? r.parsed.blocking : "?";
90
+ lines.push(`- [${r.id}] blocking=${blocking}`);
91
+ }
92
+ }
93
+ lines.push("");
94
+ return lines;
95
+ }
96
+ async function debtBudgetDelta(cwd, input, records, entries, ports) {
97
+ const partitions = records.filter((r) => r.template === "Partition");
98
+ if (partitions.length === 0) {
99
+ return ["- _No Partition records in scope (debt budget not tracked)._"];
100
+ }
101
+ const out = [];
102
+ const prevByPath = new Map();
103
+ if (input.against !== undefined &&
104
+ ports.readAtRef !== undefined &&
105
+ ports.repoRoot !== undefined) {
106
+ let root;
107
+ try {
108
+ root = await ports.repoRoot(cwd);
109
+ }
110
+ catch {
111
+ out.push(`- _Unable to resolve repo root for --against=${input.against}._`);
112
+ return out;
113
+ }
114
+ for (const e of entries) {
115
+ const prev = await ports.readAtRef(root, input.against, e.path);
116
+ if (prev !== null) {
117
+ prevByPath.set(e.path, lintRecordsFromMarkdown(e.path, prev));
118
+ }
119
+ }
120
+ }
121
+ for (const p of partitions) {
122
+ const budget = p.parsed.unmodeled_budget;
123
+ if (!isRecord(budget)) {
124
+ out.push(`- [${p.id}] _no \`unmodeled_budget\` declared (P3.1)._`);
125
+ continue;
126
+ }
127
+ const current = budget.current;
128
+ const trend = budget.trend;
129
+ const baselineAt = budget.baseline_at;
130
+ if (typeof current !== "number") {
131
+ out.push(`- [${p.id}] _\`unmodeled_budget.current\` is not a number._`);
132
+ continue;
133
+ }
134
+ let delta = "";
135
+ if (input.against !== undefined) {
136
+ const prevRecs = prevByPath.get(p.file);
137
+ const prevPart = prevRecs?.find((r) => r.id === p.id);
138
+ const prevBudget = prevPart !== undefined && isRecord(prevPart.parsed.unmodeled_budget)
139
+ ? prevPart.parsed.unmodeled_budget
140
+ : null;
141
+ const prevCurrent = prevBudget !== null && typeof prevBudget.current === "number"
142
+ ? prevBudget.current
143
+ : null;
144
+ if (prevCurrent !== null) {
145
+ const diff = current - prevCurrent;
146
+ const sign = diff > 0 ? "+" : "";
147
+ delta = ` (was ${prevCurrent} at ${input.against}, ${sign}${diff})`;
148
+ }
149
+ else {
150
+ delta = ` (no comparable budget at ${input.against})`;
151
+ }
152
+ }
153
+ const baselineLabel = typeof baselineAt === "string" ? `, baseline_at ${baselineAt}` : "";
154
+ const trendLabel = typeof trend === "string" ? `, trend=${trend}` : "";
155
+ out.push(`- [${p.id}] current=${current}${delta}${baselineLabel}${trendLabel}`);
156
+ }
157
+ return out;
158
+ }
159
+ function isRecord(value) {
160
+ return typeof value === "object" && value !== null && !Array.isArray(value);
161
+ }
@@ -0,0 +1,8 @@
1
+ import type { CommandResult, OutputFormat } from "../../../../shared/domain/CliOutput.js";
2
+ export interface ReportRequest {
3
+ prSummary: boolean;
4
+ against?: string;
5
+ }
6
+ export interface ReportCommand {
7
+ execute(cwd: string, req: ReportRequest, format: Exclude<OutputFormat, "yaml">): Promise<CommandResult>;
8
+ }
@@ -0,0 +1,4 @@
1
+ import type { SddConfig } from "../../../../shared/domain/Config.js";
2
+ export interface ReportConfigPort {
3
+ config(repoRoot: string): Promise<SddConfig>;
4
+ }
@@ -0,0 +1,7 @@
1
+ export interface SpecFileEntry {
2
+ path: string;
3
+ content: string;
4
+ }
5
+ export interface ReportFileReader {
6
+ resolveSpecFiles(repoRoot: string, patterns: readonly string[]): Promise<SpecFileEntry[]>;
7
+ }
@@ -0,0 +1,8 @@
1
+ import { type CommandResult, type OutputFormat } from "../../../../shared/domain/CliOutput.js";
2
+ import { type ComputeTokenPorts } from "../../application/ComputeToken.js";
3
+ import type { TokenCommand } from "../../ports/inbound/TokenCommand.js";
4
+ export declare class CliTokenHandler implements TokenCommand {
5
+ private readonly ports;
6
+ constructor(ports: ComputeTokenPorts);
7
+ execute(cwd: string, format: Exclude<OutputFormat, "yaml">): Promise<CommandResult>;
8
+ }
@@ -0,0 +1,53 @@
1
+ import { failed, ok, } from "../../../../shared/domain/CliOutput.js";
2
+ import { CliFailure } from "../../../../shared/domain/Errors.js";
3
+ import { computeToken, } from "../../application/ComputeToken.js";
4
+ export class CliTokenHandler {
5
+ ports;
6
+ constructor(ports) {
7
+ this.ports = ports;
8
+ }
9
+ async execute(cwd, format) {
10
+ try {
11
+ const outcome = await computeToken(cwd, this.ports);
12
+ if (outcome.kind === "dirty") {
13
+ return dirtyResult(outcome.dirtyPaths, format);
14
+ }
15
+ if (format === "json") {
16
+ return ok(JSON.stringify({
17
+ format_version: 1,
18
+ ok: true,
19
+ token: outcome.token,
20
+ commit_sha: outcome.commitSha,
21
+ mechanism: outcome.mechanism,
22
+ scope: outcome.scope,
23
+ }));
24
+ }
25
+ return ok(`token ${outcome.token}\n commit_sha: ${outcome.commitSha}\n mechanism: ${outcome.mechanism}\n scope: ${outcome.scope.join(", ")}`);
26
+ }
27
+ catch (error) {
28
+ if (error instanceof CliFailure) {
29
+ return failed(error, format);
30
+ }
31
+ throw error;
32
+ }
33
+ }
34
+ }
35
+ function dirtyResult(dirtyPaths, format) {
36
+ if (format === "json") {
37
+ return {
38
+ exitCode: 1,
39
+ stdout: `${JSON.stringify({
40
+ format_version: 1,
41
+ ok: false,
42
+ reason: "baseline-dirty",
43
+ dirty_paths: dirtyPaths,
44
+ })}\n`,
45
+ stderr: "",
46
+ };
47
+ }
48
+ return {
49
+ exitCode: 1,
50
+ stdout: "",
51
+ stderr: `baseline-dirty: scope has uncommitted changes\n ${dirtyPaths.join("\n ")}\n`,
52
+ };
53
+ }
@@ -0,0 +1,8 @@
1
+ import type { TokenGitPort } from "../../ports/outbound/TokenGitPort.js";
2
+ export declare class ChildProcessTokenGit implements TokenGitPort {
3
+ repoRoot(cwd: string): Promise<string>;
4
+ headSha(repoRoot: string): Promise<string>;
5
+ treeBytes(repoRoot: string, scope: readonly string[]): Promise<Uint8Array>;
6
+ treePaths(repoRoot: string, scope: readonly string[]): Promise<string[]>;
7
+ dirtyPaths(repoRoot: string, scope: readonly string[]): Promise<string[]>;
8
+ }
@@ -0,0 +1,112 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { environmentFailure } from "../../../../shared/domain/Errors.js";
5
+ export class ChildProcessTokenGit {
6
+ async repoRoot(cwd) {
7
+ const result = await runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
8
+ if (result.code !== 0 || result.stdout.toString("utf8").trim() !== "true") {
9
+ throw environmentFailure("not-a-git-repo", "cwd is not inside a git working tree", cwd);
10
+ }
11
+ return findRepoRoot(cwd);
12
+ }
13
+ async headSha(repoRoot) {
14
+ const result = await runGit(repoRoot, ["rev-parse", "HEAD"]);
15
+ if (result.code === 0) {
16
+ return result.stdout.toString("utf8").trim();
17
+ }
18
+ throw environmentFailure("head-unborn", "HEAD does not resolve to a commit", result.stderr.toString("utf8").trim());
19
+ }
20
+ async treeBytes(repoRoot, scope) {
21
+ const result = await runGit(repoRoot, ["ls-tree", "HEAD", "--", ...scope]);
22
+ if (result.code !== 0) {
23
+ throw environmentFailure("head-unborn", "git ls-tree failed", result.stderr.toString("utf8").trim());
24
+ }
25
+ return result.stdout;
26
+ }
27
+ async treePaths(repoRoot, scope) {
28
+ const result = await runGit(repoRoot, [
29
+ "ls-tree",
30
+ "-r",
31
+ "--name-only",
32
+ "HEAD",
33
+ "--",
34
+ ...scope,
35
+ ]);
36
+ if (result.code !== 0) {
37
+ throw environmentFailure("head-unborn", "git ls-tree failed", result.stderr.toString("utf8").trim());
38
+ }
39
+ return nonEmptyLines(result.stdout.toString("utf8"));
40
+ }
41
+ async dirtyPaths(repoRoot, scope) {
42
+ const diffResult = await runGit(repoRoot, [
43
+ "diff",
44
+ "--quiet",
45
+ "HEAD",
46
+ "--",
47
+ ...scope,
48
+ ]);
49
+ if (diffResult.code !== 0 && diffResult.code !== 1) {
50
+ throw environmentFailure("head-unborn", "git diff --quiet failed", diffResult.stderr.toString("utf8").trim());
51
+ }
52
+ const result = await runGit(repoRoot, [
53
+ "status",
54
+ "--porcelain",
55
+ "--",
56
+ ...scope,
57
+ ]);
58
+ if (result.code !== 0) {
59
+ throw environmentFailure("not-a-git-repo", "git status failed", result.stderr.toString("utf8").trim());
60
+ }
61
+ return nonEmptyLines(result.stdout.toString("utf8"))
62
+ .map((line) => porcelainPath(line))
63
+ .sort();
64
+ }
65
+ }
66
+ function runGit(cwd, args) {
67
+ return new Promise((resolve, reject) => {
68
+ const child = spawn("git", [...args], {
69
+ cwd,
70
+ stdio: ["ignore", "pipe", "pipe"],
71
+ });
72
+ const stdout = [];
73
+ const stderr = [];
74
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
75
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
76
+ child.on("error", (error) => {
77
+ if (error.code === "ENOENT") {
78
+ reject(environmentFailure("git-not-on-path", "git binary is not on PATH"));
79
+ return;
80
+ }
81
+ reject(error);
82
+ });
83
+ child.on("close", (code) => {
84
+ resolve({
85
+ code: code ?? 1,
86
+ stdout: Buffer.concat(stdout),
87
+ stderr: Buffer.concat(stderr),
88
+ });
89
+ });
90
+ });
91
+ }
92
+ function nonEmptyLines(value) {
93
+ return value.split(/\r?\n/).filter((line) => line.length > 0);
94
+ }
95
+ function porcelainPath(line) {
96
+ const raw = line.slice(3);
97
+ const parts = raw.split(" -> ");
98
+ return parts[parts.length - 1];
99
+ }
100
+ function findRepoRoot(cwd) {
101
+ let current = resolve(cwd);
102
+ while (true) {
103
+ if (existsSync(join(current, ".git"))) {
104
+ return current;
105
+ }
106
+ const parent = dirname(current);
107
+ if (parent === current) {
108
+ throw environmentFailure("not-a-git-repo", "cwd is not inside a git working tree", cwd);
109
+ }
110
+ current = parent;
111
+ }
112
+ }
@@ -0,0 +1,5 @@
1
+ import { type SddConfig } from "../../../../shared/domain/Config.js";
2
+ import type { TokenConfigPort } from "../../ports/outbound/TokenConfigPort.js";
3
+ export declare class NodeTokenConfigReader implements TokenConfigPort {
4
+ config(repoRoot: string): Promise<SddConfig>;
5
+ }
@@ -0,0 +1,41 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { configFromJson, } from "../../../../shared/domain/Config.js";
4
+ import { configFailure } from "../../../../shared/domain/Errors.js";
5
+ export class NodeTokenConfigReader {
6
+ async config(repoRoot) {
7
+ const configPath = join(repoRoot, ".sdd", "config.json");
8
+ const text = await readConfig(configPath);
9
+ const config = configFromJson(parseConfigJson(text, configPath), configPath);
10
+ await assertSpecReadable(repoRoot, config);
11
+ return config;
12
+ }
13
+ }
14
+ async function readConfig(path) {
15
+ try {
16
+ return await readFile(path, "utf8");
17
+ }
18
+ catch (error) {
19
+ throw configFailure("config-missing", ".sdd/config.json is missing or unreadable", errorMessage(error), path);
20
+ }
21
+ }
22
+ function parseConfigJson(text, path) {
23
+ try {
24
+ return JSON.parse(text);
25
+ }
26
+ catch (error) {
27
+ throw configFailure("config-invalid", ".sdd/config.json is not valid JSON", errorMessage(error), path);
28
+ }
29
+ }
30
+ async function assertSpecReadable(repoRoot, config) {
31
+ const specPath = join(repoRoot, config.specFile);
32
+ try {
33
+ await readFile(specPath, "utf8");
34
+ }
35
+ catch (error) {
36
+ throw configFailure("config-invalid", `spec_file is not readable: ${config.specFile}`, errorMessage(error), specPath);
37
+ }
38
+ }
39
+ function errorMessage(error) {
40
+ return error instanceof Error ? error.message : String(error);
41
+ }
@@ -0,0 +1,18 @@
1
+ import { TOKEN_MECHANISM } from "../../../shared/domain/Token.js";
2
+ import type { TokenConfigPort } from "../ports/outbound/TokenConfigPort.js";
3
+ import type { TokenGitPort } from "../ports/outbound/TokenGitPort.js";
4
+ export type TokenOutcome = {
5
+ kind: "success";
6
+ token: string;
7
+ commitSha: string;
8
+ mechanism: typeof TOKEN_MECHANISM;
9
+ scope: string[];
10
+ } | {
11
+ kind: "dirty";
12
+ dirtyPaths: string[];
13
+ };
14
+ export interface ComputeTokenPorts {
15
+ config: TokenConfigPort;
16
+ git: TokenGitPort;
17
+ }
18
+ export declare function computeToken(cwd: string, ports: ComputeTokenPorts): Promise<TokenOutcome>;
@@ -0,0 +1,27 @@
1
+ import { configFailure } from "../../../shared/domain/Errors.js";
2
+ import { TOKEN_MECHANISM, token } from "../../../shared/domain/Token.js";
3
+ export async function computeToken(cwd, ports) {
4
+ const repoRoot = await ports.git.repoRoot(cwd);
5
+ const commitSha = await ports.git.headSha(repoRoot);
6
+ const config = await ports.config.config(repoRoot);
7
+ await assertGlobMatches(ports.git, repoRoot, config.discoveryScope);
8
+ const dirtyPaths = await ports.git.dirtyPaths(repoRoot, config.discoveryScope);
9
+ if (dirtyPaths.length > 0) {
10
+ return { kind: "dirty", dirtyPaths };
11
+ }
12
+ return {
13
+ kind: "success",
14
+ token: token(await ports.git.treeBytes(repoRoot, config.discoveryScope)),
15
+ commitSha,
16
+ mechanism: TOKEN_MECHANISM,
17
+ scope: config.discoveryScope,
18
+ };
19
+ }
20
+ async function assertGlobMatches(git, repoRoot, scope) {
21
+ for (const entry of scope.filter((value) => /[*?[]/.test(value))) {
22
+ const paths = await git.treePaths(repoRoot, [entry]);
23
+ if (paths.length === 0) {
24
+ throw configFailure("config-invalid", `discovery_scope glob matched zero files: ${entry}`);
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,4 @@
1
+ import type { CommandResult, OutputFormat } from "../../../../shared/domain/CliOutput.js";
2
+ export interface TokenCommand {
3
+ execute(cwd: string, format: Exclude<OutputFormat, "yaml">): Promise<CommandResult>;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { SddConfig } from "../../../../shared/domain/Config.js";
2
+ export interface TokenConfigPort {
3
+ config(repoRoot: string): Promise<SddConfig>;
4
+ }
@@ -0,0 +1,7 @@
1
+ export interface TokenGitPort {
2
+ repoRoot(cwd: string): Promise<string>;
3
+ headSha(repoRoot: string): Promise<string>;
4
+ treeBytes(repoRoot: string, scope: readonly string[]): Promise<Uint8Array>;
5
+ treePaths(repoRoot: string, scope: readonly string[]): Promise<string[]>;
6
+ dirtyPaths(repoRoot: string, scope: readonly string[]): Promise<string[]>;
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export declare const BUILTIN_AGENT_BLOCKLIST: ReadonlySet<string>;
2
+ /** True when `approver` is forbidden as an approver — either listed in the
3
+ * built-in blocklist (case-insensitive), or starts with the `bot:` prefix,
4
+ * or is in the consumer-supplied `extraBlocklist`. */
5
+ export declare function isBlockedApprover(approver: string, extraBlocklist?: readonly string[]): boolean;
@@ -0,0 +1,28 @@
1
+ /*
2
+ * Identities that may never act as approvers (SDD §7.5 / ENF-010);
3
+ * shared by the approve and lint slices.
4
+ */
5
+ export const BUILTIN_AGENT_BLOCKLIST = new Set([
6
+ "agent",
7
+ "claude",
8
+ "claude-code",
9
+ "code-gen-agent",
10
+ "codex",
11
+ "pipeline-driver",
12
+ "spec-author-bot",
13
+ "spec-lint",
14
+ "sdd-cli",
15
+ ]);
16
+ /** True when `approver` is forbidden as an approver — either listed in the
17
+ * built-in blocklist (case-insensitive), or starts with the `bot:` prefix,
18
+ * or is in the consumer-supplied `extraBlocklist`. */
19
+ export function isBlockedApprover(approver, extraBlocklist = []) {
20
+ const lower = approver.toLowerCase();
21
+ if (BUILTIN_AGENT_BLOCKLIST.has(lower)) {
22
+ return true;
23
+ }
24
+ if (approver.startsWith("bot:")) {
25
+ return true;
26
+ }
27
+ return extraBlocklist.map((s) => s.toLowerCase()).includes(lower);
28
+ }
@@ -0,0 +1,5 @@
1
+ import type { LintRecord } from "./SpecRecord.js";
2
+ export declare const EXTERNAL_BOUNDARY_TYPES: ReadonlySet<string>;
3
+ /** Returns the set of normative IDs that are boundary elements per the
4
+ * reachability edges above. Computed once per partition view. */
5
+ export declare function reachableBoundaryIds(records: ReadonlyArray<LintRecord>): Set<string>;
@@ -0,0 +1,71 @@
1
+ /*
2
+ * Boundary reachability for P2.1 lint rules (ENF-013/014/015/016):
3
+ * a Behavior/Contract reachable from a Surface with an external
4
+ * boundary_type, via single-hop surface_ref / members[] edges.
5
+ */
6
+ export const EXTERNAL_BOUNDARY_TYPES = new Set([
7
+ "api",
8
+ "sdk",
9
+ "event_bus",
10
+ "cli",
11
+ "public_db",
12
+ "public_storage",
13
+ ]);
14
+ /** Returns the set of normative IDs that are boundary elements per the
15
+ * reachability edges above. Computed once per partition view. */
16
+ export function reachableBoundaryIds(records) {
17
+ const out = new Set();
18
+ /*
19
+ * Map every Surface ID -> boundary_type so we can decide which Surfaces
20
+ * count as external.
21
+ */
22
+ const surfaceBoundaryType = new Map();
23
+ for (const r of records) {
24
+ if (r.template !== "Surface") {
25
+ continue;
26
+ }
27
+ const bt = r.parsed.boundary_type;
28
+ if (typeof bt === "string") {
29
+ surfaceBoundaryType.set(r.id, bt);
30
+ }
31
+ }
32
+ const isExternalSurface = (id) => {
33
+ const bt = surfaceBoundaryType.get(id);
34
+ return bt !== undefined && EXTERNAL_BOUNDARY_TYPES.has(bt);
35
+ };
36
+ /*
37
+ * Pass A: every Contract or Behavior whose surface_ref points to an
38
+ * external Surface is itself a boundary element.
39
+ */
40
+ for (const r of records) {
41
+ if (r.template !== "Contract" && r.template !== "Behavior") {
42
+ continue;
43
+ }
44
+ const ref = r.parsed.surface_ref;
45
+ if (typeof ref === "string" && isExternalSurface(ref)) {
46
+ out.add(r.id);
47
+ }
48
+ }
49
+ /*
50
+ * Pass B: every member listed under an external Surface's members[] is a
51
+ * boundary element (reverse edge for completeness).
52
+ */
53
+ for (const r of records) {
54
+ if (r.template !== "Surface") {
55
+ continue;
56
+ }
57
+ if (!isExternalSurface(r.id)) {
58
+ continue;
59
+ }
60
+ const members = r.parsed.members;
61
+ if (!Array.isArray(members)) {
62
+ continue;
63
+ }
64
+ for (const m of members) {
65
+ if (typeof m === "string") {
66
+ out.add(m);
67
+ }
68
+ }
69
+ }
70
+ return out;
71
+ }
@@ -0,0 +1,10 @@
1
+ export type BaselineComparison = {
2
+ kind: "match";
3
+ recordedToken: string;
4
+ recomputedToken: string;
5
+ } | {
6
+ kind: "stale";
7
+ recordedToken: string;
8
+ recomputedToken: string;
9
+ };
10
+ export declare function baselineComparison(recordedToken: string, recomputedToken: string): BaselineComparison;
@@ -0,0 +1,7 @@
1
+ /* Pure baseline-comparison decision shared between check and ready slices. */
2
+ export function baselineComparison(recordedToken, recomputedToken) {
3
+ if (recordedToken === recomputedToken) {
4
+ return { kind: "match", recordedToken, recomputedToken };
5
+ }
6
+ return { kind: "stale", recordedToken, recomputedToken };
7
+ }
@@ -0,0 +1,10 @@
1
+ import type { CliFailure, ExitCode } from "./Errors.js";
2
+ export type OutputFormat = "human" | "json" | "yaml";
3
+ export interface CommandResult {
4
+ exitCode: ExitCode;
5
+ stdout: string;
6
+ stderr: string;
7
+ }
8
+ export declare function ok(stdout: string): CommandResult;
9
+ export declare function failed(failure: CliFailure, format: OutputFormat): CommandResult;
10
+ export declare function withLf(value: string): string;