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
package/README.ru.md ADDED
@@ -0,0 +1,1046 @@
1
+ # `agent-sdd`
2
+
3
+ [![CI](https://github.com/cyberash-dev/sdd-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/cyberash-dev/sdd-cli/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
+ [![Node ≥ 20](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](package.json)
6
+
7
+ 📖 На других языках: [English](README.md)
8
+
9
+ Самостоятельный CLI-помощник для разработки на основе спецификации
10
+ (Spec-Driven Development, SDD). Вычисляет детерминированный
11
+ `freshness_token` по настраиваемой области обнаружения (Discovery scope)
12
+ вашего репозитория, сравнивает текущее состояние со значением,
13
+ записанным в блоке Brownfield-baseline спецификации, выдаёт
14
+ машиночитаемые заготовки (`Delta` / `Open-Q`), описывающие дрейф области
15
+ с момента зафиксированного базового коммита, прогоняет правила
16
+ SDD spec-lint по нормативным ID, переводит `lifecycle.status` из
17
+ `proposed` в `approved` с типизированным блоком `approval_record` через
18
+ `sdd approve` и закрывает мерджи единственной командой `sdd ready` —
19
+ строгим надмножеством `sdd lint` и `sdd check` плюс проверками покрытия
20
+ маркерами и изоляции песочницы для гейта `implementation-valid`
21
+ (gate-3) методологии SDD.
22
+
23
+ CLI **в основном работает со спецификацией только на чтение**: `sdd
24
+ token`, `sdd check`, `sdd refresh`, `sdd lint`, `sdd ready` никогда не
25
+ перезаписывают нормативное содержимое. Единственное исключение — `sdd
26
+ approve`, который атомарно записывает `lifecycle.status` +
27
+ `approval_record` и отказывается работать с агентскими идентичностями
28
+ (SDD §7.5: самоутверждение запрещено).
29
+
30
+ > **Статус**: v1.0.0, управляется `spec/spec.md`. Полная нормативная
31
+ > спецификация (Surfaces, Behaviors, Contracts, Invariants, Policies,
32
+ > Constraints, External dependencies, Migrations, Deltas,
33
+ > Implementation bindings) находится там. Этот README — руководство для
34
+ > потребителя; за деталями спецификации читайте `spec/spec.md`.
35
+ > Заметки о релизах: [CHANGELOG.md](CHANGELOG.md).
36
+ >
37
+ > **Что нового в v1.0.0** — синхронизация с Plan 2 методологии SDD:
38
+ > двухшаговое утверждение (`sdd approve` → аттестация в
39
+ > `.sdd/plans/<plan_id>.yaml`, затем `sdd finalize` для атомарного
40
+ > переключения с проспективной проверкой графа), `sdd report
41
+ > --pr-summary`, механика бюджета долга на `Partition`
42
+ > (`unmodeled_budget`), каскад semver в `sdd ready` (Policy /
43
+ > Invariant(contractual) → ссылающийся Surface). Устаревший путь прямой
44
+ > перезаписи доживает один минор как `sdd approve --inline` (deprecated;
45
+ > удаляется в v1.1.0).
46
+
47
+ ---
48
+
49
+ ## Зачем `agent-sdd`?
50
+
51
+ SDD рассматривает спецификацию проекта как единственный источник истины
52
+ для генерации кода. Блок Brownfield-baseline в `spec.md` записывает:
53
+
54
+ - `freshness_token` — хеш по области обнаружения репозитория на
55
+ некотором коммите;
56
+ - `baseline_commit_sha` — коммит, на котором был вычислен токен.
57
+
58
+ `freshness_token` позволяет гейту `baseline-valid` методологии SDD
59
+ проверить, что базовая линия спецификации всё ещё описывает реальный
60
+ репозиторий. Без механического токена у агента нет способа обнаружить,
61
+ что дерево исходников ушло от базовой линии с момента последнего
62
+ ревью.
63
+
64
+ `sdd-cli` предоставляет следующие подкоманды — первые шесть
65
+ автоматизируют цикл свежести/спецификации, `sdd record` навигирует по
66
+ спецификации, а `sdd install` распространяет правила методологии в
67
+ конфигурацию вашего агента:
68
+
69
+ | Команда | Назначение |
70
+ |---------------|--------------------------------------------------------------------|
71
+ | `sdd token` | Вычислить текущий токен области на `HEAD` (без чтения спецификации). |
72
+ | `sdd check` | Сравнить текущий токен со значением, записанным в `spec.md`. |
73
+ | `sdd refresh` | Сравнить состояние области с базовой линией, выдать заготовки. |
74
+ | `sdd lint` | Прогнать правила SDD spec-lint по `lint.spec_files`; код 1 при ошибках. |
75
+ | `sdd approve` | Перевести `proposed` ID в `approved` с типизированным `approval_record`. Отказывает агентским идентичностям (SDD §7.5). |
76
+ | `sdd ready` | Единственная проверка gate-3 (`implementation-valid`) для CI: покрытие маркерами, изоляция песочницы, агрегация lint + check. |
77
+ | `sdd record` | Навигация/правка `spec.md` по одной записи (только чтение `list`/`get`; атомарные `set`/`add` для draft/proposed). |
78
+ | `sdd install` | Установить правила методологии SDD (+ хуки Claude) в конфигурацию агента уровня пользователя (`~/.claude`, `~/.codex`) либо в репозиторий через `--scope project`. |
79
+
80
+ Механизм зафиксирован (`git_tree_hash_v1`), но инструмент универсален:
81
+ каждый следующий SDD репозиторий настраивает его через небольшой
82
+ JSON-файл (`.sdd/config.json`).
83
+
84
+ ---
85
+
86
+ ## Требования
87
+
88
+ - **Node.js** ≥ 20
89
+ - **git** ≥ 2.30 в `PATH`
90
+ - git-репозиторий — вне репозитория CLI запускаться отказывается
91
+
92
+ ---
93
+
94
+ ## Установка
95
+
96
+ ### Вариант 1 — реестр npm (рекомендуется)
97
+
98
+ ```sh
99
+ npm install --save-dev agent-sdd
100
+ ```
101
+
102
+ После установки `sdd` доступен по пути `node_modules/.bin/sdd` и
103
+ запускается через `npx sdd ...` или ваш предпочтительный скрипт пакета.
104
+
105
+ ### Вариант 2 — локальный путь
106
+
107
+ При разработке самого `sdd-cli` рядом с потребляющим репозиторием:
108
+
109
+ ```sh
110
+ npm install --save-dev "file:../sdd-cli"
111
+ ```
112
+
113
+ `package.json` будет ссылаться на `"agent-sdd": "file:../sdd-cli"`.
114
+ Это рекомендуемая компоновка, когда оба репозитория лежат рядом,
115
+ поскольку правки в `sdd-cli/` подхватываются сразу после `npm run
116
+ build`.
117
+
118
+ ### Вариант 3 — тарбол `npm pack`
119
+
120
+ Для замороженного артефакта без доступа к реестру:
121
+
122
+ ```sh
123
+ # внутри ~/Projects/sdd-cli
124
+ npm run build
125
+ npm pack # создаёт agent-sdd-<version>.tgz
126
+
127
+ # внутри потребляющего репозитория
128
+ npm install --save-dev /path/to/agent-sdd-1.0.0.tgz
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Конфигурация — `.sdd/config.json`
134
+
135
+ Положите один JSON-файл по пути `<repo_root>/.sdd/config.json`.
136
+ Минимальный пример:
137
+
138
+ ```json
139
+ {
140
+ "$schema": "https://github.com/cyberash-dev/sdd-cli/blob/main/schema/sdd.config.schema.json",
141
+ "spec_file": "spec/spec.md",
142
+ "baseline_id": "my-partition:BL-001",
143
+ "discovery_scope": [
144
+ "src",
145
+ "tests",
146
+ "package.json",
147
+ "tsconfig.json"
148
+ ],
149
+ "mechanism": "git_tree_hash_v1"
150
+ }
151
+ ```
152
+
153
+ ### Справочник полей
154
+
155
+ | Поле | Тип | Обязательно | По умолчанию | Значение |
156
+ |-----------------------------|-----------|-------------|------------------------|-------------------------------------------------------------------------|
157
+ | `spec_file` | string | да | — | Путь к файлу спецификации SDD относительно корня репозитория. |
158
+ | `baseline_id` | string | да | — | Полный `<partition>:BL-<n>` блока BrownfieldBaseline для чтения. |
159
+ | `discovery_scope` | string[] | да | — | git pathspec (каталоги, файлы, globs), передаются дословно в `git ls-tree`. |
160
+ | `mechanism` | enum | да | — | Сейчас только `"git_tree_hash_v1"`. |
161
+ | `footprint.binding_id_prefix` | string | нет | `"IMP-"` | Префикс нейтрального id, сканируемый на пути footprint. |
162
+ | `footprint.binding_field` | string | нет | `"binding"` | YAML-ключ, под которым лежат пути к файлам в блоках IMP. |
163
+ | `lint.spec_files` | string[] | нет | `[spec_file]` | Glob-шаблоны (posix) для файлов спецификации, сканируемых `sdd lint`/`sdd approve`. |
164
+ | `lint.approver_blocklist` | string[] | нет | `[]` | Дополнительные идентичности утверждающих, отклоняемые поверх встроенного списка агентов. |
165
+ | `partitions` | object | нет | отсутствует → плоская сокращённая форма | Режим нескольких партиций (CTR-015). Для каждой партиции `spec_paths` (обязательно), `test_paths`, `sandbox_paths`. Ключи соответствуют regex имени партиции ниже. |
166
+ | `test_paths` (верхнего уровня) | string[] | нет | `[]` | Сокращение, применяемое к синтезированной однопартиционной запасной форме, когда `partitions` отсутствует. |
167
+ | `sandbox_paths` (верхнего уровня) | string[] | нет | `[]` | Сокращение, применяемое к синтезированной однопартиционной запасной форме, когда `partitions` отсутствует. |
168
+
169
+ Ключи `baseline_id` и `partitions.<name>` соответствуют
170
+ `^[a-z][a-z0-9-]*(:[a-z][a-z0-9-]*)*$` (один или более токенов в нижнем
171
+ регистре, соединённых `:`; расширение CST-007 / CTR-015). Примеры:
172
+ `pipeline-driver:BL-001`, `bridge:commands:CON-004`. Односегментная
173
+ форма — это умолчание v0.1.0/v0.2.0, оно сохраняется без изменений.
174
+ Неизвестные поля верхнего уровня отклоняются — см. формальную JSON Schema
175
+ в `schema/sdd.config.schema.json`.
176
+
177
+ ### Советы по области обнаружения
178
+
179
+ - Запись области, которая разрешается в **ноль файлов** на HEAD, —
180
+ жёсткая ошибка конфигурации. Это защищает от опечаток вроде
181
+ `spec/0[0-9]-*.md`, когда таких файлов ещё нет.
182
+ - Globs используют синтаксис git pathspec (`*`, `?`, `[abc]`). Они
183
+ разрешаются относительно `git ls-tree -r --name-only HEAD`.
184
+ - Порядок не важен: `git ls-tree` канонизирует по имени, поэтому
185
+ итоговый токен стабилен при переупорядочивании.
186
+
187
+ ---
188
+
189
+ ## Блок Brownfield-baseline
190
+
191
+ `sdd-cli` ищет в `<spec_file>` один YAML-блок, у которого `id` равен
192
+ `<config.baseline_id>`, а `type` равен `BrownfieldBaseline`. Он читает
193
+ из этого блока два поля:
194
+
195
+ ```yaml
196
+ ---
197
+ id: my-partition:BL-001
198
+ type: BrownfieldBaseline
199
+ freshness_token: <64-char hex>
200
+ baseline_commit_sha: <40-char hex>
201
+ mechanism: git_tree_hash_v1
202
+ # ... lifecycle, discovery_scope, coverage_evidence и т. д.
203
+ ---
204
+ ```
205
+
206
+ Дублирующиеся базовые блоки (один и тот же `id` совпадает дважды) CLI
207
+ трактует как ошибку конфигурации.
208
+
209
+ ---
210
+
211
+ ## Команды
212
+
213
+ ### `sdd token`
214
+
215
+ Вычислить и вывести текущий токен области на `HEAD`.
216
+
217
+ ```sh
218
+ sdd token # человекочитаемый формат
219
+ sdd token --format=json # машиночитаемый
220
+ ```
221
+
222
+ **JSON-вывод (успех)**:
223
+
224
+ ```json
225
+ {
226
+ "format_version": 1,
227
+ "ok": true,
228
+ "token": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
229
+ "commit_sha": "0b0f4d84e5c7a9182f15c7f3d4e0f6a8c0e1d2b7",
230
+ "mechanism": "git_tree_hash_v1",
231
+ "scope": ["src", "tests", "package.json"]
232
+ }
233
+ ```
234
+
235
+ **JSON-вывод (область грязная)**:
236
+
237
+ ```json
238
+ {
239
+ "format_version": 1,
240
+ "ok": false,
241
+ "reason": "baseline-dirty",
242
+ "dirty_paths": ["src/foo.ts"]
243
+ }
244
+ ```
245
+
246
+ `sdd token` завершается с кодом **1**, когда рабочее дерево грязное
247
+ внутри области — CLI никогда не вычисляет токен по незакоммиченным
248
+ изменениям. Неотслеживаемые файлы внутри области считаются грязью.
249
+
250
+ ### `sdd check`
251
+
252
+ Сравнить свежевычисленный токен со значением, записанным в базовом
253
+ блоке.
254
+
255
+ ```sh
256
+ sdd check # человекочитаемый формат
257
+ sdd check --format=json
258
+ ```
259
+
260
+ **Исходы**:
261
+
262
+ | Код | Причина | Значение |
263
+ |------|--------------------|--------------------------------------------------------------------|
264
+ | 0 | — | Записанный токен совпадает с пересчитанным; дерево чистое в области. |
265
+ | 1 | `baseline-stale` | Дерево чистое, но записанный токен отличается от пересчитанного. |
266
+ | 1 | `baseline-dirty` | В рабочем дереве есть незакоммиченные изменения области; проверка прерывается. |
267
+
268
+ `sdd check` — типичный CI-гейт. Если вы поставите его в пайплайн перед
269
+ мерджем или деплоем, код выхода 1 остановит сборку, пока спецификация не
270
+ будет обновлена (через `sdd refresh` и ревью человеком) либо рабочее
271
+ дерево не будет очищено.
272
+
273
+ ### `sdd refresh`
274
+
275
+ Сравнить текущее состояние области с записанным `baseline_commit_sha` и
276
+ выдать по одной заготовке на каждый дрейфующий путь.
277
+
278
+ ```sh
279
+ sdd refresh # по умолчанию: --format=yaml
280
+ sdd refresh --format=json
281
+ sdd refresh --format=human
282
+ ```
283
+
284
+ Каждый изменённый путь раскладывается по корзинам:
285
+
286
+ - **Внутри footprint `IMP-*`** → заготовка `Delta`, называющая IMP-id,
287
+ чей `binding` покрывает этот путь, плюс `target_ids` этого IMP. Человек
288
+ или нижестоящий агент заполняет `compatibility_action`,
289
+ `kind_of_change`, `tests_old_behavior`, `tests_new_behavior`.
290
+ - **Внутри области, но вне всех footprint** → заготовка `Open-Q`,
291
+ спрашивающая, нужно ли привязать путь к нормативному ID.
292
+
293
+ **YAML-поток** (по умолчанию):
294
+
295
+ ```yaml
296
+ ---
297
+ kind: Delta
298
+ path: "src/foo.ts"
299
+ target_imp_ids:
300
+ - "my-partition:IMP-002"
301
+ target_ids:
302
+ - "my-partition:BEH-014"
303
+ emitted_at: "2026-04-29T15:37:35.000Z"
304
+ compatibility_action: TODO
305
+ kind_of_change: TODO
306
+ tests_old_behavior: TODO
307
+ tests_new_behavior: TODO
308
+ ---
309
+ kind: Open-Q
310
+ path: "spec/notes.md"
311
+ question: "Should spec/notes.md be bound to a normative ID?"
312
+ options:
313
+ - "bind_to_existing_or_new_id"
314
+ - "leave_unmodeled"
315
+ blocking: TODO
316
+ emitted_at: "2026-04-29T15:37:35.000Z"
317
+ ```
318
+
319
+ **Пустой дрейф в режиме JSON**:
320
+
321
+ ```json
322
+ { "format_version": 1, "stubs": [] }
323
+ ```
324
+
325
+ `sdd refresh` завершается с кодом **0** даже когда выданы заготовки —
326
+ команда компонуема в скриптах (`sdd refresh > stubs.yaml`). Сигнал
327
+ дрейфа — это `sdd check`, а не `sdd refresh`.
328
+
329
+ ### `sdd lint`
330
+
331
+ Прогнать правила SDD spec-lint по каждому файлу, совпавшему с
332
+ `lint.spec_files` (с откатом к единственному `spec_file`, когда блок
333
+ `lint` отсутствует). Lint никогда не модифицирует спецификацию.
334
+
335
+ ```sh
336
+ sdd lint # человекочитаемый формат (по умолчанию)
337
+ sdd lint --format=json
338
+ ```
339
+
340
+ Каждая запись ID, нарушающая правило, даёт один диагностический вывод.
341
+ ID правил (например, `sdd:weasel-word`,
342
+ `sdd:approval-record-required`, `sdd:test-obligation-required`) —
343
+ только-добавляемые: однажды опубликованный ID правила никогда не
344
+ переименовывается и не перенацеливается.
345
+
346
+ **JSON-конверт**:
347
+
348
+ ```json
349
+ {
350
+ "format_version": 1,
351
+ "ok": false,
352
+ "error_count": 3,
353
+ "warn_count": 0,
354
+ "diagnostics": [
355
+ {
356
+ "severity": "error",
357
+ "rule": "sdd:approval-record-required",
358
+ "file": "spec/spec.md",
359
+ "line": 141,
360
+ "message": "ID \"my:SUR-001\" has lifecycle.status=approved but no real approval_record (SDD §7.5)."
361
+ }
362
+ ]
363
+ }
364
+ ```
365
+
366
+ | Код | Значение |
367
+ |------|--------------------------------------------------------------------------------------|
368
+ | 0 | Все ошибки устранены (предупреждения допускаются). |
369
+ | 1 | Хотя бы один диагностический вывод уровня **error**. `ok: false` в JSON. |
370
+ | 2 | Ошибка argv (неизвестный флаг, недопустимое значение format). |
371
+ | 3 | Ошибка окружения (например, отсутствует `.sdd/config.json`). |
372
+
373
+ ### `sdd approve`
374
+
375
+ Перевести один или несколько нормативных ID из `proposed` (с
376
+ заглушкой `not_applicable_for_proposed`) в `approved` (либо
377
+ `deprecated` / `removed`), записав типизированный блок `approval_record`
378
+ в том же атомарном изменении. CLI отказывается работать, когда
379
+ `--approver` находится во встроенном списке блокировки агентов
380
+ (например, `claude`, `bot:*`, `spec-author-bot`, `sdd-cli`) или
381
+ присутствует в `lint.approver_blocklist`.
382
+
383
+ ```sh
384
+ sdd approve \
385
+ --id "my-partition:BEH-014" \
386
+ --approver alice \
387
+ --owner-role tech-lead \
388
+ --change-request "https://example.com/pr/42"
389
+ ```
390
+
391
+ **Обязательные флаги**: `--id`, `--approver`, `--owner-role`,
392
+ `--change-request`.
393
+
394
+ **Необязательные флаги**:
395
+
396
+ - `--scope <string>` (по умолчанию: `first-time-approval`)
397
+ - `--target-status approved|deprecated|removed` (по умолчанию: `approved`)
398
+ - `--reviewed-test-oracle <ref>` (рекомендуется для Surface с major-bump)
399
+ - `--format json|human` (по умолчанию: `human`)
400
+
401
+ `--id` принимает точный id или glob с `*` (например, `pol:*`). Все
402
+ совпадающие записи во всех файлах под `lint.spec_files` переписываются
403
+ одним пакетом. Записанный блок `approval_record` выглядит так:
404
+
405
+ ```yaml
406
+ approval_record:
407
+ owner_role: tech-lead
408
+ approver_identity: alice
409
+ timestamp: 2026-04-30T10:15:42.001Z
410
+ change_request: https://example.com/pr/42
411
+ scope: first-time-approval
412
+ ```
413
+
414
+ | Код | Причина | Значение |
415
+ |------|-------------------------|---------------------------------------------------------------------------------|
416
+ | 0 | — | Хотя бы одна запись совпала и была переписана. |
417
+ | 1 | `agent-approver` | `--approver` во встроенном списке блокировки агентов или начинается с `bot:` (SDD §7.5). |
418
+ | 1 | `invalid-owner-role` | `--owner-role` не входит в закрытый enum (шесть допустимых ролей). |
419
+ | 1 | `no-id-match` | `--id`/glob не совпал ни с одной записью нормативного ID во всех файлах спецификации. |
420
+ | 2 | — | Ошибка argv (отсутствует обязательный флаг, неизвестный флаг, недопустимый `--target-status`). |
421
+
422
+ **Enum роли владельца** (закрытый): `tech-lead`, `architect`,
423
+ `security-owner`, `platform-runtime-lead`, `product-owner`,
424
+ `compliance`.
425
+
426
+ ### `sdd ready`
427
+
428
+ Единственная авторитетная проверка gate-3 для CI. Строгое надмножество
429
+ `sdd lint` и `sdd check`: сканирует маркеры `@covers <partition>:<id>`
430
+ в ваших тестовых файлах, отклоняет `proposed`/`draft` ID вне
431
+ `sandbox_paths`, требует типизированный маркер `compatibility_action=…`
432
+ для `removed` ID и перезапускает семантику lint/check под одним
433
+ JSON-конвертом. Добавление `sdd ready` в политику защищённой ветки —
434
+ это то, что делает трёхгейтовый контракт SDD обеспечиваемым на практике.
435
+
436
+ ```sh
437
+ sdd ready # по умолчанию: все партиции, человекочитаемый вывод
438
+ sdd ready --format=json # стабильный JSON для CI / аннотаций GitHub
439
+ sdd ready --partition pipeline-driver # фильтр (и рычаг поэтапного развёртывания)
440
+ ```
441
+
442
+ **Коды выхода**:
443
+
444
+ | Код | Значение |
445
+ |------|--------------------------------------------------------------------------------------|
446
+ | 0 | Можно мерджить. Блокеров не найдено. |
447
+ | 1 | Найден хотя бы один блокер мерджа (любой из семи видов правил, либо агрегированный). |
448
+ | 2 | Не удалось оценить (`config_invalid` / `spec_parse_failed` / `unreadable_test_paths`). |
449
+
450
+ **Грамматика маркера** (CST-007): `@covers <partition>:<id> [key=value ...]`,
451
+ где `<partition>` соответствует
452
+ `^[a-z][a-z0-9-]*(:[a-z][a-z0-9-]*)*$` (один или более токенов в нижнем
453
+ регистре, соединённых `:` — и односегментный `my-partition:BEH-001`, и
454
+ многосегментный `bridge:commands:CON-004` парсятся), `<id>`
455
+ соответствует `^[A-Z]+-\d+$`, а единственный допустимый хвостовой ключ
456
+ в v0.3.0 — `compatibility_action=<value>`. Неизвестные хвостовые ключи
457
+ молча игнорируются (forward-compat). Разделение partition/id происходит
458
+ по самому правому `:` захваченного токена (хвост id не содержит `:`).
459
+ Размещайте маркеры где угодно в тестовых файлах — обычно как
460
+ `// @covers <id>` рядом с тестом, закрывающим обязательство.
461
+
462
+ **Настройка партиций** (CTR-015): плоская форма `.sdd/config.json` из
463
+ v0.1.0/v0.2.0 сохраняется как однопартиционное сокращение, когда
464
+ `partitions` отсутствует. Для многопартиционных репозиториев объявите:
465
+
466
+ ```json
467
+ {
468
+ "partitions": {
469
+ "my-partition": {
470
+ "spec_paths": ["spec/spec.md"],
471
+ "test_paths": ["tests/**/*.test.ts"],
472
+ "sandbox_paths": ["spike/**"]
473
+ }
474
+ }
475
+ }
476
+ ```
477
+
478
+ Кросс-партиционный тест, который законно покрывает ID и из A, и из B,
479
+ должен присутствовать в `test_paths` **обеих** партиций. Неявного
480
+ кросс-зачёта нет.
481
+
482
+ > `sdd ready` проверяет наличие трассируемости, а не достоверность теста.
483
+ > Корректность major-bump (резюме оракула/утверждений, классы входов,
484
+ > негативный оракул) — это ревью человеком по SDD §three gates.
485
+
486
+ ### `sdd record`
487
+
488
+ Навигация и правка большого `spec.md` по одной записи, без чтения или
489
+ перезаписи всего файла — спроектировано для AI-агентов, у которых
490
+ контекстное окно является дефицитным ресурсом. Четыре подкоманды:
491
+
492
+ ```sh
493
+ sdd record list # компактный индекс всех записей
494
+ sdd record list --partition my-partition # фильтр по одной партиции
495
+ sdd record get my-partition:BEH-001 # одна запись, дословно
496
+ sdd record set my-partition:BEH-001 --from-file body.yaml
497
+ sdd record set my-partition:BEH-001 --content "$BODY"
498
+ sdd record add --after my-partition:BEH-001 --from-file new.yaml
499
+ ```
500
+
501
+ - **`list`** — по одной строке на запись: `id` · `type` ·
502
+ `lifecycle.status` · производный заголовок (`title` записи, иначе
503
+ `name` у Surface, иначе пусто). `--partition <name>` оставляет только
504
+ записи, у которых компонент партиции (id за вычетом хвоста
505
+ `:<ID-tail>`) равен `<name>`. Только чтение.
506
+ - **`get <id>`** — печатает точное тело записи в исходнике
507
+ (round-trip обратно в `set`). `--format=json` добавляет `file`,
508
+ `start_line`, `end_line`. Код 1, если id не найден.
509
+ - **`set <id>`** — заменяет тело существующей записи **`draft`/`proposed`**
510
+ на месте; окружающий fence и маркеры `---` сохраняются. Тело берётся
511
+ из `--from-file <path>` или `--content <string>`, переданное либо
512
+ голым (как выдаёт `get`), либо обёрнутым в
513
+ ```` ```yaml ```` fence — оба варианта нормализуются.
514
+ - **`add --after <id>`** — вставляет новую запись в
515
+ ```` ```yaml ````-fence сразу после fence якоря. `id` тела должен быть
516
+ новым, а его статус — `draft`/`proposed`.
517
+
518
+ `set`/`add` **отказываются работать с записями `approved`/`deprecated`/`removed`**
519
+ (код 1) — изменение управляемой записи — это задача `Delta` + `sdd
520
+ approve`/`sdd finalize`. Запись атомарна (временный файл + rename) и
521
+ затрагивает только файл из `lint.spec_files`, содержащий запись; всё
522
+ остальное в файле, плюс `.sdd/config.json` и `.git/`, остаётся
523
+ побайтово идентичным (`INV-015`). `list`/`get` вообще не пишут
524
+ (`INV-002`). Запускайте `sdd lint` после любого `set`/`add` — тело
525
+ вставляется дословно, поэтому lint остаётся структурным гейтом.
526
+
527
+ **Коды выхода**: 0 успех · 1 `record-not-found` / `anchor-not-found` /
528
+ `duplicate-id` / `record-protected` / промах get · 2 `invalid-body`
529
+ (нет `id:`, не парсится, оба/ни одного входного флага, или у set id≠id тела).
530
+
531
+ ### `sdd install`
532
+
533
+ Сделать `sdd-cli` точкой распространения правил методологии SDD
534
+ (поставляются под `rules/`). Устанавливает их в конфигурацию агента
535
+ **уровня пользователя**, чтобы любой проект мог следовать дисциплине.
536
+
537
+ ```sh
538
+ sdd install all # обе цели ниже
539
+ sdd install claude # ~/.claude
540
+ sdd install codex # ~/.codex
541
+ sdd install all --dry-run # вывести запланированные файловые операции, ничего не писать
542
+ sdd install claude --format=json
543
+ sdd install all --scope project # писать в ЭТОТ репозиторий, а не в домашний каталог
544
+ ```
545
+
546
+ `--scope user|project` (по умолчанию `user`) выбирает корень назначения:
547
+
548
+ - **`user`** (по умолчанию) — побайтово как раньше: пишет под `~/.claude`
549
+ и `~/.codex` (`$SDD_INSTALL_HOME` переопределяет корень дома).
550
+ - **`project`** — пишет конфигурацию агента в текущий рабочий каталог,
551
+ чтобы репозиторий нёс SDD-настройку для всей команды: `./CLAUDE.md` и
552
+ `./AGENTS.md` в корне репозитория, а правила, навыки и `settings.json`
553
+ под `./.claude/**` и `./.codex/**`. В project-режиме команды хуков в
554
+ `settings.json` используют `$CLAUDE_PROJECT_DIR/.claude/sdd/...`, так что
555
+ закоммиченный `settings.json` переносим между машинами. Project-режим
556
+ пишет только этот набор конфигурации агента — никогда `spec/*.md`,
557
+ `.sdd/config.json`, `.git` или исходники (`INV-016` / `POL-003` /
558
+ `POL-001`).
559
+
560
+ Что появляется для каждой цели:
561
+
562
+ - **`claude`** — минимальные контекстные правила TDD+SDD копируются в
563
+ `~/.claude/sdd/` и подключаются через `@import` из управляемого блока в
564
+ `~/.claude/CLAUDE.md`; полная справка ставится как навык по требованию
565
+ в `~/.claude/skills/spec-driven-development/SKILL.md`; и два хука
566
+ `PreToolUse` сливаются в `~/.claude/settings.json` — напоминание о
567
+ lint и **спец-страж чтения**, который запрещает читать `spec/*.md` в
568
+ любом проекте, несущем `.sdd/config.json` (вынуждая использовать `sdd
569
+ record`).
570
+ - **`codex`** — каждое правило копируется в `~/.codex/sdd/` и
571
+ перечисляется в управляемом блоке в `~/.codex/AGENTS.md` (у Codex нет
572
+ хоста для `@import` / хуков, поэтому хуки отмечаются как пропущенные).
573
+
574
+ Набор артефактов управляется данными из `rules/manifest.json`
575
+ (`CST-008`). Установка **идемпотентна** (управляемые блоки заменяются на
576
+ месте, записи хуков дедуплицируются по matcher+command, заранее
577
+ существующие пользовательские хуки сохраняются) и это единственная
578
+ команда, которая пишет вне репозитория: только под корнями домашних
579
+ каталогов агентов, никогда внутри `<repo_root>` (`INV-016` /
580
+ `POL-003`). `$SDD_INSTALL_HOME` переопределяет корень домашнего каталога.
581
+
582
+ | Код | Причина | Значение |
583
+ |------|---------------------|------------------------------------------------------|
584
+ | 0 | — | Установка (или план `--dry-run`) завершена. |
585
+ | 1 | `manifest-missing` / `manifest-invalid` / `artifact-missing` | Не удалось прочитать поставляемый файл правила или манифест; ничего не записывается. |
586
+ | 2 | — | Ошибка argv (отсутствует/неизвестная цель, неизвестный флаг). |
587
+
588
+ ### Сводка форматов вывода
589
+
590
+ | Подкоманда | `human` | `json` | `yaml` |
591
+ |---------------|----------------|--------|--------|
592
+ | `sdd token` | да (умолчание) | да | — |
593
+ | `sdd check` | да (умолчание) | да | — |
594
+ | `sdd refresh` | да | да | да (умолчание) |
595
+ | `sdd lint` | да (умолчание) | да | — |
596
+ | `sdd approve` | да (умолчание) | да | — |
597
+ | `sdd ready` | да (умолчание) | да | — |
598
+ | `sdd record` | да (умолчание) | да | — |
599
+ | `sdd install` | да (умолчание) | да | — |
600
+
601
+ JSON-выводы несут `format_version: 1` и стабильны согласно контрактам в
602
+ `spec/spec.md` §7. Человекочитаемый вывод — это однострочное резюме плюс
603
+ отступленная детализация; он опускает временную метку `emitted_at`.
604
+
605
+ ---
606
+
607
+ ## Таксономия кодов выхода
608
+
609
+ ```
610
+ 0 чисто / успех
611
+ 1 дрейф (baseline-stale ИЛИ baseline-dirty); refresh-с-заготовками НЕ равен 1
612
+ 2 ошибка конфигурации
613
+ 3 ошибка окружения
614
+ ```
615
+
616
+ | Код | Причина | Откуда может прийти |
617
+ |------|---------------------------------|----------------------------------------------------|
618
+ | 0 | — | Успешный запуск. |
619
+ | 1 | `baseline-dirty` | Незакоммиченные изменения, затрагивающие область. |
620
+ | 1 | `baseline-stale` | Записанный токен не совпадает с пересчитанным. |
621
+ | 2 | `config-missing` | `.sdd/config.json` не существует. |
622
+ | 2 | `config-invalid` | Нарушение схемы, плохой JSON, неразрешимый baseline_commit_sha, glob области с нулём совпадений и т. д. |
623
+ | 2 | `baseline-block-missing` | В спецификации нет блока с `id == config.baseline_id`. |
624
+ | 2 | `baseline-block-duplicate` | В спецификации несколько блоков с одинаковым `id`. |
625
+ | 3 | `git-not-on-path` | Бинарь `git` не в `PATH`. |
626
+ | 3 | `not-a-git-repo` | cwd не внутри рабочего дерева git. |
627
+ | 3 | `head-unborn` | Репозиторий есть, но `HEAD` не разрешается. |
628
+ | 1 | `agent-approver` | `sdd approve` отказывает, когда `--approver` — агентская идентичность. |
629
+ | 1 | `invalid-owner-role` | `sdd approve` отказывает при неизвестном `--owner-role`. |
630
+ | 1 | `no-id-match` | `sdd approve` отказывает, когда `--id`/glob не совпал с записями. |
631
+
632
+ Причины — стабильные строки: нижестоящий инструментарий может на них
633
+ полагаться.
634
+
635
+ ---
636
+
637
+ ## Механизм токена — `git_tree_hash_v1`
638
+
639
+ ```
640
+ 1. git diff --quiet HEAD -- <scope> # если не ноль -> baseline-dirty (код 1)
641
+ 2. git ls-tree HEAD -- <scope> # захватить байты stdout дословно
642
+ 3. token = hex(sha256(stdout_bytes))
643
+ 4. commit_sha = trim(stdout от `git rev-parse HEAD`)
644
+ 5. выдать { token, commit_sha, mechanism, scope }
645
+ ```
646
+
647
+ Детерминизм идёт из канонического вывода `ls-tree` git: для
648
+ фиксированного коммита и набора pathspec байты идентичны между вызовами
649
+ на одном семействе версий git. Переупорядочивание записей области не
650
+ меняет токен, поскольку git канонизирует по имени.
651
+
652
+ Набор git-подкоманд, используемых `sdd-cli`, — строгий allowlist:
653
+ `diff --quiet HEAD`, `ls-tree HEAD`, `rev-parse HEAD`,
654
+ `rev-parse --is-inside-work-tree`, `diff --name-only baseline..HEAD`,
655
+ `status --porcelain`. Ни одна мутирующая состояние подкоманда никогда не
656
+ вызывается (см. `spec/spec.md` POL-002).
657
+
658
+ ---
659
+
660
+ ## Рабочий процесс с высоты птичьего полёта
661
+
662
+ Два взгляда на один и тот же цикл: блок-схема полного жизненного цикла
663
+ и таблица поиска для «я знаю свою ситуацию, просто дай команду».
664
+ Подробные пошаговые сценарии — в разделе
665
+ [Типичные рабочие процессы](#типичные-рабочие-процессы) ниже.
666
+
667
+ ### Цикл SDD
668
+
669
+ ```mermaid
670
+ flowchart TD
671
+ classDef cmd fill:#e3f2fd,stroke:#1565c0,color:#0d47a1
672
+ classDef human fill:#fff8e1,stroke:#f57f17,color:#5d4037
673
+ classDef ok fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20
674
+ classDef gate fill:#f3e5f5,stroke:#6a1b9a,color:#311b92
675
+
676
+ S([SDD repo]):::ok
677
+
678
+ S --> Q1{BL block has a real<br/>freshness_token?}
679
+
680
+ Q1 -- "no — fresh repo" --> B1["sdd token --format=json"]:::cmd
681
+ B1 --> B2[paste token + commit_sha<br/>into the BL block]:::human
682
+ B2 --> B3["sdd approve<br/>--id partition:BL-NNN ..."]:::cmd
683
+ B3 --> Q2
684
+
685
+ Q1 -- "yes" --> Q2{routine check<br/>or CI gate?}
686
+
687
+ Q2 -- "CI / pre-merge" --> RDY["sdd ready"]:::gate
688
+ Q2 -- "introspect" --> L["sdd lint"]:::cmd
689
+ Q2 -- "introspect" --> C["sdd check"]:::cmd
690
+
691
+ RDY -- "exit 0" --> OK([all green]):::ok
692
+ RDY -- "exit 1 — uncovered / unapproved /<br/>orphan_covers / unknown_partition_covers /<br/>aggregated_lint / aggregated_check / ..." --> RDYF[add `@covers <id>` next to a test /<br/>move proposed IDs into sandbox_paths /<br/>fix the upstream lint/check blocker]:::human
693
+ RDYF --> RDY
694
+ RDY -- "exit 2 — config_invalid /<br/>spec_parse_failed /<br/>unreadable_test_paths" --> RDYC[fix .sdd/config.json or<br/>spec.md syntax, retry]:::human
695
+ RDYC --> RDY
696
+
697
+ L -- "exit 1" --> LF[fix weasel words /<br/>missing approval_record /<br/>missing test_obligation / ...]:::human
698
+ LF --> L
699
+ L -- "exit 0" --> OK
700
+
701
+ C -- "exit 0" --> OK
702
+ C -- "exit 1 — baseline-dirty" --> CD[commit or stash<br/>scope-touching edits]:::human
703
+ CD --> C
704
+ C -- "exit 1 — baseline-stale" --> R["sdd refresh > stubs.yaml"]:::cmd
705
+ R --> RS[fill Delta / Open-Q stubs,<br/>edit spec.md, commit]:::human
706
+ RS --> RT["sdd token<br/>paste new token + sha<br/>into BL block"]:::cmd
707
+ RT --> C
708
+
709
+ OK --> Q3{proposed ID got<br/>human sign-off?}
710
+ Q3 -- "yes" --> A["sdd approve --id ...<br/>--approver alice<br/>--owner-role ...<br/>--change-request ..."]:::cmd
711
+ A --> RDY
712
+ Q3 -- "no" --> Q4{cutting a release?}
713
+ Q4 -- "yes" --> RE([release gate:<br/>sdd ready exit 0]):::ok
714
+ Q4 -- "no" --> END([continue work]):::ok
715
+ ```
716
+
717
+ Читайте схему в четыре слоя:
718
+
719
+ 1. **Bootstrap** (левая ветка от `Q1`) — единоразово, когда блок
720
+ Brownfield-baseline ещё содержит значения-заглушки. Вычислите токен,
721
+ вставьте его, утвердите запись BL человеческой идентичностью,
722
+ убедитесь, что `sdd check` зелёный.
723
+ 2. **CI / pre-merge** (`Q2 → RDY`) — `sdd ready` — единственный
724
+ авторитетный гейт. Это строгое надмножество `sdd lint` и `sdd
725
+ check`: перезапускает оба под одним JSON-конвертом, плюс обеспечивает
726
+ покрытие маркерами (`@covers <id>`), изоляцию песочницы для
727
+ `proposed` ID и маркеры `compatibility_action=…` для `removed` ID.
728
+ Добавьте `sdd ready` в политику защищённой ветки — и вам не нужно
729
+ подключать `sdd lint` и `sdd check` отдельно.
730
+ 3. **Интроспекция** (`Q2 → L` / `Q2 → C`) — когда вам нужен точечный
731
+ ответ на один вопрос (только правила спецификации, только свежесть
732
+ области), отдельные команды по-прежнему полезны для сужения
733
+ диагностики.
734
+ 4. **Реакция на дрейф** (правая ветка от `C`) — когда `sdd check`
735
+ сообщает `baseline-stale`, `sdd refresh` выдаёт по одной заготовке на
736
+ дрейфующий путь. После того как человек заполнит заготовки и обновит
737
+ спецификацию, пересчитайте токен через `sdd token` и перезапишите его
738
+ в блоке BL.
739
+
740
+ Утверждение (`A`) по дизайну доступно только человеку (SDD §7.5: `sdd
741
+ approve` отказывает агентским идентичностям). Это переход с `proposed`
742
+ на `approved` для нормативного ID, но никогда не способ обойти `sdd
743
+ ready` (или `sdd lint` / `sdd check` под ним).
744
+
745
+ ### Когда какую команду запускать
746
+
747
+ | Ситуация | Команда(ы) |
748
+ |-----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
749
+ | Свежий репозиторий, BrownfieldBaseline ещё с заглушками | `sdd token` → вставить token + commit_sha → `sdd approve --id <part>:BL-NNN ...` → `sdd check` |
750
+ | **Гейт CI перед мерджем / деплоем** | **`sdd ready`** (строгое надмножество `sdd lint` + `sdd check` — одна команда, один JSON-конверт) |
751
+ | **Проверка работоспособности перед релизом** | **`sdd ready`** |
752
+ | Интроспекция: следует ли спецификация правилам SDD? | `sdd lint` |
753
+ | Интроспекция: дрейфнуло ли что-то в области с базовой линии? | `sdd check` |
754
+ | `sdd ready` помечает `[uncovered]` | добавить `// @covers <partition>:<id>` рядом с тестом, закрывающим обязательство, или поставить `Test obligation: not_applicable + reason` в спецификации |
755
+ | `sdd ready` помечает `[unapproved]` | повысить через `sdd approve …` (с человеческой идентичностью) или перенести файл спецификации с proposed ID в `partitions[*].sandbox_paths` |
756
+ | `sdd ready` помечает `[unknown_partition_covers]` | добавить партицию в `.sdd/config.json#partitions` или исправить префикс маркера на проблемной строке |
757
+ | `sdd check` сообщает `baseline-dirty` | `git commit` или `git stash` ваших правок рабочего дерева, затрагивающих область, затем перезапустить `sdd check` (или `sdd ready`) |
758
+ | `sdd check` сообщает `baseline-stale` | `sdd refresh > stubs.yaml` → заполнить заготовки `Delta` / `Open-Q` в спецификацию → коммит → `sdd token` → вставить свежий token + commit_sha → `sdd check` |
759
+ | Ревьюер одобрил `proposed` ID | `sdd approve --id ... --approver <human> --owner-role ... --change-request <url>` → `sdd ready` |
760
+ | Посмотреть текущий токен области, не трогая спецификацию | `sdd token` (или `sdd token --format=json` для пайпа) |
761
+
762
+ > Все команды работают со спецификацией только на чтение, **кроме `sdd
763
+ > approve`**, который атомарно переписывает `lifecycle.status` +
764
+ > `approval_record` (INV-002 / INV-007). `sdd refresh` пишет только в
765
+ > stdout — применяйте его заготовки вручную.
766
+
767
+ ---
768
+
769
+ ## Типичные рабочие процессы
770
+
771
+ ### 1 — начальная настройка новой базовой линии SDD
772
+
773
+ У вас есть репозиторий со спецификацией, но пока без `freshness_token`.
774
+
775
+ ```sh
776
+ # 1. добавьте config + пустой блок BrownfieldBaseline в spec.md.
777
+ # оставьте freshness_token / baseline_commit_sha как заглушки.
778
+
779
+ # 2. вычислите реальный токен на текущем HEAD.
780
+ sdd token --format=json
781
+ # {"token":"<TOKEN>","commit_sha":"<SHA>", ... }
782
+
783
+ # 3. вставьте TOKEN и SHA в блок BL-001 в spec.md.
784
+ # закоммитьте. добавьте не-агентский approval_record к BL-001.
785
+
786
+ # 4. подтвердите, что базовая линия согласована.
787
+ sdd check
788
+ # код 0
789
+ ```
790
+
791
+ ### 2 — ежедневный / CI гейт
792
+
793
+ Подключите `sdd check` к гейту, который решает, разрешено ли коду
794
+ двигаться из `spec-valid` в `implementation-valid`.
795
+
796
+ ```yaml
797
+ # пример шага GitHub Actions
798
+ - run: npx sdd check
799
+ ```
800
+
801
+ Если `sdd check` завершается с кодом 1, то либо:
802
+
803
+ - рабочее дерево грязное (закоммитьте изменения), либо
804
+ - с момента записанной базовой линии приземлился коммит, затрагивающий
805
+ область (запустите `sdd refresh` и обновите спецификацию).
806
+
807
+ ### 3 — приземлилось изменение, затрагивающее область
808
+
809
+ После коммита изменения кода `sdd check` сообщает `baseline-stale`.
810
+ Теперь вы знаете, что спецификации нужно обновление — но спецификация
811
+ является источником истины, поэтому нельзя просто перезаписать новый
812
+ токен. Вместо этого:
813
+
814
+ ```sh
815
+ sdd refresh > /tmp/stubs.yaml
816
+ ```
817
+
818
+ Для каждого изменённого пути CLI выдаёт ровно одну заготовку:
819
+
820
+ - заготовку `Delta`, если путь живёт внутри существующего footprint IMP
821
+ — заполните `compatibility_action`, `kind_of_change` и ссылки на
822
+ тесты, затем добавьте заготовку в секцию `Deltas` вашей спецификации;
823
+ - заготовку `Open-Q`, если путь в области, но ни один IMP его не
824
+ заявляет — решите, привязать ли его к существующему/новому
825
+ нормативному id или оставить unmodeled.
826
+
827
+ После того как правки спецификации приземлятся, пересчитайте токен:
828
+
829
+ ```sh
830
+ sdd token --format=json | jq -r .token # вставить в BL-001.freshness_token
831
+ sdd token --format=json | jq -r .commit_sha # вставить в BL-001.baseline_commit_sha
832
+ ```
833
+
834
+ …и `sdd check` снова зелёный.
835
+
836
+ ### 4 — `sdd ready` как единственный CI-гейт
837
+
838
+ `sdd ready` — единственная команда, которую должен вызывать CI. Это
839
+ строгое надмножество `sdd lint` и `sdd check`: перезапускает оба под
840
+ единым JSON-конвертом (виды `aggregated_lint` / `aggregated_check`), и
841
+ поверх добавляет проверки gate-3 (`implementation-valid`) — каждый
842
+ `approved`/`deprecated` нормативный ID должен иметь ≥ 1 тест,
843
+ аннотированный `@covers <partition>:<id>`, каждый `removed` ID должен
844
+ иметь соответствующий маркер `compatibility_action=…`, ни один
845
+ `proposed`/`draft` ID не может жить вне `partitions[*].sandbox_paths`, а
846
+ сиротские/неизвестно-партиционные маркеры всплывают как
847
+ `[orphan_covers]` / `[unknown_partition_covers]`.
848
+
849
+ ```yaml
850
+ - run: npx sdd ready
851
+ ```
852
+
853
+ Подключение `sdd ready` в политику защищённой ветки — это то, что делает
854
+ трёхгейтовый контракт SDD обеспечиваемым на практике. Вам больше не
855
+ нужно подключать `sdd lint` и `sdd check` отдельно — оба выполняются
856
+ внутри `sdd ready`. Они остаются полезными для сужения диагностики при
857
+ локальной разработке.
858
+
859
+ `sdd ready` завершается с 0 (можно мерджить), 1 (блокер — см. список
860
+ нарушений) или 2 (`config_invalid` / `spec_parse_failed` /
861
+ `unreadable_test_paths`, т. е. гейт не смог даже оценить).
862
+
863
+ ### 5 — повышение `proposed` ID до `approved`
864
+
865
+ Когда ревьюер-человек одобряет ID (Behavior, Contract, Invariant,
866
+ Surface и т. д.), он переключает его жизненный цикл с `proposed` на
867
+ `approved` и проставляет типизированный блок `approval_record`. `sdd
868
+ approve` делает это одним атомарным изменением и отказывает агентским
869
+ идентичностям (SDD §7.5: самоутверждение запрещено).
870
+
871
+ ```sh
872
+ sdd approve \
873
+ --id "my-partition:BEH-014" \
874
+ --approver alice \
875
+ --owner-role tech-lead \
876
+ --change-request "https://example.com/pr/42"
877
+
878
+ # `sdd approve` переписывает:
879
+ # lifecycle.status: approved
880
+ # approval_record:
881
+ # owner_role: tech-lead
882
+ # approver_identity: alice
883
+ # timestamp: 2026-04-30T10:15:42.001Z
884
+ # change_request: https://example.com/pr/42
885
+ # scope: first-time-approval
886
+ ```
887
+
888
+ Если `--approver` во встроенном списке блокировки агентов (например,
889
+ `claude`, `codex`, `bot:tg-1`, сам `sdd-cli`), команда завершается с
890
+ кодом 1 и причиной `agent-approver` и ничего не пишет.
891
+
892
+ После утверждения запустите `sdd ready`, чтобы убедиться, что запись
893
+ теперь проходит `sdd:approval-record-required` и что gate-3 (тест,
894
+ аннотированный `@covers` для только что утверждённого ID) на месте.
895
+
896
+ ### 6 — подтверждение релиза
897
+
898
+ Прямо перед тегированием релиза `sdd ready` должен быть код 0. Этот
899
+ единственный сигнал означает: правила спецификации проходят, записанная
900
+ базовая линия свежая, каждый approved ID имеет тест `@covers`, ни один
901
+ `proposed`/`draft` ID не выскользнул за пределы `sandbox_paths`, и
902
+ рабочее дерево чистое. Релизы без этого сигнала нарушают инвариант SDD о
903
+ том, что «спецификация является источником истины».
904
+
905
+ ---
906
+
907
+ ## Архитектура
908
+
909
+ `sdd-cli` следует архитектуре Vertical Slice + Hexagonal. Каждая
910
+ команда (`token`, `check`, `refresh`, `lint`, `approve`, `ready`)
911
+ владеет своим слайсом с локальными domain, application, ports и
912
+ adapters. Композиционный корень — `src/cli.ts`.
913
+
914
+ ```
915
+ src/
916
+ cli.ts # роутер argv / DI
917
+ features/
918
+ token/
919
+ domain/ # —
920
+ application/ # ComputeToken
921
+ ports/{inbound,outbound}/
922
+ adapters/{inbound,outbound}/ # CliTokenHandler, ChildProcessTokenGit, NodeTokenConfigReader
923
+ check/
924
+ domain/ # BaselineComparison
925
+ application/ # CheckBaseline
926
+ ports/{inbound,outbound}/
927
+ adapters/{inbound,outbound}/
928
+ refresh/
929
+ domain/ # Footprint, DiffStubs
930
+ application/ # BuildRefreshStubs
931
+ ports/{inbound,outbound}/
932
+ adapters/{inbound,outbound}/
933
+ lint/
934
+ domain/ # Diagnostic, Record, SpecParser, Rules
935
+ application/ # RunLint
936
+ ports/{inbound,outbound}/
937
+ adapters/{inbound,outbound}/
938
+ approve/
939
+ domain/ # ApproveRequest (incl. BUILTIN_AGENT_BLOCKLIST), Rewrite
940
+ application/ # ApplyApproval
941
+ ports/{inbound,outbound}/
942
+ adapters/{inbound,outbound}/
943
+ ready/
944
+ domain/ # MarkerParser (CST-007), PartitionResolver, Rules (8 rule fns)
945
+ application/ # RunReady — strict superset of lint + check
946
+ ports/{inbound,outbound}/
947
+ adapters/{inbound,outbound}/
948
+ shared/
949
+ domain/ # Config (incl. LintConfig + partitions), Token, SpecBlocks,
950
+ # Scope, CliOutput, Errors, PartitionGrammar (CST-007 source of truth),
951
+ # SpecRecord, LintReport, LintRules, CheckOutcome
952
+ ```
953
+
954
+ Кросс-фичевые импорты запрещены и обеспечиваются
955
+ `tests/unit/layer-imports.test.ts` (по `INV-004`). Общие примитивы
956
+ живут только под `src/shared/domain`.
957
+
958
+ ---
959
+
960
+ ## Разработка
961
+
962
+ ```sh
963
+ git clone <repo>
964
+ cd sdd-cli
965
+ npm install
966
+
967
+ npm run tsc # проверка типов (без эмита)
968
+ npm run test:unit
969
+ npm run test:integration
970
+ npm run build # tsc + chmod +x dist/cli.js
971
+ node dist/cli.js --help
972
+ ```
973
+
974
+ Интеграционный набор поднимает временные git-репозитории и запускает
975
+ собранный CLI. `tests/integration/git-shim-allowlist.test.ts`
976
+ обеспечивает POL-002 (только git-подкоманды из allowlist EXT-001), а
977
+ `tests/integration/fs-readonly.test.ts` обеспечивает INV-002 / POL-001
978
+ (спецификация, конфиг и git refs/objects не изменены после каждого
979
+ запуска).
980
+
981
+ `tests/integration/package-bin.test.ts` запускает `npm pack` от начала
982
+ до конца и устанавливает тарбол в свежий потребляющий проект, чтобы
983
+ проверить проводку `bin` (CTR-007). Выделите ~2 минуты на этот тест при
984
+ первом запуске.
985
+
986
+ ---
987
+
988
+ ## Ограничения / вне области (v0.3.0)
989
+
990
+ - Публикация `agent-sdd` в реестре npm.
991
+ - Другие механизмы токена (`sha256_of_concat`, `git_tag_based`).
992
+ - Команда скаффолдинга (`sdd init`).
993
+ - Авто-применение заготовок `sdd refresh` обратно в `spec.md`
994
+ (запрещено INV-002).
995
+ - Локализованный вывод / каталоги сообщений.
996
+ - Lint/агрегированная диагностика на near-miss-ах `@covers` (например,
997
+ верхний регистр в префиксе партиции). v0.3.0 молча их пропускает — см.
998
+ [`OQ-017`](spec/spec.md) для отложенного решения.
999
+
1000
+ `sdd lint` поставлен в v0.2.0, а `sdd ready` — в v0.3.0, оба больше не
1001
+ вне области. `sdd record` (только чтение `list`/`get`, плюс `set`/`add`,
1002
+ пишущие одну draft/proposed запись) — единственный санкционированный
1003
+ писатель `spec.md`; общий запрет «никаких авто-записей» выше касается
1004
+ конкретно заготовок `sdd refresh`; записи record управляются `INV-015`.
1005
+ `sdd install` (`SUR-016`) в области и отличается от вне-области
1006
+ скаффолдинга `sdd init`: он пишет правила методологии и хуки Claude в
1007
+ конфигурацию агента уровня пользователя, никогда в рабочее дерево
1008
+ репозитория (`INV-016` / `POL-003`).
1009
+
1010
+ ---
1011
+
1012
+ ## Документы в этом репозитории
1013
+
1014
+ | Файл | Назначение |
1015
+ |-------------------|--------------------------------------------------------------------------------|
1016
+ | `spec/spec.md` | Нормативная спецификация — единственный источник истины. |
1017
+ | `README.md` | Руководство потребителя (английская версия). |
1018
+ | `README.ru.md` | Руководство потребителя (этот файл, русская версия). |
1019
+ | `CHANGELOG.md` | Заметки о релизах по версиям, привязанные к ID спецификации. |
1020
+ | `RELEASING.md` | Как нарезать релиз и опубликовать в npm. |
1021
+ | `CLAUDE.md` | Инструкции для агентов Claude Code, специфичные для проекта. |
1022
+ | `AGENTS.md` | Привязанные к корню репозитория, агент-агностичные правила для любого AI-агента. |
1023
+ | `LICENSE` | MIT. |
1024
+ | `schema/sdd.config.schema.json` | Опубликованная JSON Schema для `.sdd/config.json`. |
1025
+
1026
+ ---
1027
+
1028
+ ## Участие в разработке
1029
+
1030
+ Это персональный инструмент, опубликованный для повторного
1031
+ использования. PR приветствуются, но дисциплина SDD обеспечивается:
1032
+ каждое изменение поведения требует соответствующего обновления
1033
+ спецификации в том же PR, `sdd lint` должен завершаться с кодом 0, а
1034
+ `sdd approve` доступен только человеку (CLI отказывает агентским
1035
+ идентичностям). См. `AGENTS.md` для правил, которым должен следовать
1036
+ AI-агент при работе в этом репозитории.
1037
+
1038
+ ---
1039
+
1040
+ ## Спецификация
1041
+
1042
+ Полная нормативная спецификация — Surfaces, Behaviors, Contracts,
1043
+ Invariants, Policies, Constraints, External dependencies, Generated
1044
+ artefacts, Implementation bindings, Open questions, Assumptions — в
1045
+ `spec/spec.md`. Если поведение вас удивляет, этот файл — источник
1046
+ истины, и любое расхождение между кодом и спецификацией является багом.