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
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { parseAllDocuments, parseDocument } from "yaml";
|
|
2
|
+
export const NORMATIVE_TEMPLATES = new Set([
|
|
3
|
+
"Behavior",
|
|
4
|
+
"Invariant",
|
|
5
|
+
"Contract",
|
|
6
|
+
"Scenario",
|
|
7
|
+
"NFR",
|
|
8
|
+
"Constraint",
|
|
9
|
+
"Policy",
|
|
10
|
+
"Migration",
|
|
11
|
+
"Delta",
|
|
12
|
+
"GeneratedArtifact",
|
|
13
|
+
"ExternalDependency",
|
|
14
|
+
"LocalizationContract",
|
|
15
|
+
"Surface",
|
|
16
|
+
]);
|
|
17
|
+
export const VALID_LIFECYCLE_STATUS = new Set([
|
|
18
|
+
"draft",
|
|
19
|
+
"proposed",
|
|
20
|
+
"approved",
|
|
21
|
+
"deprecated",
|
|
22
|
+
"removed",
|
|
23
|
+
]);
|
|
24
|
+
const ALIAS_FIELDS = [
|
|
25
|
+
"negative_test_obligations",
|
|
26
|
+
"tests_pre",
|
|
27
|
+
"tests_during",
|
|
28
|
+
"tests_post",
|
|
29
|
+
"tests_old_behavior",
|
|
30
|
+
"tests_new_behavior",
|
|
31
|
+
];
|
|
32
|
+
export function lintRecordsFromMarkdown(file, markdown) {
|
|
33
|
+
const out = [];
|
|
34
|
+
for (const fence of yamlFences(markdown)) {
|
|
35
|
+
out.push(...lintRecordsFromFence(file, fence));
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function yamlFences(markdown) {
|
|
40
|
+
const lines = markdown.split(/\r?\n/);
|
|
41
|
+
const fences = [];
|
|
42
|
+
let inFence = false;
|
|
43
|
+
let buffer = [];
|
|
44
|
+
let startLine = 0;
|
|
45
|
+
for (let i = 0; i < lines.length; i++) {
|
|
46
|
+
const line = lines[i];
|
|
47
|
+
if (!inFence && /^```yaml\s*$/.test(line)) {
|
|
48
|
+
inFence = true;
|
|
49
|
+
buffer = [];
|
|
50
|
+
startLine = i + 2;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (inFence && /^```\s*$/.test(line)) {
|
|
54
|
+
fences.push({ raw: buffer.join("\n"), startLine });
|
|
55
|
+
inFence = false;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (inFence) {
|
|
59
|
+
buffer.push(line);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return fences;
|
|
63
|
+
}
|
|
64
|
+
function lintRecordsFromFence(file, fence) {
|
|
65
|
+
const hasSeparator = fence.raw.split(/\r?\n/).some((l) => l === "---");
|
|
66
|
+
if (hasSeparator) {
|
|
67
|
+
const docs = parseAllDocuments(fence.raw, { prettyErrors: false });
|
|
68
|
+
const out = [];
|
|
69
|
+
for (const doc of docs) {
|
|
70
|
+
if (doc.errors.length > 0) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const value = doc.toJS();
|
|
74
|
+
if (!isObject(value)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const rec = recordFromObject(file, value, fence.startLine + (doc.range?.[0] ?? 0));
|
|
78
|
+
if (rec !== null) {
|
|
79
|
+
out.push(rec);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
const doc = parseDocument(fence.raw, { prettyErrors: false });
|
|
85
|
+
if (doc.errors.length > 0) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
const value = doc.toJS();
|
|
89
|
+
if (Array.isArray(value)) {
|
|
90
|
+
const out = [];
|
|
91
|
+
const fenceLines = fence.raw.split(/\r?\n/);
|
|
92
|
+
let cursor = 0;
|
|
93
|
+
for (const item of value) {
|
|
94
|
+
if (!isObject(item)) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const id = typeof item.id === "string" ? item.id : null;
|
|
98
|
+
let lineWithin = cursor;
|
|
99
|
+
if (id !== null) {
|
|
100
|
+
for (let j = cursor; j < fenceLines.length; j++) {
|
|
101
|
+
if (new RegExp(`^-\\s+id:\\s*${escapeRe(id)}\\b`).test(fenceLines[j])) {
|
|
102
|
+
lineWithin = j;
|
|
103
|
+
cursor = j + 1;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const rec = recordFromObject(file, item, fence.startLine + lineWithin);
|
|
109
|
+
if (rec !== null) {
|
|
110
|
+
out.push(rec);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
if (isObject(value)) {
|
|
116
|
+
const rec = recordFromObject(file, value, fence.startLine);
|
|
117
|
+
return rec === null ? [] : [rec];
|
|
118
|
+
}
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
function recordFromObject(file, value, line) {
|
|
122
|
+
const id = typeof value.id === "string" ? value.id : null;
|
|
123
|
+
if (id === null) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const template = pickTemplate(value);
|
|
127
|
+
const lifecycleStatus = pickLifecycleStatus(value);
|
|
128
|
+
const approvalRecord = pickApprovalRecord(value);
|
|
129
|
+
const testObligations = stringArray(value.test_obligations);
|
|
130
|
+
const hasAliasedObligations = ALIAS_FIELDS.some((k) => value[k] !== undefined) ||
|
|
131
|
+
hasSingularTestObligation(value);
|
|
132
|
+
return {
|
|
133
|
+
id,
|
|
134
|
+
template,
|
|
135
|
+
lifecycleStatus,
|
|
136
|
+
approvalRecord,
|
|
137
|
+
testObligations,
|
|
138
|
+
hasAliasedObligations,
|
|
139
|
+
parsed: value,
|
|
140
|
+
file,
|
|
141
|
+
line,
|
|
142
|
+
rawBlock: "",
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function hasSingularTestObligation(value) {
|
|
146
|
+
const v = value.test_obligation;
|
|
147
|
+
if (isObject(v)) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
if (typeof v === "string" && v.startsWith("not_applicable")) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
function pickTemplate(value) {
|
|
156
|
+
const t = value.template ?? value.type;
|
|
157
|
+
return typeof t === "string" ? t : null;
|
|
158
|
+
}
|
|
159
|
+
function pickLifecycleStatus(value) {
|
|
160
|
+
const flat = value["lifecycle.status"];
|
|
161
|
+
if (typeof flat === "string") {
|
|
162
|
+
return flat;
|
|
163
|
+
}
|
|
164
|
+
const nested = value.lifecycle;
|
|
165
|
+
if (isObject(nested) && typeof nested.status === "string") {
|
|
166
|
+
return nested.status;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
function pickApprovalRecord(value) {
|
|
171
|
+
const top = value.approval_record;
|
|
172
|
+
const fromTop = describeApprovalRecord(top);
|
|
173
|
+
if (fromTop !== null) {
|
|
174
|
+
return fromTop;
|
|
175
|
+
}
|
|
176
|
+
const lifecycle = value.lifecycle;
|
|
177
|
+
if (isObject(lifecycle)) {
|
|
178
|
+
return describeApprovalRecord(lifecycle.approval_record);
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
function describeApprovalRecord(v) {
|
|
183
|
+
if (v === undefined) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
if (typeof v === "string") {
|
|
187
|
+
return v;
|
|
188
|
+
}
|
|
189
|
+
if (isObject(v)) {
|
|
190
|
+
const tag = typeof v.approver_identity === "string"
|
|
191
|
+
? `obj:${v.approver_identity}`
|
|
192
|
+
: "obj:unknown";
|
|
193
|
+
return tag;
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
function stringArray(value) {
|
|
198
|
+
if (!Array.isArray(value)) {
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
return value.filter((v) => typeof v === "string");
|
|
202
|
+
}
|
|
203
|
+
function isObject(value) {
|
|
204
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
205
|
+
}
|
|
206
|
+
function escapeRe(value) {
|
|
207
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
208
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* IS_NORMATIVE table per typed normative template: which fields the
|
|
3
|
+
* field-aware weasel pass treats as normative (`true`). Structural,
|
|
4
|
+
* descriptive and test-design fields are `false`; unknown templates
|
|
5
|
+
* default `false` so modal weasel never blocks where we are not certain.
|
|
6
|
+
*/
|
|
7
|
+
export const IS_NORMATIVE = {
|
|
8
|
+
Behavior: {
|
|
9
|
+
title: false,
|
|
10
|
+
notes: false,
|
|
11
|
+
test_obligation: false,
|
|
12
|
+
given: true,
|
|
13
|
+
when: true,
|
|
14
|
+
then: true,
|
|
15
|
+
negative_cases: true,
|
|
16
|
+
out_of_scope: true,
|
|
17
|
+
applicability: true,
|
|
18
|
+
concurrency_model: true,
|
|
19
|
+
data_scope: true,
|
|
20
|
+
policy_refs: true,
|
|
21
|
+
},
|
|
22
|
+
Contract: {
|
|
23
|
+
title: false,
|
|
24
|
+
notes: false,
|
|
25
|
+
test_obligation: false,
|
|
26
|
+
test_obligations: false,
|
|
27
|
+
surface_ref: true,
|
|
28
|
+
schema: true,
|
|
29
|
+
preconditions: true,
|
|
30
|
+
postconditions: true,
|
|
31
|
+
external_identifiers: true,
|
|
32
|
+
compatibility_rules: true,
|
|
33
|
+
error_taxonomy: true,
|
|
34
|
+
applicability: true,
|
|
35
|
+
concurrency_model: true,
|
|
36
|
+
data_scope: true,
|
|
37
|
+
policy_refs: true,
|
|
38
|
+
},
|
|
39
|
+
Invariant: {
|
|
40
|
+
title: false,
|
|
41
|
+
notes: false,
|
|
42
|
+
test_obligation: false,
|
|
43
|
+
always: true,
|
|
44
|
+
never: true,
|
|
45
|
+
when: true,
|
|
46
|
+
then: true,
|
|
47
|
+
scope: true,
|
|
48
|
+
evidence: true,
|
|
49
|
+
stability: true,
|
|
50
|
+
negative_cases: true,
|
|
51
|
+
out_of_scope: true,
|
|
52
|
+
applicability: true,
|
|
53
|
+
concurrency_model: true,
|
|
54
|
+
data_scope: true,
|
|
55
|
+
policy_refs: true,
|
|
56
|
+
},
|
|
57
|
+
Scenario: {
|
|
58
|
+
title: false,
|
|
59
|
+
notes: false,
|
|
60
|
+
test_obligation: false,
|
|
61
|
+
given: true,
|
|
62
|
+
when: true,
|
|
63
|
+
then: true,
|
|
64
|
+
applicability: true,
|
|
65
|
+
data_scope: true,
|
|
66
|
+
},
|
|
67
|
+
NFR: {
|
|
68
|
+
title: false,
|
|
69
|
+
notes: false,
|
|
70
|
+
test_obligation: false,
|
|
71
|
+
metric: true,
|
|
72
|
+
target: true,
|
|
73
|
+
verification_obligation: true,
|
|
74
|
+
applicability: true,
|
|
75
|
+
data_scope: true,
|
|
76
|
+
policy_refs: true,
|
|
77
|
+
},
|
|
78
|
+
Constraint: {
|
|
79
|
+
title: false,
|
|
80
|
+
notes: false,
|
|
81
|
+
test_obligations: false,
|
|
82
|
+
rule: true,
|
|
83
|
+
rationale: true,
|
|
84
|
+
scope: true,
|
|
85
|
+
applicability: true,
|
|
86
|
+
data_scope: true,
|
|
87
|
+
policy_refs: true,
|
|
88
|
+
},
|
|
89
|
+
Policy: {
|
|
90
|
+
title: false,
|
|
91
|
+
notes: false,
|
|
92
|
+
test_obligation: false,
|
|
93
|
+
rule: true,
|
|
94
|
+
rationale: true,
|
|
95
|
+
scope: true,
|
|
96
|
+
applicability: true,
|
|
97
|
+
data_scope: true,
|
|
98
|
+
},
|
|
99
|
+
Migration: {
|
|
100
|
+
title: false,
|
|
101
|
+
notes: false,
|
|
102
|
+
test_obligation: false,
|
|
103
|
+
direction: true,
|
|
104
|
+
mode: true,
|
|
105
|
+
runtime_state: true,
|
|
106
|
+
enforcement_stage: true,
|
|
107
|
+
target_ids: true,
|
|
108
|
+
baseline_version: true,
|
|
109
|
+
partition_slice: true,
|
|
110
|
+
applicability: true,
|
|
111
|
+
data_scope: true,
|
|
112
|
+
policy_refs: true,
|
|
113
|
+
},
|
|
114
|
+
Delta: {
|
|
115
|
+
title: false,
|
|
116
|
+
notes: false,
|
|
117
|
+
test_obligation: false,
|
|
118
|
+
kind: true,
|
|
119
|
+
compatibility_action: true,
|
|
120
|
+
target_ids: true,
|
|
121
|
+
baseline_version: true,
|
|
122
|
+
tests_old_behavior: false,
|
|
123
|
+
tests_new_behavior: false,
|
|
124
|
+
applicability: true,
|
|
125
|
+
data_scope: true,
|
|
126
|
+
},
|
|
127
|
+
GeneratedArtifact: {
|
|
128
|
+
title: false,
|
|
129
|
+
notes: false,
|
|
130
|
+
test_obligation: false,
|
|
131
|
+
generator: true,
|
|
132
|
+
inputs: true,
|
|
133
|
+
outputs: true,
|
|
134
|
+
published_surface: true,
|
|
135
|
+
surface_ref: true,
|
|
136
|
+
applicability: true,
|
|
137
|
+
data_scope: true,
|
|
138
|
+
},
|
|
139
|
+
ExternalDependency: {
|
|
140
|
+
title: false,
|
|
141
|
+
notes: false,
|
|
142
|
+
test_obligation: false,
|
|
143
|
+
provider: true,
|
|
144
|
+
interface: true,
|
|
145
|
+
failure_modes: true,
|
|
146
|
+
fallback_policy: true,
|
|
147
|
+
applicability: true,
|
|
148
|
+
data_scope: true,
|
|
149
|
+
policy_refs: true,
|
|
150
|
+
},
|
|
151
|
+
LocalizationContract: {
|
|
152
|
+
title: false,
|
|
153
|
+
notes: false,
|
|
154
|
+
test_obligation: false,
|
|
155
|
+
text_id: true,
|
|
156
|
+
fallback_locale: true,
|
|
157
|
+
contract: true,
|
|
158
|
+
applicability: true,
|
|
159
|
+
},
|
|
160
|
+
Surface: {
|
|
161
|
+
name: false,
|
|
162
|
+
notes: false,
|
|
163
|
+
title: false,
|
|
164
|
+
test_obligation: false,
|
|
165
|
+
test_obligations: false,
|
|
166
|
+
version: true,
|
|
167
|
+
boundary_type: true,
|
|
168
|
+
members: true,
|
|
169
|
+
consumer_compat_policy: true,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
export function isFieldNormative(template, field) {
|
|
173
|
+
if (template === null || template === undefined) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
return IS_NORMATIVE[template]?.[field] === true;
|
|
177
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Source of truth for SDD weasel-word detection (CTR-016 / SUR-009):
|
|
3
|
+
* `absolute` words trigger inside any normative section; `modal_in_normative`
|
|
4
|
+
* words only inside fields whose IS_NORMATIVE entry is `true`.
|
|
5
|
+
*/
|
|
6
|
+
export const WEASEL_ABSOLUTE = [
|
|
7
|
+
"возможно",
|
|
8
|
+
"вероятно",
|
|
9
|
+
"обычно",
|
|
10
|
+
"as a rule",
|
|
11
|
+
"etc.",
|
|
12
|
+
"and so on",
|
|
13
|
+
"should usually",
|
|
14
|
+
"similar to",
|
|
15
|
+
"approximately",
|
|
16
|
+
"best-effort",
|
|
17
|
+
"best effort",
|
|
18
|
+
"informally",
|
|
19
|
+
];
|
|
20
|
+
export const WEASEL_MODAL_IN_NORMATIVE = [
|
|
21
|
+
"may be",
|
|
22
|
+
"might be",
|
|
23
|
+
];
|
|
24
|
+
/*
|
|
25
|
+
* Backward-compatible aggregate used by the legacy single-pass weaselFindings
|
|
26
|
+
* caller. P0.5 splits this into two passes; the union is the conservative
|
|
27
|
+
* upper bound until field-aware detection lands.
|
|
28
|
+
*/
|
|
29
|
+
export const WEASEL_WORDS = [
|
|
30
|
+
...WEASEL_ABSOLUTE,
|
|
31
|
+
...WEASEL_MODAL_IN_NORMATIVE,
|
|
32
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-sdd",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Spec-Driven Development helper CLI: freshness tokens, scope drift stubs, spec-lint, approval ceremony.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"spec-driven-development",
|
|
7
|
+
"sdd",
|
|
8
|
+
"cli",
|
|
9
|
+
"spec-lint",
|
|
10
|
+
"freshness-token",
|
|
11
|
+
"baseline"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "cyberash <cyberash-dev@yandex.com>",
|
|
15
|
+
"homepage": "https://github.com/cyberash-dev/sdd-cli#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/cyberash-dev/sdd-cli.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/cyberash-dev/sdd-cli/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"prettier": "@cyberash-dev/dev-tooling/prettier.base.json",
|
|
25
|
+
"bin": {
|
|
26
|
+
"sdd": "dist/cli.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"schema",
|
|
31
|
+
"rules",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/cli.d.ts",
|
|
38
|
+
"default": "./dist/cli.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "rm -rf dist && tsc && chmod +x dist/cli.js",
|
|
49
|
+
"test": "npm run test:unit && npm run test:integration",
|
|
50
|
+
"test:unit": "node --import tsx --test tests/unit/*.test.ts",
|
|
51
|
+
"test:integration": "node --import tsx --test tests/integration/*.test.ts",
|
|
52
|
+
"tsc": "tsc --noEmit",
|
|
53
|
+
"lint": "eslint src",
|
|
54
|
+
"lint:fix": "eslint src --fix",
|
|
55
|
+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
56
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
57
|
+
"lint:spec": "node dist/cli.js lint",
|
|
58
|
+
"prepublishOnly": "npm run tsc && npm test && npm run build"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"yaml": "^2.7.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@cyberash-dev/dev-tooling": "^5.0.0",
|
|
65
|
+
"@types/node": "^22.10.5",
|
|
66
|
+
"eslint": "^9.39.4",
|
|
67
|
+
"prettier": "^3.8.3",
|
|
68
|
+
"tsx": "^4.19.2",
|
|
69
|
+
"typescript": "^5.7.2"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# SDD Enforcement Registry
|
|
2
|
+
|
|
3
|
+
Single source of truth for «which SDD requirement is closed by which channel». Every MUST and every stop-condition from `spec-driven-development.md` and `SKILL.md` has exactly one row here. Adding a normative requirement without a row is invalid; adding a CLI diagnostic without a row is invalid.
|
|
4
|
+
|
|
5
|
+
## Compatibility metadata
|
|
6
|
+
|
|
7
|
+
| Field | Value |
|
|
8
|
+
|---|---|
|
|
9
|
+
| sdd_methodology_version | 1.0.0 |
|
|
10
|
+
| compatible_sdd_cli | >=1.0 <2.0 |
|
|
11
|
+
| diagnostic_surface | @cyberash/sdd-cli/diagnostics@1.0 |
|
|
12
|
+
|
|
13
|
+
## Schema
|
|
14
|
+
|
|
15
|
+
Each row carries:
|
|
16
|
+
- `id` — `ENF-NNN[A-Z]?` (suffix letter when split via `parent_id`).
|
|
17
|
+
- `parent_id` — optional; links rows that decompose one requirement.
|
|
18
|
+
- `requirement` — short imperative statement (one sentence).
|
|
19
|
+
- `enforcement_class` — one of 10 semantic classes (see below).
|
|
20
|
+
- `executor` — one of `sdd lint | sdd check | sdd refresh | sdd ready | sdd finalize | sdd report | sdd doctor | agent | human`.
|
|
21
|
+
- `gate` — `baseline-valid | spec-valid | implementation-valid` (omitted for process-class rows).
|
|
22
|
+
- `diagnostic_id` — required for lint/ready/doctor rows with `maturity=implemented`. Contains the exact string the CLI emits and `sdd doctor` reconciles against `DiagnosticRegistry`: `sdd:*` for lint rules, snake-case for ready violation kinds, and the doctor drift kinds (`version_mismatch`/`missing_diagnostic`/`stale_diagnostic`). A single cell may list several ids separated by ` | ` (escaped `\|`). Set to `—` when the row is verified by another channel and emits no doctor-tracked diagnostic: `report-evidence` rows (verified by the report integration tests, e.g. BEH-044..048) and the finalize graph-violation reason `proposed-references` (an envelope reason, not a `DiagnosticRegistry` member).
|
|
23
|
+
- `maturity` — `implemented | planned | out_of_scope` (omitted for process-class rows). Suffix `:hybrid` when the row mixes mechanical and human verdicts.
|
|
24
|
+
- `process_owner` — required when `executor ∈ {agent, human}`.
|
|
25
|
+
- `review_trigger` — required when `executor ∈ {agent, human}`; one of `PR merge | sprint close | release cut`.
|
|
26
|
+
|
|
27
|
+
### Enforcement classes
|
|
28
|
+
|
|
29
|
+
- `structural-lint` — single-record textual/typed check.
|
|
30
|
+
- `graph-consistency` — cross-record reference validation.
|
|
31
|
+
- `freshness` — comparison against recorded baseline.
|
|
32
|
+
- `aggregate-gate` — composite check across multiple sub-rules.
|
|
33
|
+
- `derived-versioning` — semver/diff classification computed from references.
|
|
34
|
+
- `derived-artifact` — structural diff over emitted artefact.
|
|
35
|
+
- `report-evidence` — produces machine-readable narrative for PR.
|
|
36
|
+
- `agent-halt` — agent reflex on closed stop-condition (term not in Glossary → Open-Q).
|
|
37
|
+
- `agent-judgment` — agent semantic classification (provider-owned → ExternalDependency).
|
|
38
|
+
- `human-review` — human-only gate (test oracle quality, debt content).
|
|
39
|
+
|
|
40
|
+
## Registry
|
|
41
|
+
|
|
42
|
+
| id | parent_id | requirement | enforcement_class | executor | gate | diagnostic_id | maturity | process_owner | review_trigger |
|
|
43
|
+
|----|-----------|-------------|-------------------|----------|------|---------------|----------|---------------|----------------|
|
|
44
|
+
| ENF-001 | — | Weasel words banned in normative sections; modal phrases in `is_normative=true` fields | structural-lint | sdd lint | spec-valid | sdd:weasel-word | implemented | — | — |
|
|
45
|
+
| ENF-002A | ENF-002 | Approved/deprecated/removed Surface referencing a non-approved member is flagged | graph-consistency | sdd ready | spec-valid | surface_unapproved_ref | implemented | — | — |
|
|
46
|
+
| ENF-002B | ENF-002 | Materialising `lifecycle.status=approved` via finalize refuses when the plan references still-proposed IDs (envelope reason `proposed-references`) | graph-consistency | sdd finalize | spec-valid | — | implemented | — | — |
|
|
47
|
+
| ENF-003 | — | Delta/Migration carries `baseline_version` | structural-lint | sdd lint | spec-valid | sdd:baseline-version-required | implemented | — | — |
|
|
48
|
+
| ENF-004A | ENF-004 | Major Surface bump computed via semver cascade (Policy / Invariant(contractual) → referencing Surface) | derived-versioning | sdd ready | spec-valid | surface_semver_cascade | implemented | — | — |
|
|
49
|
+
| ENF-004B | ENF-004 | Major-bump test oracle reviewed (input classes, negative oracle, assertion summary) | human-review | human | — | — | — | surface owner | PR merge |
|
|
50
|
+
| ENF-005 | — | Unknown Glossary term → agent raises Open-Q | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
51
|
+
| ENF-006 | — | Provider-owned behavior → agent classifies as ExternalDependency | agent-judgment | agent | — | — | — | PR author | PR merge |
|
|
52
|
+
| ENF-007A | ENF-007 | PR report lists closed Test obligations + tests | report-evidence | sdd report | implementation-valid | — | implemented | PR author | PR merge |
|
|
53
|
+
| ENF-007B | ENF-007 | PR report lists internal decisions (candidates for new Constraint/Policy/ASSUMPTION) | report-evidence | sdd report | implementation-valid | — | implemented | PR author | PR merge |
|
|
54
|
+
| ENF-007C | ENF-007 | PR report lists ASSUMPTIONs + `review_by` | report-evidence | sdd report | implementation-valid | — | implemented | PR author | PR merge |
|
|
55
|
+
| ENF-007D | ENF-007 | PR report lists remaining Open-Qs + non-block reason | report-evidence | sdd report | implementation-valid | — | implemented | PR author | PR merge |
|
|
56
|
+
| ENF-007E | ENF-007 | PR report shows debt budget delta vs previous PR | report-evidence | sdd report | implementation-valid | — | implemented:hybrid | PR author | PR merge |
|
|
57
|
+
| ENF-008 | — | Diagnostic rule IDs are stable public Surface (drift kinds: `version_mismatch`, `missing_diagnostic`, `stale_diagnostic`) | graph-consistency | sdd doctor | — | version_mismatch \| missing_diagnostic \| stale_diagnostic | implemented | — | release cut |
|
|
58
|
+
| ENF-009 | — | `deprecated` ⇒ `sunset_version` + `replacement_id` | structural-lint | sdd lint | spec-valid | sdd:deprecated-fields-required | implemented | — | — |
|
|
59
|
+
| ENF-010 | — | ASSUMPTION downgrade `blocking → advisory` requires `approval_record` (≠ agent identity) | structural-lint | sdd lint | spec-valid | sdd:assumption-downgrade-approval | implemented | — | — |
|
|
60
|
+
| ENF-011 | — | Partition declares `default_policy_set[]` (≥0, explicit) | structural-lint | sdd lint | spec-valid | sdd:partition-default-policy-set | implemented | — | — |
|
|
61
|
+
| ENF-012 | — | `GeneratedArtifact.published_surface=yes` ⇒ Surface-ref | structural-lint | sdd lint | spec-valid | sdd:generated-artifact-surface-ref | implemented | — | — |
|
|
62
|
+
| ENF-013 | — | Boundary Contract/Behavior references ≥1 Policy or carries explicit `Policy.override` | structural-lint | sdd lint | spec-valid | sdd:boundary-policy-ref | implemented | — | — |
|
|
63
|
+
| ENF-014 | — | `concurrency_model` present on boundary elements with required sub-fields | structural-lint | sdd lint | spec-valid | sdd:boundary-concurrency-model | implemented | — | — |
|
|
64
|
+
| ENF-015 | — | `applicability` present (or `invariant_to_all_axes: true`) | structural-lint | sdd lint | spec-valid | sdd:applicability-required | implemented | — | — |
|
|
65
|
+
| ENF-016 | — | `data_scope` present on persistent-state elements | structural-lint | sdd lint | spec-valid | sdd:data-scope-required | implemented | — | — |
|
|
66
|
+
| ENF-017 | — | Migration `runtime_state` ↔ `enforcement_stage` marker consistency | structural-lint | sdd lint | spec-valid | sdd:migration-enforcement-stage | implemented | — | — |
|
|
67
|
+
| ENF-018 | — | Cross-partition Migration uses `partition_slice[]` + `coordinator_id` | structural-lint | sdd lint | spec-valid | sdd:migration-cross-partition | implemented | — | — |
|
|
68
|
+
| ENF-019 | — | `GeneratedArtifact(published_surface=yes)` structural-breaking diff ⇒ major bump | derived-artifact | sdd ready | spec-valid | generated_artifact_structural_diff_unbumped | implemented | — | — |
|
|
69
|
+
| ENF-020A | ENF-020 | Iterative debt budget per Partition — `unmodeled_budget` form/required-sub-fields check | structural-lint | sdd lint | spec-valid | sdd:debt-budget-form | implemented | — | — |
|
|
70
|
+
| ENF-020B | ENF-020 | Iterative debt budget per Partition — monotonic non-increase vs recorded baseline | aggregate-gate | sdd ready | implementation-valid | debt_budget_increased | implemented:hybrid | partition owner | sprint close |
|
|
71
|
+
| ENF-021 | — | Brownfield baseline `freshness_token` matches source tree | freshness | sdd check | baseline-valid | — | implemented | — | — |
|
|
72
|
+
| ENF-022 | — | weasel-words list has single canonical source (drift detected by `sdd doctor`) | graph-consistency | sdd doctor | — | sdd:weasel-source-drift | planned | — | release cut |
|
|
73
|
+
| ENF-023 | — | Code↔spec contradiction outside Delta → agent halts | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
74
|
+
| ENF-024 | — | Text-as-contract → agent classifies as LocalizationContract | agent-judgment | agent | — | — | — | PR author | PR merge |
|
|
75
|
+
| ENF-025 | — | Generated output → agent classifies as GeneratedArtifact | agent-judgment | agent | — | — | — | PR author | PR merge |
|
|
76
|
+
| ENF-026 | — | Removal without `compatibility_action` → agent halts | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
77
|
+
| ENF-027 | — | Missing `Policy`-ref on a boundary detected by agent → halt | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
78
|
+
| ENF-028 | — | Missing `applicability` when an axis variable is detectable in baseline → halt | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
79
|
+
| ENF-029 | — | Missing `concurrency_model` on a boundary → halt | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
80
|
+
| ENF-030 | — | Missing `data_scope` on persistent state → halt | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
81
|
+
| ENF-031 | — | Missing `baseline_version` on Delta/Migration → halt before relying on lint | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
82
|
+
| ENF-032 | — | `proposed → approved` Surface while referenced Policy/Contract not yet approved → halt before invoking finalize | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
83
|
+
| ENF-033 | — | Migration `runtime_state` change without updating dependent `enforcement_stage` → halt | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
84
|
+
| ENF-034 | — | Structural-breaking diff in `GeneratedArtifact(published_surface=yes)` without major bump → halt before commit | agent-halt | agent | — | — | — | PR author | PR merge |
|
|
85
|
+
| ENF-035 | — | Spec document carries every required section (§2) | structural-lint | sdd lint | spec-valid | sdd:section-presence | implemented | — | — |
|
|
86
|
+
| ENF-036 | — | Spec document sections appear in the required order (§2) | structural-lint | sdd lint | spec-valid | sdd:section-order | implemented | — | — |
|
|
87
|
+
| ENF-037 | — | Every normative ID carries `lifecycle.status` (§7.2) | structural-lint | sdd lint | spec-valid | sdd:lifecycle-status-present | implemented | — | — |
|
|
88
|
+
| ENF-038 | — | `lifecycle.status` is a valid enum value (§7.2) | structural-lint | sdd lint | spec-valid | sdd:lifecycle-status-valid | implemented | — | — |
|
|
89
|
+
| ENF-039 | — | `approval_record` present for `approved`/`deprecated`/`removed` (§7.5) | structural-lint | sdd lint | spec-valid | sdd:approval-record-required | implemented | — | — |
|
|
90
|
+
| ENF-040 | — | `approval_record` forbidden for `draft`/`proposed` (§7.5) | structural-lint | sdd lint | spec-valid | sdd:approval-record-forbidden | implemented | — | — |
|
|
91
|
+
| ENF-041 | — | Every normative ID declares a `Test obligation` (§4) | structural-lint | sdd lint | spec-valid | sdd:test-obligation-required | implemented | — | — |
|
|
92
|
+
| ENF-042 | — | `version` field is an integer where required (§1.5) | structural-lint | sdd lint | spec-valid | sdd:type-version-int | implemented | — | — |
|
|
93
|
+
| ENF-043 | — | `Invariant.evidence` is a valid enum value (§1.5) | structural-lint | sdd lint | spec-valid | sdd:type-invariant-evidence | implemented | — | — |
|
|
94
|
+
| ENF-044 | — | `Invariant.stability` is a valid enum value (§1.5) | structural-lint | sdd lint | spec-valid | sdd:type-invariant-stability | implemented | — | — |
|
|
95
|
+
| ENF-045 | — | `data_scope` is a valid enum value (§1.5) | structural-lint | sdd lint | spec-valid | sdd:type-data-scope | implemented | — | — |
|
|
96
|
+
| ENF-046 | — | `NFR.verification_stage` is a valid enum value (§1.5) | structural-lint | sdd lint | spec-valid | sdd:type-nfr-stage | implemented | — | — |
|
|
97
|
+
| ENF-047 | — | `Migration.direction` is a valid enum value (§1.5) | structural-lint | sdd lint | spec-valid | sdd:type-migration-direction | implemented | — | — |
|
|
98
|
+
| ENF-048 | — | `Migration.mode` is a valid enum value (§1.5) | structural-lint | sdd lint | spec-valid | sdd:type-migration-mode | implemented | — | — |
|
|
99
|
+
| ENF-049 | — | `Migration.runtime_state` is a valid enum value (§1.5) | structural-lint | sdd lint | spec-valid | sdd:type-migration-runtime-state | implemented | — | — |
|
|
100
|
+
| ENF-050 | — | `Surface.boundary_type` is a valid enum value (§1.5) | structural-lint | sdd lint | spec-valid | sdd:type-surface-boundary-type | implemented | — | — |
|
|
101
|
+
| ENF-051 | — | `proposed`/`draft` ID outside a partition `sandbox_paths` is not mergeable | graph-consistency | sdd ready | implementation-valid | unapproved | implemented | — | — |
|
|
102
|
+
| ENF-052 | — | Every `approved` ID is covered by ≥1 `@covers` marker | aggregate-gate | sdd ready | implementation-valid | uncovered | implemented | — | — |
|
|
103
|
+
| ENF-053 | — | `removed` ID has a test for its `compatibility_action` | aggregate-gate | sdd ready | implementation-valid | removed_no_compat_test | implemented | — | — |
|
|
104
|
+
| ENF-054 | — | `removed` ID test matches its declared `compatibility_action` | aggregate-gate | sdd ready | implementation-valid | removed_compat_action_mismatch | implemented | — | — |
|
|
105
|
+
| ENF-055 | — | `@covers` marker references an existing normative ID | graph-consistency | sdd ready | implementation-valid | orphan_covers | implemented | — | — |
|
|
106
|
+
| ENF-056 | — | `@covers` marker uses a declared partition prefix | graph-consistency | sdd ready | implementation-valid | unknown_partition_covers | implemented | — | — |
|
|
107
|
+
| ENF-057 | — | `sdd ready` aggregates upstream `sdd lint` violations | aggregate-gate | sdd ready | implementation-valid | aggregated_lint | implemented | — | — |
|
|
108
|
+
| ENF-058 | — | `sdd ready` aggregates baseline (`sdd check`) violations | aggregate-gate | sdd ready | implementation-valid | aggregated_check | implemented | — | — |
|
|
109
|
+
| ENF-059 | — | Unresolved `Open-Q.blocking=yes` blocks spec-valid (§0) | structural-lint | sdd lint | spec-valid | sdd:open-q-blocking | implemented | — | — |
|
|
110
|
+
| ENF-060 | — | Near-miss `@covers` markers surface as non-blocking `advisories[]` in the ready envelope (never affect exit code) | report-evidence | sdd ready | implementation-valid | — | implemented | — | — |
|
|
111
|
+
|
|
112
|
+
## Maturity transitions
|
|
113
|
+
|
|
114
|
+
- `planned` → `implemented`: requires CHANGELOG entry in `sdd-cli` with PR number and integration test; `compatible_sdd_cli` range in this registry must cover the implementing CLI version.
|
|
115
|
+
- `implemented` → `deprecated`: alias period ≥1 minor on `Surface: diagnostics` (SUR-009 in `sdd-cli/spec/spec.md`); the alias is recorded in CLI's `DiagnosticRegistry.ts`.
|
|
116
|
+
- `out_of_scope` → ничего: фиксирует, что это не будет автоматизировано в данной major-line.
|
|
117
|
+
|
|
118
|
+
Many `agent-halt` rows duplicate mechanical lint coverage (e.g. ENF-027 vs ENF-013, ENF-031 vs ENF-003). The duplication is intentional — `sdd lint` catches the violation at gate-2 (`spec-valid`), but the agent must still raise it as `Open-Q` proactively rather than relying on the linter to find it. `enforcement_class: agent-halt` is the *behavior expected from the agent*; `structural-lint` is the *mechanical safety net*.
|
|
119
|
+
|
|
120
|
+
## How to add a new requirement
|
|
121
|
+
|
|
122
|
+
1. Сформулировать как одно предложение MUST.
|
|
123
|
+
2. Выбрать `enforcement_class` (одно из 10) и `executor`.
|
|
124
|
+
3. Если class mechanical и `maturity=implemented` — обязателен `diagnostic_id`. Если `executor ∈ {agent, human}` — обязательны `process_owner` и `review_trigger`.
|
|
125
|
+
4. Добавить строку в Registry. Если требование декомпозируется (mechanical part + human-review part) — две строки с общим `parent_id`.
|
|
126
|
+
5. В `rules/spec-driven-development.md` и/или `SKILL.md` сформулировать сам MUST и сослаться на ENF-id.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Stdin: JSON с tool_name и tool_input.
|
|
3
|
+
# Stdout: hookSpecificOutput c additionalContext: reminder запустить `sdd lint`
|
|
4
|
+
input=$(cat)
|
|
5
|
+
tool=$(printf '%s' "$input" | jq -r '.tool_name')
|
|
6
|
+
path=$(printf '%s' "$input" | jq -r '.tool_input.path // .tool_input.file_path // empty')
|
|
7
|
+
|
|
8
|
+
case "$path" in
|
|
9
|
+
*/spec/*.md) ;;
|
|
10
|
+
*) exit 0 ;;
|
|
11
|
+
esac
|
|
12
|
+
|
|
13
|
+
config_dir="$(dirname "$path")"
|
|
14
|
+
found_config=
|
|
15
|
+
while [[ "$config_dir" != "/" && "$config_dir" != "." ]]; do
|
|
16
|
+
if [[ -f "$config_dir/.sdd/config.json" ]]; then
|
|
17
|
+
found_config=1
|
|
18
|
+
break
|
|
19
|
+
fi
|
|
20
|
+
config_dir="$(dirname "$config_dir")"
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
if [[ -z "$found_config" ]]; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
jq -n --arg tool "$tool" --arg path "$path" '{
|
|
28
|
+
hookSpecificOutput: {
|
|
29
|
+
hookEventName: "PreToolUse",
|
|
30
|
+
permissionDecision: "allow",
|
|
31
|
+
additionalContext: ("Reminder: " + $path + " is a spec file in an SDD project (.sdd/config.json found). After this " + $tool + " completes, run `sdd lint` and do not proceed to Implement/Commit until exit 0. See @sdd/sdd-cli-usage.md for full phase-to-command mapping.")
|
|
32
|
+
}
|
|
33
|
+
}'
|