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.
- package/LICENSE +21 -0
- package/README.md +1028 -0
- package/README.ru.md +1046 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +867 -0
- package/dist/features/approve/adapters/inbound/CliApproveHandler.d.ts +17 -0
- package/dist/features/approve/adapters/inbound/CliApproveHandler.js +108 -0
- package/dist/features/approve/adapters/outbound/NodeApproveFileSystem.d.ts +8 -0
- package/dist/features/approve/adapters/outbound/NodeApproveFileSystem.js +147 -0
- package/dist/features/approve/adapters/outbound/NodePlanFileWriter.d.ts +6 -0
- package/dist/features/approve/adapters/outbound/NodePlanFileWriter.js +92 -0
- package/dist/features/approve/adapters/outbound/SystemApproveClock.d.ts +4 -0
- package/dist/features/approve/adapters/outbound/SystemApproveClock.js +5 -0
- package/dist/features/approve/application/ApplyApproval.d.ts +19 -0
- package/dist/features/approve/application/ApplyApproval.js +30 -0
- package/dist/features/approve/application/WriteAttestation.d.ts +19 -0
- package/dist/features/approve/application/WriteAttestation.js +23 -0
- package/dist/features/approve/domain/ApproveRequest.d.ts +24 -0
- package/dist/features/approve/domain/ApproveRequest.js +24 -0
- package/dist/features/approve/domain/Rewrite.d.ts +1 -0
- package/dist/features/approve/domain/Rewrite.js +6 -0
- package/dist/features/approve/ports/inbound/ApproveCommand.d.ts +10 -0
- package/dist/features/approve/ports/inbound/ApproveCommand.js +1 -0
- package/dist/features/approve/ports/outbound/ApproveClock.d.ts +3 -0
- package/dist/features/approve/ports/outbound/ApproveClock.js +1 -0
- package/dist/features/approve/ports/outbound/ApproveConfigPort.d.ts +4 -0
- package/dist/features/approve/ports/outbound/ApproveConfigPort.js +1 -0
- package/dist/features/approve/ports/outbound/ApproveFileSystem.d.ts +8 -0
- package/dist/features/approve/ports/outbound/ApproveFileSystem.js +1 -0
- package/dist/features/approve/ports/outbound/PlanFileWriter.d.ts +13 -0
- package/dist/features/approve/ports/outbound/PlanFileWriter.js +1 -0
- package/dist/features/check/adapters/inbound/CliCheckHandler.d.ts +8 -0
- package/dist/features/check/adapters/inbound/CliCheckHandler.js +62 -0
- package/dist/features/check/adapters/outbound/ChildProcessCheckGit.d.ts +8 -0
- package/dist/features/check/adapters/outbound/ChildProcessCheckGit.js +112 -0
- package/dist/features/check/adapters/outbound/NodeCheckFileReader.d.ts +7 -0
- package/dist/features/check/adapters/outbound/NodeCheckFileReader.js +44 -0
- package/dist/features/check/application/CheckBaseline.d.ts +28 -0
- package/dist/features/check/application/CheckBaseline.js +54 -0
- package/dist/features/check/domain/BaselineComparison.d.ts +1 -0
- package/dist/features/check/domain/BaselineComparison.js +2 -0
- package/dist/features/check/ports/inbound/CheckCommand.d.ts +4 -0
- package/dist/features/check/ports/inbound/CheckCommand.js +1 -0
- package/dist/features/check/ports/outbound/CheckConfigPort.d.ts +4 -0
- package/dist/features/check/ports/outbound/CheckConfigPort.js +1 -0
- package/dist/features/check/ports/outbound/CheckGitPort.d.ts +7 -0
- package/dist/features/check/ports/outbound/CheckGitPort.js +1 -0
- package/dist/features/check/ports/outbound/CheckSpecPort.d.ts +9 -0
- package/dist/features/check/ports/outbound/CheckSpecPort.js +1 -0
- package/dist/features/doctor/adapters/inbound/CliDoctorHandler.d.ts +8 -0
- package/dist/features/doctor/adapters/inbound/CliDoctorHandler.js +77 -0
- package/dist/features/doctor/adapters/outbound/NodeRegistryReader.d.ts +5 -0
- package/dist/features/doctor/adapters/outbound/NodeRegistryReader.js +40 -0
- package/dist/features/doctor/application/RunDoctor.d.ts +30 -0
- package/dist/features/doctor/application/RunDoctor.js +78 -0
- package/dist/features/doctor/domain/RegistryRow.d.ts +23 -0
- package/dist/features/doctor/domain/RegistryRow.js +114 -0
- package/dist/features/doctor/domain/SemverRange.d.ts +7 -0
- package/dist/features/doctor/domain/SemverRange.js +82 -0
- package/dist/features/doctor/ports/inbound/DoctorCommand.d.ts +8 -0
- package/dist/features/doctor/ports/inbound/DoctorCommand.js +1 -0
- package/dist/features/doctor/ports/outbound/RegistryReader.d.ts +12 -0
- package/dist/features/doctor/ports/outbound/RegistryReader.js +1 -0
- package/dist/features/finalize/adapters/inbound/CliFinalizeHandler.d.ts +8 -0
- package/dist/features/finalize/adapters/inbound/CliFinalizeHandler.js +80 -0
- package/dist/features/finalize/adapters/outbound/NodeFinalizeFileSystem.d.ts +11 -0
- package/dist/features/finalize/adapters/outbound/NodeFinalizeFileSystem.js +167 -0
- package/dist/features/finalize/adapters/outbound/NodePlanRepo.d.ts +7 -0
- package/dist/features/finalize/adapters/outbound/NodePlanRepo.js +82 -0
- package/dist/features/finalize/adapters/outbound/SystemFinalizeClock.d.ts +4 -0
- package/dist/features/finalize/adapters/outbound/SystemFinalizeClock.js +5 -0
- package/dist/features/finalize/application/RunFinalize.d.ts +34 -0
- package/dist/features/finalize/application/RunFinalize.js +98 -0
- package/dist/features/finalize/domain/ValidateFinalizeGraph.d.ts +9 -0
- package/dist/features/finalize/domain/ValidateFinalizeGraph.js +86 -0
- package/dist/features/finalize/ports/inbound/FinalizeCommand.d.ts +7 -0
- package/dist/features/finalize/ports/inbound/FinalizeCommand.js +1 -0
- package/dist/features/finalize/ports/outbound/FinalizeClock.d.ts +3 -0
- package/dist/features/finalize/ports/outbound/FinalizeClock.js +1 -0
- package/dist/features/finalize/ports/outbound/FinalizeConfigPort.d.ts +4 -0
- package/dist/features/finalize/ports/outbound/FinalizeConfigPort.js +1 -0
- package/dist/features/finalize/ports/outbound/FinalizeFileSystem.d.ts +14 -0
- package/dist/features/finalize/ports/outbound/FinalizeFileSystem.js +1 -0
- package/dist/features/finalize/ports/outbound/PlanRepo.d.ts +21 -0
- package/dist/features/finalize/ports/outbound/PlanRepo.js +1 -0
- package/dist/features/install/adapters/inbound/CliInstallHandler.d.ts +8 -0
- package/dist/features/install/adapters/inbound/CliInstallHandler.js +54 -0
- package/dist/features/install/adapters/outbound/NodeInstallSource.d.ts +7 -0
- package/dist/features/install/adapters/outbound/NodeInstallSource.js +24 -0
- package/dist/features/install/adapters/outbound/NodeInstallTargetFs.d.ts +7 -0
- package/dist/features/install/adapters/outbound/NodeInstallTargetFs.js +30 -0
- package/dist/features/install/application/InstallRules.d.ts +10 -0
- package/dist/features/install/application/InstallRules.js +73 -0
- package/dist/features/install/domain/InstallPlan.d.ts +27 -0
- package/dist/features/install/domain/InstallPlan.js +168 -0
- package/dist/features/install/domain/InstallResult.d.ts +23 -0
- package/dist/features/install/domain/InstallResult.js +1 -0
- package/dist/features/install/domain/InstallTarget.d.ts +6 -0
- package/dist/features/install/domain/InstallTarget.js +7 -0
- package/dist/features/install/domain/ManagedBlock.d.ts +3 -0
- package/dist/features/install/domain/ManagedBlock.js +20 -0
- package/dist/features/install/domain/RuleManifest.d.ts +17 -0
- package/dist/features/install/domain/RuleManifest.js +69 -0
- package/dist/features/install/domain/SettingsMerge.d.ts +5 -0
- package/dist/features/install/domain/SettingsMerge.js +43 -0
- package/dist/features/install/ports/inbound/InstallCommand.d.ts +10 -0
- package/dist/features/install/ports/inbound/InstallCommand.js +1 -0
- package/dist/features/install/ports/outbound/InstallSource.d.ts +4 -0
- package/dist/features/install/ports/outbound/InstallSource.js +1 -0
- package/dist/features/install/ports/outbound/InstallTargetFs.d.ts +6 -0
- package/dist/features/install/ports/outbound/InstallTargetFs.js +1 -0
- package/dist/features/lint/adapters/inbound/CliLintHandler.d.ts +8 -0
- package/dist/features/lint/adapters/inbound/CliLintHandler.js +61 -0
- package/dist/features/lint/adapters/outbound/NodeLintFileReader.d.ts +7 -0
- package/dist/features/lint/adapters/outbound/NodeLintFileReader.js +165 -0
- package/dist/features/lint/application/RunLint.d.ts +10 -0
- package/dist/features/lint/application/RunLint.js +100 -0
- package/dist/features/lint/domain/Diagnostic.d.ts +1 -0
- package/dist/features/lint/domain/Diagnostic.js +2 -0
- package/dist/features/lint/domain/Record.d.ts +1 -0
- package/dist/features/lint/domain/Record.js +5 -0
- package/dist/features/lint/domain/Rules.d.ts +1 -0
- package/dist/features/lint/domain/Rules.js +2 -0
- package/dist/features/lint/domain/SpecParser.d.ts +1 -0
- package/dist/features/lint/domain/SpecParser.js +2 -0
- package/dist/features/lint/ports/inbound/LintCommand.d.ts +4 -0
- package/dist/features/lint/ports/inbound/LintCommand.js +1 -0
- package/dist/features/lint/ports/outbound/LintConfigPort.d.ts +4 -0
- package/dist/features/lint/ports/outbound/LintConfigPort.js +1 -0
- package/dist/features/lint/ports/outbound/LintFileReader.d.ts +10 -0
- package/dist/features/lint/ports/outbound/LintFileReader.js +1 -0
- package/dist/features/plan/adapters/inbound/CliPlanShowHandler.d.ts +8 -0
- package/dist/features/plan/adapters/inbound/CliPlanShowHandler.js +73 -0
- package/dist/features/plan/adapters/outbound/NodePlanReader.d.ts +7 -0
- package/dist/features/plan/adapters/outbound/NodePlanReader.js +68 -0
- package/dist/features/plan/application/ShowPlan.d.ts +7 -0
- package/dist/features/plan/application/ShowPlan.js +4 -0
- package/dist/features/plan/ports/inbound/PlanShowCommand.d.ts +7 -0
- package/dist/features/plan/ports/inbound/PlanShowCommand.js +1 -0
- package/dist/features/plan/ports/outbound/PlanConfigPort.d.ts +4 -0
- package/dist/features/plan/ports/outbound/PlanConfigPort.js +1 -0
- package/dist/features/plan/ports/outbound/PlanReader.d.ts +19 -0
- package/dist/features/plan/ports/outbound/PlanReader.js +1 -0
- package/dist/features/ready/adapters/inbound/CliReadyHandler.d.ts +8 -0
- package/dist/features/ready/adapters/inbound/CliReadyHandler.js +79 -0
- package/dist/features/ready/adapters/outbound/ChildProcessReadyGit.d.ts +9 -0
- package/dist/features/ready/adapters/outbound/ChildProcessReadyGit.js +113 -0
- package/dist/features/ready/adapters/outbound/NodeReadyFileSystem.d.ts +8 -0
- package/dist/features/ready/adapters/outbound/NodeReadyFileSystem.js +159 -0
- package/dist/features/ready/application/RunReady.d.ts +16 -0
- package/dist/features/ready/application/RunReady.js +572 -0
- package/dist/features/ready/domain/AggregatedRules.d.ts +16 -0
- package/dist/features/ready/domain/AggregatedRules.js +42 -0
- package/dist/features/ready/domain/MarkerParser.d.ts +17 -0
- package/dist/features/ready/domain/MarkerParser.js +108 -0
- package/dist/features/ready/domain/PartitionResolver.d.ts +1 -0
- package/dist/features/ready/domain/PartitionResolver.js +5 -0
- package/dist/features/ready/domain/ReadyInput.d.ts +6 -0
- package/dist/features/ready/domain/ReadyInput.js +1 -0
- package/dist/features/ready/domain/ReadyViolation.d.ts +38 -0
- package/dist/features/ready/domain/ReadyViolation.js +19 -0
- package/dist/features/ready/domain/Rules.d.ts +22 -0
- package/dist/features/ready/domain/Rules.js +243 -0
- package/dist/features/ready/domain/SpecDiff.d.ts +33 -0
- package/dist/features/ready/domain/SpecDiff.js +321 -0
- package/dist/features/ready/ports/inbound/ReadyCommand.d.ts +4 -0
- package/dist/features/ready/ports/inbound/ReadyCommand.js +1 -0
- package/dist/features/ready/ports/outbound/ReadyConfigPort.d.ts +4 -0
- package/dist/features/ready/ports/outbound/ReadyConfigPort.js +1 -0
- package/dist/features/ready/ports/outbound/ReadyFileReader.d.ts +12 -0
- package/dist/features/ready/ports/outbound/ReadyFileReader.js +1 -0
- package/dist/features/ready/ports/outbound/ReadyGitPort.d.ts +10 -0
- package/dist/features/ready/ports/outbound/ReadyGitPort.js +5 -0
- package/dist/features/record/adapters/inbound/CliRecordHandler.d.ts +10 -0
- package/dist/features/record/adapters/inbound/CliRecordHandler.js +111 -0
- package/dist/features/record/adapters/outbound/NodeRecordFileSystem.d.ts +9 -0
- package/dist/features/record/adapters/outbound/NodeRecordFileSystem.js +152 -0
- package/dist/features/record/application/AddRecord.d.ts +11 -0
- package/dist/features/record/application/AddRecord.js +84 -0
- package/dist/features/record/application/GetRecord.d.ts +8 -0
- package/dist/features/record/application/GetRecord.js +22 -0
- package/dist/features/record/application/ListRecords.d.ts +9 -0
- package/dist/features/record/application/ListRecords.js +24 -0
- package/dist/features/record/application/SetRecord.d.ts +11 -0
- package/dist/features/record/application/SetRecord.js +68 -0
- package/dist/features/record/domain/RecordBody.d.ts +12 -0
- package/dist/features/record/domain/RecordBody.js +66 -0
- package/dist/features/record/domain/RecordPartition.d.ts +1 -0
- package/dist/features/record/domain/RecordPartition.js +7 -0
- package/dist/features/record/domain/RecordSlice.d.ts +7 -0
- package/dist/features/record/domain/RecordSlice.js +1 -0
- package/dist/features/record/domain/RecordSummary.d.ts +11 -0
- package/dist/features/record/domain/RecordSummary.js +13 -0
- package/dist/features/record/domain/RecordWrite.d.ts +14 -0
- package/dist/features/record/domain/RecordWrite.js +8 -0
- package/dist/features/record/ports/inbound/RecordCommand.d.ts +19 -0
- package/dist/features/record/ports/inbound/RecordCommand.js +1 -0
- package/dist/features/record/ports/outbound/RecordConfigPort.d.ts +4 -0
- package/dist/features/record/ports/outbound/RecordConfigPort.js +1 -0
- package/dist/features/record/ports/outbound/RecordFileReader.d.ts +10 -0
- package/dist/features/record/ports/outbound/RecordFileReader.js +1 -0
- package/dist/features/record/ports/outbound/RecordFileWriter.d.ts +6 -0
- package/dist/features/record/ports/outbound/RecordFileWriter.js +1 -0
- package/dist/features/refresh/adapters/inbound/CliRefreshHandler.d.ts +8 -0
- package/dist/features/refresh/adapters/inbound/CliRefreshHandler.js +24 -0
- package/dist/features/refresh/adapters/outbound/ChildProcessRefreshGit.d.ts +8 -0
- package/dist/features/refresh/adapters/outbound/ChildProcessRefreshGit.js +118 -0
- package/dist/features/refresh/adapters/outbound/NodeRefreshFileReader.d.ts +7 -0
- package/dist/features/refresh/adapters/outbound/NodeRefreshFileReader.js +44 -0
- package/dist/features/refresh/adapters/outbound/SystemRefreshClock.d.ts +4 -0
- package/dist/features/refresh/adapters/outbound/SystemRefreshClock.js +5 -0
- package/dist/features/refresh/application/BuildRefreshStubs.d.ts +25 -0
- package/dist/features/refresh/application/BuildRefreshStubs.js +43 -0
- package/dist/features/refresh/domain/DiffStubs.d.ts +24 -0
- package/dist/features/refresh/domain/DiffStubs.js +81 -0
- package/dist/features/refresh/domain/Footprint.d.ts +14 -0
- package/dist/features/refresh/domain/Footprint.js +45 -0
- package/dist/features/refresh/ports/inbound/RefreshCommand.d.ts +4 -0
- package/dist/features/refresh/ports/inbound/RefreshCommand.js +1 -0
- package/dist/features/refresh/ports/outbound/RefreshClockPort.d.ts +3 -0
- package/dist/features/refresh/ports/outbound/RefreshClockPort.js +1 -0
- package/dist/features/refresh/ports/outbound/RefreshConfigPort.d.ts +4 -0
- package/dist/features/refresh/ports/outbound/RefreshConfigPort.js +1 -0
- package/dist/features/refresh/ports/outbound/RefreshGitPort.d.ts +7 -0
- package/dist/features/refresh/ports/outbound/RefreshGitPort.js +1 -0
- package/dist/features/refresh/ports/outbound/RefreshSpecPort.d.ts +9 -0
- package/dist/features/refresh/ports/outbound/RefreshSpecPort.js +1 -0
- package/dist/features/report/adapters/inbound/CliReportHandler.d.ts +8 -0
- package/dist/features/report/adapters/inbound/CliReportHandler.js +35 -0
- package/dist/features/report/adapters/outbound/NodeReportFileSystem.d.ts +7 -0
- package/dist/features/report/adapters/outbound/NodeReportFileSystem.js +128 -0
- package/dist/features/report/application/RunReport.d.ts +19 -0
- package/dist/features/report/application/RunReport.js +161 -0
- package/dist/features/report/ports/inbound/ReportCommand.d.ts +8 -0
- package/dist/features/report/ports/inbound/ReportCommand.js +1 -0
- package/dist/features/report/ports/outbound/ReportConfigPort.d.ts +4 -0
- package/dist/features/report/ports/outbound/ReportConfigPort.js +1 -0
- package/dist/features/report/ports/outbound/ReportFileReader.d.ts +7 -0
- package/dist/features/report/ports/outbound/ReportFileReader.js +1 -0
- package/dist/features/token/adapters/inbound/CliTokenHandler.d.ts +8 -0
- package/dist/features/token/adapters/inbound/CliTokenHandler.js +53 -0
- package/dist/features/token/adapters/outbound/ChildProcessTokenGit.d.ts +8 -0
- package/dist/features/token/adapters/outbound/ChildProcessTokenGit.js +112 -0
- package/dist/features/token/adapters/outbound/NodeTokenConfigReader.d.ts +5 -0
- package/dist/features/token/adapters/outbound/NodeTokenConfigReader.js +41 -0
- package/dist/features/token/application/ComputeToken.d.ts +18 -0
- package/dist/features/token/application/ComputeToken.js +27 -0
- package/dist/features/token/ports/inbound/TokenCommand.d.ts +4 -0
- package/dist/features/token/ports/inbound/TokenCommand.js +1 -0
- package/dist/features/token/ports/outbound/TokenConfigPort.d.ts +4 -0
- package/dist/features/token/ports/outbound/TokenConfigPort.js +1 -0
- package/dist/features/token/ports/outbound/TokenGitPort.d.ts +7 -0
- package/dist/features/token/ports/outbound/TokenGitPort.js +1 -0
- package/dist/shared/domain/AgentBlocklist.d.ts +5 -0
- package/dist/shared/domain/AgentBlocklist.js +28 -0
- package/dist/shared/domain/BoundaryReachability.d.ts +5 -0
- package/dist/shared/domain/BoundaryReachability.js +71 -0
- package/dist/shared/domain/CheckOutcome.d.ts +10 -0
- package/dist/shared/domain/CheckOutcome.js +7 -0
- package/dist/shared/domain/CliOutput.d.ts +10 -0
- package/dist/shared/domain/CliOutput.js +29 -0
- package/dist/shared/domain/Config.d.ts +29 -0
- package/dist/shared/domain/Config.js +201 -0
- package/dist/shared/domain/DiagnosticRegistry.d.ts +8 -0
- package/dist/shared/domain/DiagnosticRegistry.js +71 -0
- package/dist/shared/domain/Errors.d.ts +12 -0
- package/dist/shared/domain/Errors.js +23 -0
- package/dist/shared/domain/GlobMatch.d.ts +2 -0
- package/dist/shared/domain/GlobMatch.js +58 -0
- package/dist/shared/domain/LintReport.d.ts +16 -0
- package/dist/shared/domain/LintReport.js +11 -0
- package/dist/shared/domain/LintRules.d.ts +67 -0
- package/dist/shared/domain/LintRules.js +956 -0
- package/dist/shared/domain/PartitionGrammar.d.ts +4 -0
- package/dist/shared/domain/PartitionGrammar.js +16 -0
- package/dist/shared/domain/PlanFile.d.ts +28 -0
- package/dist/shared/domain/PlanFile.js +112 -0
- package/dist/shared/domain/Scope.d.ts +1 -0
- package/dist/shared/domain/Scope.js +3 -0
- package/dist/shared/domain/SpecApprovalRewrite.d.ts +23 -0
- package/dist/shared/domain/SpecApprovalRewrite.js +254 -0
- package/dist/shared/domain/SpecBlocks.d.ts +12 -0
- package/dist/shared/domain/SpecBlocks.js +96 -0
- package/dist/shared/domain/SpecRecord.d.ts +17 -0
- package/dist/shared/domain/SpecRecord.js +208 -0
- package/dist/shared/domain/TemplateFieldMetadata.d.ts +2 -0
- package/dist/shared/domain/TemplateFieldMetadata.js +177 -0
- package/dist/shared/domain/Token.d.ts +2 -0
- package/dist/shared/domain/Token.js +5 -0
- package/dist/shared/domain/WeaselWords.d.ts +3 -0
- package/dist/shared/domain/WeaselWords.js +32 -0
- package/package.json +71 -0
- package/rules/enforcement_registry.md +126 -0
- package/rules/hooks/sdd-lint-reminder.sh +33 -0
- package/rules/hooks/sdd-spec-read-guard.sh +73 -0
- package/rules/manifest.json +15 -0
- package/rules/review-sdd.md +9 -0
- package/rules/sdd-cli-usage.md +91 -0
- package/rules/skills/spec-driven-development/SKILL.md +554 -0
- package/rules/skills/spec-driven-development/data/weasel-words.json +22 -0
- package/rules/spec-driven-development.md +69 -0
- package/rules/tdd-sdd.md +127 -0
- package/rules/workflow-sdd.md +56 -0
- package/schema/sdd.config.schema.json +104 -0
package/README.ru.md
ADDED
|
@@ -0,0 +1,1046 @@
|
|
|
1
|
+
# `agent-sdd`
|
|
2
|
+
|
|
3
|
+
[](https://github.com/cyberash-dev/sdd-cli/actions/workflows/ci.yml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](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
|
+
истины, и любое расхождение между кодом и спецификацией является багом.
|