create-yss-spec 2.2.3 → 2.2.5
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/README.md +17 -1
- package/package.json +1 -1
- package/src/cli.js +15 -1
- package/src/self-update.js +277 -0
- package/template/.agents/skills/yss-domain/SKILL.md +5 -4
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +15 -14
- package/template/.agents/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.agents/skills/yss-router/SKILL.md +4 -2
- package/template/.agents/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.agents/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.agents/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.agents/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.agents/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.agents/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.agents/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.agents/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.agents/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.agents/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.agents/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.agents/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.agents/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.agents/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.agents/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.agents/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.agents/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.agents/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.claude/skills/yss-domain/SKILL.md +5 -4
- package/template/.claude/skills/yss-product-lifecycle/SKILL.md +15 -14
- package/template/.claude/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.claude/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.claude/skills/yss-router/SKILL.md +4 -2
- package/template/.claude/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.claude/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.claude/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.claude/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.claude/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.claude/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.claude/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.claude/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.claude/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.claude/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.claude/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.claude/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.claude/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.claude/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.claude/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.claude/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.claude/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.claude/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.claude/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.codex/skills/yss-domain/SKILL.md +5 -4
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +15 -14
- package/template/.codex/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.codex/skills/yss-router/SKILL.md +4 -2
- package/template/.codex/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.codex/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.codex/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.codex/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.codex/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.codex/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.codex/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.codex/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.codex/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.codex/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.codex/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.codex/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.codex/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.codex/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.codex/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.codex/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.codex/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.cursor/skills/yss-domain/SKILL.md +5 -4
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +15 -14
- package/template/.cursor/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.cursor/skills/yss-router/SKILL.md +4 -2
- package/template/.cursor/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.cursor/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.cursor/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.cursor/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.cursor/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.cursor/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.cursor/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.cursor/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.cursor/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.cursor/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.cursor/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.cursor/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.cursor/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.cursor/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.cursor/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.cursor/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.hermes/skills/yss-domain/SKILL.md +5 -4
- package/template/.hermes/skills/yss-product-lifecycle/SKILL.md +15 -14
- package/template/.hermes/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.hermes/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.hermes/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.hermes/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.hermes/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.hermes/skills/yss-router/SKILL.md +4 -2
- package/template/.hermes/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.hermes/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.hermes/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.hermes/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.hermes/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.hermes/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.hermes/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.hermes/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.hermes/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.hermes/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.hermes/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.hermes/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.hermes/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.hermes/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.hermes/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.hermes/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.hermes/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.pi/skills/yss-domain/SKILL.md +5 -4
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +15 -14
- package/template/.pi/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.pi/skills/yss-router/SKILL.md +4 -2
- package/template/.pi/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.pi/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.pi/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.pi/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.pi/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.pi/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.pi/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.pi/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.pi/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.pi/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.pi/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.pi/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.pi/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.pi/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.pi/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.pi/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.pi/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.pi/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.qoder/skills/yss-domain/SKILL.md +5 -4
- package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +15 -14
- package/template/.qoder/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.qoder/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.qoder/skills/yss-router/SKILL.md +4 -2
- package/template/.qoder/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.qoder/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.qoder/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.qoder/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.qoder/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.qoder/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.qoder/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.qoder/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.qoder/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.qoder/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.qoder/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.qoder/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.qoder/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.qoder/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.qoder/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.qoder/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.qoder/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.qoder/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.qoder/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.trae/skills/yss-domain/SKILL.md +5 -4
- package/template/.trae/skills/yss-product-lifecycle/SKILL.md +15 -14
- package/template/.trae/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.trae/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.trae/skills/yss-router/SKILL.md +4 -2
- package/template/.trae/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.trae/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.trae/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.trae/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.trae/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.trae/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.trae/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.trae/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.trae/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.trae/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.trae/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.trae/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.trae/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.trae/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.trae/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.trae/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.trae/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.trae/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.trae/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/AGENTS.md +22 -5
- package/template/CONTEXT.md +1 -1
- package/template/docs/agents/README.md +1 -1
- package/template/docs/agents/digital-human-roles.md +2 -2
- package/template/docs/agents/digital-human-roles.yaml +7 -1
- package/template/docs/agents/yss-skill-registry.yaml +12 -0
- package/template/docs/architecture/templates/system-overview-design-template.md +1 -0
- package/template/docs/process/lifecycle-artifact-map.md +8 -0
- package/template/docs/process/lifecycle-registry-baseline.json +11 -1
- package/template/docs/process/lifecycle-registry.yaml +46 -2
- package/template/docs/process/schemas/digital-human-task-package.schema.json +107 -0
- package/template/docs/process/schemas/subagent-task-package.schema.json +6 -0
- package/template/docs/process/subagent-collaboration.md +2 -2
- package/template/docs/templates/subagent-task-package-template.md +39 -1
- package/template/scripts/lib/lifecycle-transition.mjs +259 -0
- package/template/scripts/lib/scenario-checks.mjs +96 -2
- package/template/scripts/lib/task-package.mjs +214 -0
- package/template/scripts/verify-digital-human-task-package +14 -0
- package/template/scripts/verify-digital-human-task-package-scenarios +8 -0
- package/template/scripts/verify-lifecycle-transition-scenarios +105 -0
- package/template/scripts/verify-subagent-task-package +8 -0
- package/template/scripts/verify-subagent-task-package-scenarios +196 -0
- package/template/scripts/verify-template +13 -2
- package/template/skills-lock.json +36 -4
- package/template.snapshot.json +4 -4
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# DDD 战略设计合同
|
|
2
|
+
|
|
3
|
+
## 顶层结构
|
|
4
|
+
|
|
5
|
+
机器可读合同使用 `references/domain-strategy.schema.json`。顶层必须包含:
|
|
6
|
+
|
|
7
|
+
- `schema_version`、`domain_strategy_id`、`domain_version`、`status`;
|
|
8
|
+
- `contexts`、`subdomains`、`relationships`、`scenarios`;
|
|
9
|
+
- `concept_candidates`、`invariants`、`terminology_refs`;
|
|
10
|
+
- `downstream_mapping`、`evidence_refs`、`approval`。
|
|
11
|
+
|
|
12
|
+
## 上下文
|
|
13
|
+
|
|
14
|
+
每个上下文声明 `context_id`、职责边界、非职责、子域分类、模型所有者、关键场景和本地词汇引用。`context_id` 是稳定身份,不得使用代码模块、数据库 schema 或菜单路径替代。
|
|
15
|
+
|
|
16
|
+
## 关系
|
|
17
|
+
|
|
18
|
+
关系必须声明 `relationship_pattern`、`semantic_upstream`、`semantic_downstream`、`business_authority`、`model_change_impact`、`transport_direction` 和 `translation_responsibility`。语义方向与技术方向可以不同;关系模式只能使用合同中列出的 DDD 模式。
|
|
19
|
+
|
|
20
|
+
## 核心概念
|
|
21
|
+
|
|
22
|
+
使用 `domain_concept_candidate` 表达战略阶段的核心概念候选,记录身份特征、生命周期、不变量引用、可能的战术角色和置信度。不得在该合同中把概念直接批准为 Entity 或 Aggregate Root。
|
|
23
|
+
|
|
24
|
+
## 阶段决策包
|
|
25
|
+
|
|
26
|
+
阶段包应通过 `domain_strategy_ref` 引用 DDD 合同的当前版本和 digest,并使用 `domain_to_downstream_mapping` 描述领域变化对 Spec、设计、API、数据、Ticket 和测试 seam 的传播。
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://yss.local/schemas/domain-strategy-v1.json",
|
|
4
|
+
"title": "YSS DDD Strategic Design",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schema_version", "domain_strategy_id", "domain_version", "status", "contexts", "subdomains", "relationships", "scenarios", "concept_candidates", "invariants", "terminology_refs", "downstream_mapping", "evidence_refs", "approval"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schema_version": {"const": 1},
|
|
9
|
+
"domain_strategy_id": {"type": "string", "pattern": "^domain-strategy\\.[a-z0-9][a-z0-9-]*$"},
|
|
10
|
+
"domain_version": {"type": "string", "pattern": "^v[0-9]+$"},
|
|
11
|
+
"status": {"enum": ["draft", "ready-for-human", "approved", "stale", "blocked"]},
|
|
12
|
+
"contexts": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/context"}},
|
|
13
|
+
"subdomains": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/subdomain"}},
|
|
14
|
+
"relationships": {"type": "array", "items": {"$ref": "#/$defs/relationship"}},
|
|
15
|
+
"scenarios": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/scenario"}},
|
|
16
|
+
"concept_candidates": {"type": "array", "items": {"$ref": "#/$defs/concept"}},
|
|
17
|
+
"invariants": {"type": "array", "items": {"$ref": "#/$defs/invariant"}},
|
|
18
|
+
"terminology_refs": {"type": "array", "items": {"type": "string", "minLength": 1}},
|
|
19
|
+
"downstream_mapping": {"type": "array", "items": {"$ref": "#/$defs/mapping"}},
|
|
20
|
+
"evidence_refs": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
|
21
|
+
"approval": {"$ref": "#/$defs/approval"}
|
|
22
|
+
},
|
|
23
|
+
"$defs": {
|
|
24
|
+
"context": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["context_id", "name", "responsibilities", "non_responsibilities", "subdomain_type", "owner", "status"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"context_id": {"type": "string", "pattern": "^[A-Z][A-Za-z0-9]+$"},
|
|
29
|
+
"name": {"type": "string", "minLength": 1},
|
|
30
|
+
"responsibilities": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
|
31
|
+
"non_responsibilities": {"type": "array", "items": {"type": "string", "minLength": 1}},
|
|
32
|
+
"subdomain_type": {"enum": ["Core Domain", "Supporting Subdomain", "Generic Subdomain"]},
|
|
33
|
+
"owner": {"type": "string", "minLength": 1},
|
|
34
|
+
"status": {"enum": ["candidate", "confirmed", "stale"]}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"subdomain": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"required": ["subdomain_id", "name", "type", "rationale", "evidence_refs"],
|
|
40
|
+
"properties": {
|
|
41
|
+
"subdomain_id": {"type": "string", "pattern": "^subdomain\\.[a-z0-9][a-z0-9-]*$"},
|
|
42
|
+
"name": {"type": "string", "minLength": 1},
|
|
43
|
+
"type": {"enum": ["Core Domain", "Supporting Subdomain", "Generic Subdomain"]},
|
|
44
|
+
"rationale": {"type": "string", "minLength": 1},
|
|
45
|
+
"evidence_refs": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"relationship": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": ["relationship_id", "from_context", "to_context", "relationship_pattern", "semantic_upstream", "semantic_downstream", "business_authority", "model_change_impact", "transport_direction", "translation_responsibility", "direction_explanation"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"relationship_id": {"type": "string", "pattern": "^relationship\\.[a-z0-9][a-z0-9-]*$"},
|
|
53
|
+
"from_context": {"type": "string", "pattern": "^[A-Z][A-Za-z0-9]+$"},
|
|
54
|
+
"to_context": {"type": "string", "pattern": "^[A-Z][A-Za-z0-9]+$"},
|
|
55
|
+
"relationship_pattern": {"enum": ["Partnership", "Customer/Supplier", "Conformist", "Anti-Corruption Layer", "Open Host Service", "Published Language", "Shared Kernel", "Separate Ways"]},
|
|
56
|
+
"semantic_upstream": {"type": "string", "pattern": "^[A-Z][A-Za-z0-9]+$"},
|
|
57
|
+
"semantic_downstream": {"type": "string", "pattern": "^[A-Z][A-Za-z0-9]+$"},
|
|
58
|
+
"business_authority": {"type": "string", "minLength": 1},
|
|
59
|
+
"model_change_impact": {"type": "string", "minLength": 1},
|
|
60
|
+
"transport_direction": {"type": "string", "minLength": 1},
|
|
61
|
+
"translation_responsibility": {"type": "string", "minLength": 1},
|
|
62
|
+
"direction_explanation": {"type": "string", "minLength": 1}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"scenario": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"required": ["scenario_id", "actor", "preconditions", "commands", "rules", "events", "failure_results", "responsible_context", "consumers", "status"],
|
|
68
|
+
"properties": {
|
|
69
|
+
"scenario_id": {"type": "string", "pattern": "^scenario\\.[a-z0-9][a-z0-9-]*$"},
|
|
70
|
+
"actor": {"type": "string", "minLength": 1},
|
|
71
|
+
"preconditions": {"type": "array", "items": {"type": "string"}},
|
|
72
|
+
"commands": {"type": "array", "minItems": 1, "items": {"type": "string"}},
|
|
73
|
+
"rules": {"type": "array", "minItems": 1, "items": {"type": "string"}},
|
|
74
|
+
"events": {"type": "array", "items": {"type": "string"}},
|
|
75
|
+
"failure_results": {"type": "array", "minItems": 1, "items": {"type": "string"}},
|
|
76
|
+
"responsible_context": {"type": "string", "pattern": "^[A-Z][A-Za-z0-9]+$"},
|
|
77
|
+
"consumers": {"type": "array", "items": {"type": "string", "pattern": "^[A-Z][A-Za-z0-9]+$"}},
|
|
78
|
+
"status": {"enum": ["candidate", "confirmed", "stale"]}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"concept": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"required": ["concept_id", "name", "context_id", "identity_features", "lifecycle", "confidence", "status"],
|
|
84
|
+
"properties": {
|
|
85
|
+
"concept_id": {"type": "string", "pattern": "^domain-concept\\.[a-z0-9][a-z0-9-]*$"},
|
|
86
|
+
"name": {"type": "string", "minLength": 1},
|
|
87
|
+
"context_id": {"type": "string", "pattern": "^[A-Z][A-Za-z0-9]+$"},
|
|
88
|
+
"identity_features": {"type": "array", "minItems": 1, "items": {"type": "string"}},
|
|
89
|
+
"lifecycle": {"type": "array", "minItems": 1, "items": {"type": "string"}},
|
|
90
|
+
"invariant_refs": {"type": "array", "items": {"type": "string"}},
|
|
91
|
+
"possible_tactical_role": {"type": "string"},
|
|
92
|
+
"confidence": {"enum": ["low", "medium", "high"]},
|
|
93
|
+
"status": {"enum": ["candidate", "confirmed", "stale"]}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"invariant": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"required": ["invariant_id", "statement", "responsible_context", "scenario_refs", "verification_method"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"invariant_id": {"type": "string", "pattern": "^invariant\\.[a-z0-9][a-z0-9-]*$"},
|
|
101
|
+
"statement": {"type": "string", "minLength": 1},
|
|
102
|
+
"responsible_context": {"type": "string", "pattern": "^[A-Z][A-Za-z0-9]+$"},
|
|
103
|
+
"scenario_refs": {"type": "array", "minItems": 1, "items": {"type": "string"}},
|
|
104
|
+
"verification_method": {"type": "string", "minLength": 1}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"mapping": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"required": ["domain_change", "impacts", "propagation", "reapproval_condition"],
|
|
110
|
+
"properties": {
|
|
111
|
+
"domain_change": {"type": "string", "minLength": 1},
|
|
112
|
+
"impacts": {"type": "array", "minItems": 1, "items": {"type": "string"}},
|
|
113
|
+
"propagation": {"enum": ["direct", "transitive", "stale"]},
|
|
114
|
+
"reapproval_condition": {"type": "string", "minLength": 1}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"approval": {
|
|
118
|
+
"type": "object",
|
|
119
|
+
"required": ["approval_ref", "approver", "persisted_ref", "current_version"],
|
|
120
|
+
"properties": {
|
|
121
|
+
"approval_ref": {"type": "string", "minLength": 1},
|
|
122
|
+
"approver": {"type": "string", "minLength": 1},
|
|
123
|
+
"persisted_ref": {"type": "string", "minLength": 1},
|
|
124
|
+
"current_version": {"type": "string", "pattern": "^v[0-9]+$"}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 阶段决策包合同
|
|
2
|
+
|
|
3
|
+
`stage_decision_package` 是 Discovery、需求/产品/商务决策与 DDD 战略设计之间的冻结入口。它记录“做什么、为什么做、范围是什么、哪些仍未决”,不复制领域模型事实。
|
|
4
|
+
|
|
5
|
+
## 必须包含
|
|
6
|
+
|
|
7
|
+
- 问题陈述、目标用户、MVP、非目标、成功标准和可执行的测试 seam;
|
|
8
|
+
- 已确认决策、假设、约束和未决项;
|
|
9
|
+
- `domain_strategy_ref`:领域战略合同的稳定 ID、版本、digest 和批准状态;
|
|
10
|
+
- UI/API/Data/Backend/Frontend/跨仓/高风险影响面布尔值;
|
|
11
|
+
- 每个下游消费者的传播方式、重新批准条件、证据引用和批准记录。`approval.approval_ref` 必须指向可读取且通过 `scripts/verify-approval-record` 的 `gate.stage-decision-package-approved` 会签记录;`approval.approver` 必须与记录中的 `role_id` 一致。
|
|
12
|
+
|
|
13
|
+
## 未决项规则
|
|
14
|
+
|
|
15
|
+
`blocker` 必须阻断批准;`deferred` 必须同时记录责任人、下一动作和目标版本/发布日期。没有责任人或恢复路径的未决项不得隐藏在备注中。
|
|
16
|
+
|
|
17
|
+
## 下游消费规则
|
|
18
|
+
|
|
19
|
+
只有 `approved` 且引用的领域战略为 `approved`、版本当前的阶段包,才可以进入 Spec synthesis。任何领域战略 digest、版本或影响面变化都使阶段包 `stale`,并要求重新评估。
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://yss.local/schemas/stage-decision-package-v1.json",
|
|
4
|
+
"title": "YSS Stage Decision Package",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schema_version", "stage_decision_id", "package_version", "status", "problem_statement", "target_users", "mvp", "non_goals", "success_criteria", "test_seams", "confirmed_decisions", "assumptions", "constraints", "unresolved_items", "terminology_refs", "domain_strategy_ref", "impact_assessment", "downstream_mapping", "evidence_refs", "approval"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schema_version": {"const": 1},
|
|
9
|
+
"stage_decision_id": {"type": "string", "pattern": "^stage-decision\\.[a-z0-9][a-z0-9-]*$"},
|
|
10
|
+
"package_version": {"type": "string", "pattern": "^v[0-9]+$"},
|
|
11
|
+
"status": {"enum": ["draft", "ready-for-human", "approved", "stale", "blocked"]},
|
|
12
|
+
"problem_statement": {"type": "string", "minLength": 1},
|
|
13
|
+
"target_users": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
|
14
|
+
"mvp": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
|
15
|
+
"non_goals": {"type": "array", "items": {"type": "string", "minLength": 1}},
|
|
16
|
+
"success_criteria": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
|
17
|
+
"test_seams": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
|
18
|
+
"confirmed_decisions": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
|
19
|
+
"assumptions": {"type": "array", "items": {"type": "string", "minLength": 1}},
|
|
20
|
+
"constraints": {"type": "array", "items": {"type": "string", "minLength": 1}},
|
|
21
|
+
"unresolved_items": {"type": "array", "items": {"$ref": "#/$defs/unresolved"}},
|
|
22
|
+
"terminology_refs": {"type": "array", "items": {"type": "string", "minLength": 1}},
|
|
23
|
+
"domain_strategy_ref": {"$ref": "#/$defs/domainRef"},
|
|
24
|
+
"impact_assessment": {"$ref": "#/$defs/impact"},
|
|
25
|
+
"downstream_mapping": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/mapping"}},
|
|
26
|
+
"evidence_refs": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
|
27
|
+
"approval": {"$ref": "#/$defs/approval"}
|
|
28
|
+
},
|
|
29
|
+
"$defs": {
|
|
30
|
+
"unresolved": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"required": ["item", "type", "owner", "next_action", "target_version"],
|
|
33
|
+
"properties": {
|
|
34
|
+
"item": {"type": "string", "minLength": 1},
|
|
35
|
+
"type": {"enum": ["blocker", "deferred"]},
|
|
36
|
+
"owner": {"type": "string", "minLength": 1},
|
|
37
|
+
"next_action": {"type": "string", "minLength": 1},
|
|
38
|
+
"target_version": {"type": "string", "minLength": 1}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"domainRef": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"required": ["domain_strategy_id", "domain_version", "digest", "status", "persisted_ref"],
|
|
44
|
+
"properties": {
|
|
45
|
+
"domain_strategy_id": {"type": "string", "pattern": "^domain-strategy\\.[a-z0-9][a-z0-9-]*$"},
|
|
46
|
+
"domain_version": {"type": "string", "pattern": "^v[0-9]+$"},
|
|
47
|
+
"digest": {"type": "string", "minLength": 1},
|
|
48
|
+
"status": {"enum": ["approved", "stale", "blocked"]},
|
|
49
|
+
"persisted_ref": {"type": "string", "minLength": 1}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"impact": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"required": ["ui", "api", "data", "backend", "frontend", "cross_repo", "high_risk"],
|
|
55
|
+
"properties": {
|
|
56
|
+
"ui": {"type": "boolean"},
|
|
57
|
+
"api": {"type": "boolean"},
|
|
58
|
+
"data": {"type": "boolean"},
|
|
59
|
+
"backend": {"type": "boolean"},
|
|
60
|
+
"frontend": {"type": "boolean"},
|
|
61
|
+
"cross_repo": {"type": "boolean"},
|
|
62
|
+
"high_risk": {"type": "boolean"}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"mapping": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"required": ["domain_change", "consumer", "propagation", "reapproval_condition"],
|
|
68
|
+
"properties": {
|
|
69
|
+
"domain_change": {"type": "string", "minLength": 1},
|
|
70
|
+
"consumer": {"type": "string", "minLength": 1},
|
|
71
|
+
"propagation": {"enum": ["direct", "transitive", "not-applicable", "stale"]},
|
|
72
|
+
"reapproval_condition": {"type": "string", "minLength": 1}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"approval": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"required": ["approval_ref", "approver", "persisted_ref", "current_version"],
|
|
78
|
+
"properties": {
|
|
79
|
+
"approval_ref": {"type": "string", "minLength": 1},
|
|
80
|
+
"approver": {"type": "string", "minLength": 1},
|
|
81
|
+
"persisted_ref": {"type": "string", "minLength": 1},
|
|
82
|
+
"current_version": {"type": "string", "pattern": "^v[0-9]+$"}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# 验证规则
|
|
2
|
+
|
|
3
|
+
验证分为结构、引用和语义三层:
|
|
4
|
+
|
|
5
|
+
1. **结构**:Schema 版本、稳定 ID、状态枚举、必填字段、条件字段和证据格式。
|
|
6
|
+
2. **引用**:上下文端点、场景、不变量、术语和下游资产引用必须可解析。
|
|
7
|
+
3. **语义**:禁止关系自环、未知上下文、未批准 Shared Kernel、未解释的语义/技术方向差异、职责重叠、无责任人的不变量和跨上下文直接共享实体。
|
|
8
|
+
|
|
9
|
+
以下情况必须 `blocked`:关键规则所有权未知、关键术语冲突、上下文边界未定、关键失败场景缺失、事件语义无法解释、领域专家确认缺失。
|
|
10
|
+
|
|
11
|
+
以下变化应触发精确传播:
|
|
12
|
+
|
|
13
|
+
- 术语含义变化 → Spec、验收、原型或 API;
|
|
14
|
+
- 上下文边界变化 → 系统架构、OpenAPI、数据和切片;
|
|
15
|
+
- Context Map 关系变化 → 集成契约和模型翻译;
|
|
16
|
+
- 不变量变化 → 测试 seam、实现合同和验证策略。
|
|
17
|
+
|
|
18
|
+
验证器允许“语义方向 ≠ 技术方向”,但要求 `direction_explanation` 和翻译责任非空。
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
|
|
5
|
+
import { loadApprovalRecord, resolveApprovalRef, validateApprovalRecordFile } from "../../../../scripts/lib/approval-record.mjs";
|
|
6
|
+
|
|
7
|
+
const required = ["schema_version", "domain_strategy_id", "domain_version", "status", "contexts", "subdomains", "relationships", "scenarios", "concept_candidates", "invariants", "terminology_refs", "downstream_mapping", "evidence_refs", "approval"];
|
|
8
|
+
const idPatterns = {
|
|
9
|
+
context_id: /^[A-Z][A-Za-z0-9]+$/,
|
|
10
|
+
domain_strategy_id: /^domain-strategy\.[a-z0-9][a-z0-9-]*$/,
|
|
11
|
+
subdomain_id: /^subdomain\.[a-z0-9][a-z0-9-]*$/,
|
|
12
|
+
relationship_id: /^relationship\.[a-z0-9][a-z0-9-]*$/,
|
|
13
|
+
scenario_id: /^scenario\.[a-z0-9][a-z0-9-]*$/,
|
|
14
|
+
concept_id: /^domain-concept\.[a-z0-9][a-z0-9-]*$/,
|
|
15
|
+
invariant_id: /^invariant\.[a-z0-9][a-z0-9-]*$/
|
|
16
|
+
};
|
|
17
|
+
const relationshipPatterns = new Set(["Partnership", "Customer/Supplier", "Conformist", "Anti-Corruption Layer", "Open Host Service", "Published Language", "Shared Kernel", "Separate Ways"]);
|
|
18
|
+
|
|
19
|
+
function fail(errors) {
|
|
20
|
+
process.stderr.write(`${JSON.stringify({ result: "blocked", errors }, null, 2)}\n`);
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function nonEmpty(value) { return typeof value === "string" && value.trim().length > 0; }
|
|
25
|
+
function list(value) { return Array.isArray(value); }
|
|
26
|
+
function unique(items, label, errors) {
|
|
27
|
+
const seen = new Set();
|
|
28
|
+
for (const item of items) {
|
|
29
|
+
if (seen.has(item)) errors.push(`${label} 重复: ${item}`);
|
|
30
|
+
seen.add(item);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function requireField(object, field, path, errors) {
|
|
34
|
+
if (!(field in object) || object[field] === null || object[field] === undefined) errors.push(`${path}.${field} 缺失`);
|
|
35
|
+
}
|
|
36
|
+
function requireString(object, field, path, errors) {
|
|
37
|
+
requireField(object, field, path, errors);
|
|
38
|
+
if (field in object && !nonEmpty(object[field])) errors.push(`${path}.${field} 必须是非空字符串`);
|
|
39
|
+
}
|
|
40
|
+
function requireArray(object, field, path, errors, min = 0, itemKind = "string") {
|
|
41
|
+
requireField(object, field, path, errors);
|
|
42
|
+
if (field in object && (!list(object[field]) || object[field].length < min)) errors.push(`${path}.${field} 必须是至少 ${min} 项的数组`);
|
|
43
|
+
if (field in object && list(object[field]) && itemKind === "string") object[field].forEach((item, index) => { if (!nonEmpty(item)) errors.push(`${path}.${field}[${index}] 必须是非空字符串`); });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function validate(data) {
|
|
47
|
+
const errors = [];
|
|
48
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return ["合同必须是对象"];
|
|
49
|
+
for (const field of required) requireField(data, field, "root", errors);
|
|
50
|
+
if (data.schema_version !== 1) errors.push("schema_version 必须为 1");
|
|
51
|
+
if (!idPatterns.domain_strategy_id.test(String(data.domain_strategy_id ?? ""))) errors.push("domain_strategy_id 格式非法");
|
|
52
|
+
if (!/^v[0-9]+$/.test(String(data.domain_version ?? ""))) errors.push("domain_version 必须形如 v1");
|
|
53
|
+
if (!["draft", "ready-for-human", "approved", "stale", "blocked"].includes(data.status)) errors.push("status 非法");
|
|
54
|
+
|
|
55
|
+
requireArray(data, "contexts", "root", errors, 1, "object");
|
|
56
|
+
const contexts = list(data.contexts) ? data.contexts : [];
|
|
57
|
+
const contextIds = contexts.map((item) => item?.context_id).filter(Boolean);
|
|
58
|
+
unique(contextIds, "context_id", errors);
|
|
59
|
+
for (const [index, context] of contexts.entries()) {
|
|
60
|
+
const path = `contexts[${index}]`;
|
|
61
|
+
for (const field of ["context_id", "name", "owner", "status", "subdomain_type"]) requireString(context ?? {}, field, path, errors);
|
|
62
|
+
for (const field of ["responsibilities", "non_responsibilities"]) requireArray(context ?? {}, field, path, errors, field === "responsibilities" ? 1 : 0);
|
|
63
|
+
if (context?.context_id && !idPatterns.context_id.test(context.context_id)) errors.push(`${path}.context_id 格式非法`);
|
|
64
|
+
if (context?.status && !["candidate", "confirmed", "stale"].includes(context.status)) errors.push(`${path}.status 非法`);
|
|
65
|
+
if (context?.subdomain_type && !["Core Domain", "Supporting Subdomain", "Generic Subdomain"].includes(context.subdomain_type)) errors.push(`${path}.subdomain_type 非法`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
requireArray(data, "subdomains", "root", errors, 1, "object");
|
|
69
|
+
const subdomains = list(data.subdomains) ? data.subdomains : [];
|
|
70
|
+
unique(subdomains.map((item) => item?.subdomain_id).filter(Boolean), "subdomain_id", errors);
|
|
71
|
+
for (const [index, item] of subdomains.entries()) {
|
|
72
|
+
const path = `subdomains[${index}]`;
|
|
73
|
+
for (const field of ["subdomain_id", "name", "type", "rationale"]) requireString(item ?? {}, field, path, errors);
|
|
74
|
+
requireArray(item ?? {}, "evidence_refs", path, errors, 1);
|
|
75
|
+
if (item?.subdomain_id && !idPatterns.subdomain_id.test(item.subdomain_id)) errors.push(`${path}.subdomain_id 格式非法`);
|
|
76
|
+
if (item?.type && !["Core Domain", "Supporting Subdomain", "Generic Subdomain"].includes(item.type)) errors.push(`${path}.type 非法`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const relationships = list(data.relationships) ? data.relationships : [];
|
|
80
|
+
unique(relationships.map((item) => item?.relationship_id).filter(Boolean), "relationship_id", errors);
|
|
81
|
+
for (const [index, relationship] of relationships.entries()) {
|
|
82
|
+
const path = `relationships[${index}]`;
|
|
83
|
+
for (const field of ["relationship_id", "from_context", "to_context", "relationship_pattern", "semantic_upstream", "semantic_downstream", "business_authority", "model_change_impact", "transport_direction", "translation_responsibility", "direction_explanation"]) requireString(relationship ?? {}, field, path, errors);
|
|
84
|
+
if (relationship?.relationship_id && !idPatterns.relationship_id.test(relationship.relationship_id)) errors.push(`${path}.relationship_id 格式非法`);
|
|
85
|
+
for (const field of ["from_context", "to_context", "semantic_upstream", "semantic_downstream"]) {
|
|
86
|
+
if (relationship?.[field] && !contextIds.includes(relationship[field])) errors.push(`${path}.${field} 未引用已声明上下文`);
|
|
87
|
+
}
|
|
88
|
+
if (relationship?.from_context === relationship?.to_context) errors.push(`${path} 不允许上下文自环`);
|
|
89
|
+
if (relationship?.semantic_upstream && ![relationship.from_context, relationship.to_context].includes(relationship.semantic_upstream)) errors.push(`${path}.semantic_upstream 必须是关系端点`);
|
|
90
|
+
if (relationship?.semantic_downstream && ![relationship.from_context, relationship.to_context].includes(relationship.semantic_downstream)) errors.push(`${path}.semantic_downstream 必须是关系端点`);
|
|
91
|
+
if (relationship?.relationship_pattern && !relationshipPatterns.has(relationship.relationship_pattern)) errors.push(`${path}.relationship_pattern 非法`);
|
|
92
|
+
if (relationship?.relationship_pattern === "Shared Kernel" && !nonEmpty(relationship.shared_kernel_approval_ref)) errors.push(`${path} Shared Kernel 必须有 shared_kernel_approval_ref`);
|
|
93
|
+
if (relationship?.semantic_upstream && relationship?.semantic_downstream && relationship.semantic_upstream === relationship.semantic_downstream) errors.push(`${path} semantic_upstream 与 semantic_downstream 不能相同`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
requireArray(data, "scenarios", "root", errors, 1, "object");
|
|
97
|
+
const scenarios = list(data.scenarios) ? data.scenarios : [];
|
|
98
|
+
const scenarioIds = scenarios.map((item) => item?.scenario_id).filter(Boolean);
|
|
99
|
+
unique(scenarioIds, "scenario_id", errors);
|
|
100
|
+
for (const [index, scenario] of scenarios.entries()) {
|
|
101
|
+
const path = `scenarios[${index}]`;
|
|
102
|
+
for (const field of ["scenario_id", "actor", "responsible_context", "status"]) requireString(scenario ?? {}, field, path, errors);
|
|
103
|
+
for (const field of ["preconditions", "commands", "rules", "events", "failure_results", "consumers"]) requireArray(scenario ?? {}, field, path, errors, field === "commands" || field === "rules" || field === "failure_results" ? 1 : 0);
|
|
104
|
+
if (scenario?.scenario_id && !idPatterns.scenario_id.test(scenario.scenario_id)) errors.push(`${path}.scenario_id 格式非法`);
|
|
105
|
+
if (scenario?.responsible_context && !contextIds.includes(scenario.responsible_context)) errors.push(`${path}.responsible_context 未声明`);
|
|
106
|
+
for (const consumer of scenario?.consumers ?? []) if (!contextIds.includes(consumer)) errors.push(`${path}.consumers 引用了未声明上下文: ${consumer}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const concepts = list(data.concept_candidates) ? data.concept_candidates : [];
|
|
110
|
+
unique(concepts.map((item) => item?.concept_id).filter(Boolean), "concept_id", errors);
|
|
111
|
+
for (const [index, concept] of concepts.entries()) {
|
|
112
|
+
const path = `concept_candidates[${index}]`;
|
|
113
|
+
for (const field of ["concept_id", "name", "context_id", "confidence", "status"]) requireString(concept ?? {}, field, path, errors);
|
|
114
|
+
for (const field of ["identity_features", "lifecycle"]) requireArray(concept ?? {}, field, path, errors, 1);
|
|
115
|
+
if (concept?.concept_id && !idPatterns.concept_id.test(concept.concept_id)) errors.push(`${path}.concept_id 格式非法`);
|
|
116
|
+
if (concept?.context_id && !contextIds.includes(concept.context_id)) errors.push(`${path}.context_id 未声明`);
|
|
117
|
+
if (concept?.confidence && !["low", "medium", "high"].includes(concept.confidence)) errors.push(`${path}.confidence 非法`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const invariants = list(data.invariants) ? data.invariants : [];
|
|
121
|
+
unique(invariants.map((item) => item?.invariant_id).filter(Boolean), "invariant_id", errors);
|
|
122
|
+
for (const [index, invariant] of invariants.entries()) {
|
|
123
|
+
const path = `invariants[${index}]`;
|
|
124
|
+
for (const field of ["invariant_id", "statement", "responsible_context", "verification_method"]) requireString(invariant ?? {}, field, path, errors);
|
|
125
|
+
requireArray(invariant ?? {}, "scenario_refs", path, errors, 1);
|
|
126
|
+
if (invariant?.invariant_id && !idPatterns.invariant_id.test(invariant.invariant_id)) errors.push(`${path}.invariant_id 格式非法`);
|
|
127
|
+
if (invariant?.responsible_context && !contextIds.includes(invariant.responsible_context)) errors.push(`${path}.responsible_context 未声明`);
|
|
128
|
+
for (const reference of invariant?.scenario_refs ?? []) if (!scenarioIds.includes(reference)) errors.push(`${path}.scenario_refs 引用了未声明场景: ${reference}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
requireArray(data, "terminology_refs", "root", errors, 0);
|
|
132
|
+
requireArray(data, "evidence_refs", "root", errors, 1);
|
|
133
|
+
const mappings = list(data.downstream_mapping) ? data.downstream_mapping : [];
|
|
134
|
+
for (const [index, mapping] of mappings.entries()) {
|
|
135
|
+
const path = `downstream_mapping[${index}]`;
|
|
136
|
+
for (const field of ["domain_change", "propagation", "reapproval_condition"]) requireString(mapping ?? {}, field, path, errors);
|
|
137
|
+
requireArray(mapping ?? {}, "impacts", path, errors, 1);
|
|
138
|
+
if (mapping?.propagation && !["direct", "transitive", "stale"].includes(mapping.propagation)) errors.push(`${path}.propagation 非法`);
|
|
139
|
+
}
|
|
140
|
+
const approval = data.approval ?? {};
|
|
141
|
+
for (const field of ["approval_ref", "approver", "persisted_ref", "current_version"]) requireString(approval, field, "approval", errors);
|
|
142
|
+
if (data.status === "approved") {
|
|
143
|
+
if (contexts.some((context) => context.status !== "confirmed")) errors.push("approved 合同要求所有上下文 status=confirmed");
|
|
144
|
+
if (approval.current_version !== data.domain_version) errors.push("approval.current_version 必须等于 domain_version");
|
|
145
|
+
try {
|
|
146
|
+
const approvalPath = resolveApprovalRef(approval.approval_ref);
|
|
147
|
+
validateApprovalRecordFile(approvalPath, { requireApproved: true });
|
|
148
|
+
const record = loadApprovalRecord(approvalPath);
|
|
149
|
+
if (record.gate_id !== "gate.domain-strategy-approved") errors.push("approval.approval_ref 的 gate_id 必须为 gate.domain-strategy-approved");
|
|
150
|
+
if (record.role_id !== approval.approver) errors.push("approval.approver 必须与会签记录 role_id 一致");
|
|
151
|
+
} catch (error) { errors.push(`approval.approval_ref 会签记录无效: ${error.message}`); }
|
|
152
|
+
}
|
|
153
|
+
return errors;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const file = process.argv[2];
|
|
157
|
+
if (!file) fail(["用法: validate-domain-strategy.mjs <contract.yaml>"]);
|
|
158
|
+
else {
|
|
159
|
+
try {
|
|
160
|
+
const source = await readFile(file, "utf8");
|
|
161
|
+
const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
|
|
162
|
+
if (document.errors.length) fail([document.errors[0].message]);
|
|
163
|
+
else {
|
|
164
|
+
const errors = validate(document.toJS({ maxAliasCount: 0 }));
|
|
165
|
+
if (errors.length) fail(errors);
|
|
166
|
+
else process.stdout.write(JSON.stringify({ result: "completed", contract: file }, null, 2) + "\n");
|
|
167
|
+
}
|
|
168
|
+
} catch (error) { fail([error.message]); }
|
|
169
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
|
|
7
|
+
import { loadApprovalRecord, resolveApprovalRef, validateApprovalRecordFile } from "../../../../scripts/lib/approval-record.mjs";
|
|
8
|
+
|
|
9
|
+
const required = ["schema_version", "stage_decision_id", "package_version", "status", "problem_statement", "target_users", "mvp", "non_goals", "success_criteria", "test_seams", "confirmed_decisions", "assumptions", "constraints", "unresolved_items", "terminology_refs", "domain_strategy_ref", "impact_assessment", "downstream_mapping", "evidence_refs", "approval"];
|
|
10
|
+
const idPattern = /^stage-decision\.[a-z0-9][a-z0-9-]*$/;
|
|
11
|
+
|
|
12
|
+
function fail(errors) {
|
|
13
|
+
process.stderr.write(`${JSON.stringify({ result: "blocked", errors }, null, 2)}\n`);
|
|
14
|
+
process.exitCode = 1;
|
|
15
|
+
}
|
|
16
|
+
function nonEmpty(value) { return typeof value === "string" && value.trim().length > 0; }
|
|
17
|
+
function requireField(object, field, path, errors) { if (!(field in object) || object[field] === null || object[field] === undefined) errors.push(`${path}.${field} 缺失`); }
|
|
18
|
+
function requireString(object, field, path, errors) { requireField(object, field, path, errors); if (field in object && !nonEmpty(object[field])) errors.push(`${path}.${field} 必须是非空字符串`); }
|
|
19
|
+
function requireArray(object, field, path, errors, min = 0) { requireField(object, field, path, errors); if (field in object && (!Array.isArray(object[field]) || object[field].length < min)) errors.push(`${path}.${field} 必须是至少 ${min} 项的数组`); if (field in object && Array.isArray(object[field])) object[field].forEach((item, index) => { if (!nonEmpty(item)) errors.push(`${path}.${field}[${index}] 必须是非空字符串`); }); }
|
|
20
|
+
function canonical(value) { if (Array.isArray(value)) return value.map(canonical); if (value && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical(value[key])])); return value; }
|
|
21
|
+
function digest(value) { return `sha256:${createHash("sha256").update(JSON.stringify(canonical(value))).digest("hex")}`; }
|
|
22
|
+
|
|
23
|
+
async function validate(data) {
|
|
24
|
+
const errors = [];
|
|
25
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return ["合同必须是对象"];
|
|
26
|
+
for (const field of required) requireField(data, field, "root", errors);
|
|
27
|
+
if (data.schema_version !== 1) errors.push("schema_version 必须为 1");
|
|
28
|
+
if (!idPattern.test(String(data.stage_decision_id ?? ""))) errors.push("stage_decision_id 格式非法");
|
|
29
|
+
if (!/^v[0-9]+$/.test(String(data.package_version ?? ""))) errors.push("package_version 必须形如 v1");
|
|
30
|
+
if (!["draft", "ready-for-human", "approved", "stale", "blocked"].includes(data.status)) errors.push("status 非法");
|
|
31
|
+
for (const field of ["problem_statement"]) requireString(data, field, "root", errors);
|
|
32
|
+
for (const field of ["target_users", "mvp", "non_goals", "success_criteria", "test_seams", "confirmed_decisions", "assumptions", "constraints", "terminology_refs", "evidence_refs"]) requireArray(data, field, "root", errors, ["target_users", "mvp", "success_criteria", "test_seams", "confirmed_decisions", "evidence_refs"].includes(field) ? 1 : 0);
|
|
33
|
+
|
|
34
|
+
const unresolved = Array.isArray(data.unresolved_items) ? data.unresolved_items : [];
|
|
35
|
+
for (const [index, item] of unresolved.entries()) {
|
|
36
|
+
const path = `unresolved_items[${index}]`;
|
|
37
|
+
for (const field of ["item", "type", "owner", "next_action", "target_version"]) requireString(item ?? {}, field, path, errors);
|
|
38
|
+
if (item?.type && !["blocker", "deferred"].includes(item.type)) errors.push(`${path}.type 非法`);
|
|
39
|
+
}
|
|
40
|
+
const domainRef = data.domain_strategy_ref ?? {};
|
|
41
|
+
for (const field of ["domain_strategy_id", "domain_version", "digest", "status", "persisted_ref"]) requireString(domainRef, field, "domain_strategy_ref", errors);
|
|
42
|
+
if (domainRef.status && !["approved", "stale", "blocked"].includes(domainRef.status)) errors.push("domain_strategy_ref.status 非法");
|
|
43
|
+
if (data.status === "approved" && domainRef.status !== "approved") errors.push("approved 阶段包必须引用 approved 的 domain strategy");
|
|
44
|
+
if (nonEmpty(domainRef.persisted_ref)) {
|
|
45
|
+
try {
|
|
46
|
+
const strategySource = await readFile(resolve(process.cwd(), domainRef.persisted_ref), "utf8");
|
|
47
|
+
const strategyDocument = parseDocument(strategySource, { maxAliasCount: 0, uniqueKeys: true });
|
|
48
|
+
if (strategyDocument.errors.length) errors.push(`domain_strategy_ref.persisted_ref YAML 非法: ${strategyDocument.errors[0].message}`);
|
|
49
|
+
else {
|
|
50
|
+
const strategy = strategyDocument.toJS({ maxAliasCount: 0 });
|
|
51
|
+
if (strategy.domain_strategy_id !== domainRef.domain_strategy_id) errors.push("domain_strategy_ref.domain_strategy_id 与实际文件不一致");
|
|
52
|
+
if (strategy.domain_version !== domainRef.domain_version) errors.push("domain_strategy_ref.domain_version 与实际文件不一致");
|
|
53
|
+
if (strategy.status !== domainRef.status) errors.push("domain_strategy_ref.status 与实际文件不一致");
|
|
54
|
+
if (domainRef.digest !== digest(strategy)) errors.push("domain_strategy_ref.digest 与实际领域战略内容不一致");
|
|
55
|
+
}
|
|
56
|
+
} catch (error) { errors.push(`domain_strategy_ref.persisted_ref 无法读取: ${error.message}`); }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const impact = data.impact_assessment ?? {};
|
|
60
|
+
for (const field of ["ui", "api", "data", "backend", "frontend", "cross_repo", "high_risk"]) {
|
|
61
|
+
requireField(impact, field, "impact_assessment", errors);
|
|
62
|
+
if (field in impact && typeof impact[field] !== "boolean") errors.push(`impact_assessment.${field} 必须是 boolean`);
|
|
63
|
+
}
|
|
64
|
+
const mappings = Array.isArray(data.downstream_mapping) ? data.downstream_mapping : [];
|
|
65
|
+
if (mappings.length < 1) errors.push("downstream_mapping 至少需要一项");
|
|
66
|
+
for (const [index, mapping] of mappings.entries()) {
|
|
67
|
+
const path = `downstream_mapping[${index}]`;
|
|
68
|
+
for (const field of ["domain_change", "consumer", "propagation", "reapproval_condition"]) requireString(mapping ?? {}, field, path, errors);
|
|
69
|
+
if (mapping?.propagation && !["direct", "transitive", "not-applicable", "stale"].includes(mapping.propagation)) errors.push(`${path}.propagation 非法`);
|
|
70
|
+
}
|
|
71
|
+
const approval = data.approval ?? {};
|
|
72
|
+
for (const field of ["approval_ref", "approver", "persisted_ref", "current_version"]) requireString(approval, field, "approval", errors);
|
|
73
|
+
if (data.status === "approved" && approval.current_version !== data.package_version) errors.push("approval.current_version 必须等于 package_version");
|
|
74
|
+
if (unresolved.some((item) => item?.type === "blocker") && data.status === "approved") errors.push("存在 blocker 未决项时不得 approved");
|
|
75
|
+
if (data.status === "approved" && nonEmpty(approval.approval_ref)) {
|
|
76
|
+
try {
|
|
77
|
+
const approvalPath = resolveApprovalRef(approval.approval_ref);
|
|
78
|
+
validateApprovalRecordFile(approvalPath, { requireApproved: true });
|
|
79
|
+
const record = loadApprovalRecord(approvalPath);
|
|
80
|
+
if (record.gate_id !== "gate.stage-decision-package-approved") errors.push("approval_ref 的 gate_id 必须为 gate.stage-decision-package-approved");
|
|
81
|
+
if (record.role_id !== approval.approver) errors.push("approval.approver 必须与会签记录 role_id 一致");
|
|
82
|
+
} catch (error) { errors.push(`approval_ref 会签记录无效: ${error.message}`); }
|
|
83
|
+
}
|
|
84
|
+
return errors;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const file = process.argv[2];
|
|
88
|
+
if (!file) fail(["用法: validate-stage-decision-package.mjs <package.yaml>"]);
|
|
89
|
+
else {
|
|
90
|
+
try {
|
|
91
|
+
const source = await readFile(file, "utf8");
|
|
92
|
+
const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
|
|
93
|
+
if (document.errors.length) fail([document.errors[0].message]);
|
|
94
|
+
else {
|
|
95
|
+
const errors = await validate(document.toJS({ maxAliasCount: 0 }));
|
|
96
|
+
if (errors.length) fail(errors);
|
|
97
|
+
else process.stdout.write(JSON.stringify({ result: "completed", contract: file }, null, 2) + "\n");
|
|
98
|
+
}
|
|
99
|
+
} catch (error) { fail([error.message]); }
|
|
100
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
schema_version: 1
|
|
2
|
+
gate_id: gate.domain-strategy-approved
|
|
3
|
+
decision: approved
|
|
4
|
+
actor_kind: digital-human
|
|
5
|
+
role_id: role.product-manager
|
|
6
|
+
runtime_id: runtime.generic
|
|
7
|
+
runtime_instance_ref: "runtime.generic:role.product-manager"
|
|
8
|
+
principal_ref: "instance:role.product-manager"
|
|
9
|
+
drafter_role_id: role.requirements-manager
|
|
10
|
+
countersigner_role_ids:
|
|
11
|
+
- role.product-manager
|
|
12
|
+
evidence_refs:
|
|
13
|
+
- evidence.domain-strategy-review.supplier
|
|
14
|
+
biological_veto: false
|
|
15
|
+
notes: DDD 战略设计由需求经理起草,产品经理独立会签。
|